1. 程式人生 > >STM32基礎知識3-STM32串列埠USART1的使用方法和程式

STM32基礎知識3-STM32串列埠USART1的使用方法和程式

通用同步非同步收發器(USART)提供了一種靈活的方法來與使用工業標準NR 非同步序列資料格式的外部裝置之間進行全雙工資料交換。 USART利用分數波特率發生器提供寬範圍的波特率選擇,支援同步單向通訊和半雙工單線通訊。

1、STM32韌體庫使用外圍裝置的主要思路

在STM32中,外圍裝置的配置思路比較固定。首先是使能相關的時鐘,一方面是裝置本身的時鐘,另一方面如果裝置通過IO口輸出還需要使能IO口的時鐘;最後如果對應的IO口是複用功能的IO口,則還必須使能AFIO的時鐘。

其次是配置GPIO,GPIO的各種屬性由硬體手冊的AFIO一章詳細規定,較為簡單。

接著相關裝置需要如果需要使用中斷功能,必須先配置中斷優先順序,後文詳述。

然後是配置外圍裝置的相關屬性,視具體裝置而定,如果裝置需要使用中斷方式,必須使能相應裝置的中斷,之後需要使能相關裝置。

最後如果裝置使用了中斷功能,則還需要填寫相應的中斷服務程式,在服務程式中進行相應操作。

2、UART的配置步驟(查詢方式)

2.1、開啟時鐘

由於UART的TX和RX和AFIO都掛在APB2橋上,因此採用韌體庫函式RCC_APB2PeriphClockCmd()進行初始化。UARTx需要分情況討論,如果是UART1,則掛在APB2橋上,因此採用RCC_APB2PeriphClockCmd()進行初始化,其餘的UART2~5均掛在APB1上。

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

2.2、GPIO初始化

GPIO的屬性包含在結構體GPIO_InitTypeDef,其中對於TX引腳,GPIO_Mode欄位設定為GPIO_Mode_AF_PP(複用推輓輸出),GPIO_Speed切換速率設定為GPIO_Speed_50MHz;對於RX引腳,GPIO_Mode欄位設定為GPIO_Mode_IN_FLOATING(浮空輸入),不需要設定切換速率。最後通過GPIO_Init()使能IO口。

以下是GPIO設定的例項程式碼:

複製程式碼
    GPIO_InitTypeDef GPIO_InitStructure;

    //USART1 Tx(PA.09) 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
    GPIO_InitStructure.GPIO_Speed 
= GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1 Rx(PA.10) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure);
複製程式碼

2.3、配置UART相關屬性

通過結構體USART_InitTypeDef來確定。UART模式下的欄位如下

USART_BaudRate:波特率,視具體裝置而定

USART_WordLength:字長

USART_StopBits:停止位

USART_Parity:校驗方式

USART_HardwareFlowControl:硬體流控制

USART_Mode:單/雙工

最後設定。例項程式碼為:

複製程式碼
   //USART1配置
  USART_InitTypeDef USART_InitStructure;

  USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE);
複製程式碼

別忘了最後要使用USART_Cmd()來啟動裝置UART1。

2.4、重定向print()函式。

複製程式碼
int fputc(int ch,FILE *f)
{
    USART1->SR;  //USART_GetFlagStatus(USART1, USART_FLAG_TC) 解決第一個字元傳送失敗的問題
    //一個一個傳送字元
    USART_SendData(USART1, (unsigned char) ch);
    //等待發送完成
    while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
    
    return(ch);
}
複製程式碼

最後通過主函式直接輸出即可。

複製程式碼
int main(void)
{
    // USART1 config 9600 8-N-1 
    USART1_Config();
    
    printf("hello world!");
}
複製程式碼

3、UART的配置步驟(中斷方式)

開啟時鐘、GPIO初始化、配置UART相關屬性、重定向print()函式 與上面的相同。

3.1、中斷優先順序的配置

這是STM32比較奇怪的地方,在只有一箇中斷的情況下,仍然需要配置優先順序,其作用是使能某條中斷的觸發通道。STM32的中斷有至多兩個層次,分別是先佔優先順序和從優先順序,而整個優先順序設定引數的長度為4位,因此需要首先劃分先佔優先順序位數和從優先順序位數,通過NVIC_PriorityGroupConfig()實現;

特定裝置的中斷優先順序NVIC的屬性包含在結構體NVIC_InitTypeDef中,其中欄位NVIC_IRQChannel包含了裝置的中斷向量,儲存在啟動程式碼中;欄位NVIC_IRQChannelPreemptionPriority為主優先順序,NVIC_IRQChannelSubPriority為從優先順序,取值的範圍應根據位數劃分的情況而定;最後NVIC_IRQChannelCmd欄位是是否使能,一般定位ENABLE。最後通過NVIC_Init()來使能這一中斷向量。例項程式碼如下:

複製程式碼
//配置UART1接收中斷
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; 
    /* Configure the NVIC Preemption Priority Bits */  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    
    /* Enable the USARTy Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;     
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
複製程式碼

3.2、中斷的服務程式的設計

目前使用了UART的兩個中斷USART_IT_RXNE(接收快取補空中斷)和USART_IT_TXE(傳送快取空中斷),前一箇中斷保證了一旦有資料接收到就進入中斷以接收特定長度的資料,後一箇中斷表示一旦發完一個數據就進入中斷函式,保證連續傳送一段資料。一個裝置的所有中斷都包含在一箇中斷服務程式中,因此必須首先分清楚這次響應的是哪一個中斷,使用USART_GetITStatus()函式確定;採用USART_ReceiveData()函式接收一個位元組資料,採用USART_SendData()函式傳送一個位元組資料,當關閉中斷時採用USART_ITConfig()失能響應的中斷。例項程式:

複製程式碼
void USART1_IRQHandler(void)
{
    uint8_t ch;
    
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {     
        //ch = USART1->DR;
            ch = USART_ReceiveData(USART1);     //接受資料
          printf( "%c", ch );    //返回列印
    } 
     
}
複製程式碼

3.3、接收資料函式:

複製程式碼
//重定向scanf函式到USART1
int fgetc(FILE *f)
{
        /*等待串列埠1輸入資料*/
        while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

        return (int)USART_ReceiveData(USART1);
}
複製程式碼

4、 STM32串列埠在首次傳送字元的時候,首字元丟失解決辦法

網上關於傳送字元的程式碼大多如下:

USART_SendData(USART1, (uint8_t)ch);
while( USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);

其實咋一看是說的通的,但是在仔細看手冊的時候發現 TC 和 TXE 標誌位在復位的時候被置1 ,這樣第一次while迴圈就是沒有用的。這樣導致了首次第一個字元還沒有被輸出,就被後面的字元覆蓋掉,造成實際看到的丟失現象。解決辦法就很簡單:在前面加上一句 USART1->SR;

具體程式碼如下:

USART1->SR;
USART_SendData(USART1, (uint8_t)ch);
while( USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);

下面我來說說原因: 第一句讀取SR暫存器,第二句寫DR暫存器 剛好清除了TC標誌位 。第一次while迴圈就起作用了。

也可將USART1->SR;替換為USART_GetFlagStatus(USART1, USART_FLAG_TC)

補充:一直有一個疑問是關於接受和傳送資料的問題:對於“hello”這樣的字串是一個一個接受還是整個接受顯示,下面的實驗可以驗證是一個一個進行的。

[完]  選自:http://www.ciast.net/post/2015119.html