通用同步異步收發器(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()函數。
intfputc(intch,FILE *f)
{
USART1->SR;//USART_GetFlagStatus(USART1, USART_FLAG_TC) 解決第一個字符發送失敗的問題
//一個一個發送字符USART_SendData(USART1, (unsignedchar) ch);
//等待發送完成while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
return(ch);
}
最后通過主函數直接輸出即可。
intmain(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接收中斷voidNVIC_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()失能響應的中斷。實例程序:
voidUSART1_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函數到USART1intfgetc(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)
本實驗所有程序可參考: http://www.makeru.com.cn/live/1392_1164.html?s=45051
補充:一直有一個疑問是關於接受和發送數據的問題:對於“hello”這樣的字符串是一個一個接受還是整個接受顯示,下面的實驗可以驗證是一個一個進行的。
