學習是一個簡單的過程,只要有善於發掘的眼睛,總能學到新知識,然而如何堅持不懈的學習卻很困難,對我亦如此,生活中有太多的誘惑,最后只想說一句勿忘初心。閑話不多扯,本篇講訴的是異步串行口的輸入輸出,串口在外設中屬於比較簡單的通訊模式,但是在大型項目調試中又十分重要,理解該外設模塊對於以后的通訊協議學習以及軟件調試都有重要意義。
通訊協議是指雙方實體完成通信或服務所必須遵循的規則和約定,對於串口來說,包含波特率,數據位長度,停止位和數據校驗位,當stm32芯片和客戶端具有相同的協議約定時即能夠正確的接收數據,因此串口外設的配置正是這些參數的設定。通過前面章節的學習,以USART1為例,在外設設置之前,我們應該有大致流程:
1. 串口外設外設和其占用的GPIO端口都要配置,且占用的GPIO端口為PA9(USART1_TX), PA10(USART1_RX).
2. 外設對應時鍾都要配置,且要在初始化外設和GPIO端口配置之前
3. USART外設的配置主要是協議相關參數配置
串口外設配置
至於GPIO端口配置的參數參考<stm32F系列微控制器參考手冊>(以后以手冊簡稱)110頁表21如下圖

至於外設所在時鍾區域,請參考RCC章節,如此外設的初始化如下:
頭文件定義:
#define RCC_USART1 RCC_APB2Periph_GPIOA \
|RCC_APB2Periph_USART1 \ |RCC_APB2Periph_AFIO #define USART1_RX GPIOA
#define USART1_TX GPIOA
#define USART1_RX_Pin GPIO_Pin_10
#define USART1_TX_Pin GPIO_Pin_9
初始化代碼:
USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; USART_DeInit(USART1); RCC_APB2PeriphClockCmd(RCC_USART1, ENABLE); GPIO_InitStructure.GPIO_Pin = USART1_TX_Pin; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //配置串口1輸出端為復用推挽輸出
GPIO_Init(USART1_TX,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = USART1_RX_Pin; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //配置串口1輸入端為浮空輸入
GPIO_Init(USART1_RX, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200; //設置串口波特率為9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //輸入/輸出8位數據位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //設置停止位為1位
USART_InitStructure.USART_Parity = USART_Parity_No; //不進行奇偶校驗
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //不采用硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //串口1啟動輸入輸出
USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); //使能串口
如此,便完成了外設的初始化。
這里有幾個知識點要講:
1.波特率 每秒發送的bit個數。對於一定長度的數據,在已知配置的情況下傳輸時間計算:
(以2k數據b,每幀8位數據,1位停止位,波特率9600,傳輸完成花費時間)
T = 2*1024*(8+1)/9600*8 = 0.24s
雖然用庫函數可以直接設置波特率,但stm32的波特率如何通過寄存器計算還是很重要的,參考公式配置USART->BRR(手冊542頁): 
公式:

其中fck為串口所在區域外設時鍾,如USART即為PCLK2時鍾,USARTDIV即為要設置參數:
以波特率115200,PCLK等於系統時鍾SYSTICk(48MHZ)算,USARTDIV設置為48M/(16*115200),轉成二進制為0x1a0
2. 奇偶校驗位
因為奇偶校驗位的參與,接收到的數據幀有以下四種格式:

因此客戶端接收時也要數據位要設定為實際數據長度,不然接收到數據會是亂碼。
3. 硬件流控制
用於數據流控制,通訊的雙方由此交換是否停止后繼續接收信息,避免因處理數據速度問題而出現的緩存溢出,導致數據的丟失。
CTS:只有CTS輸入信號有效(低電平)時才能發送數據。如果在數據傳輸的過程中,CTS信號變成無效,那么發完這個數據后,傳輸就停止下來。如果當CTS為無效時,向數據寄 存器里寫數據,則要等到CTS有效時才會發送這個數據。
RTS:只有接收緩沖區內有空余的空間時才請求下一個數據。當前數據發送完成后,發送操作就需要暫停下來。如果可以接收數據了,將RTS輸出置為有效(拉至低電平)。
ps:本例中沒有使用,具體詳情參考手冊537頁
串口輸入輸出實現
串口的輸出是通過printf函數的,其包含在stdio.h頭文件內部。此外printf還需要retarget處理,具體代碼如下:
retarget.h頭文件添加:
#ifdef __GNUC__ /* With GCC/RAISONANCE, small printf ( option LD Linker->Libraries->Small printf set to 'Yes') calls __io_putchar() */ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif /* __GNUC__ */
retarget.c文件添加:
PUTCHAR_PROTOTYPE { /*將1字節數據發往串口寄存器 */ USART_SendData(USART1, (uint8_t) ch); /*等待傳輸結束*/ while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {} return ch; }
之后就可以用printf函數發送數據了,至於接收則采用緩存模式,將接受數據存儲到數組中,接收到\n時結束並發送:
u8 i = 0; do { while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET) { } USART_STORE[i] = USART1->DR; if(USART_STORE[i] == '\n') { break; } i++; if(i >= USART_ISL) { i = 0; printf("you input is overlength"); break; } }while(1); if(i < USART_ISL) { //printf("you input is "); printf("\n%s",USART_STORE); }
如此就完成了USART外設的配置和輸入輸出輪詢實現。
具體代碼參考:http://files.cnblogs.com/files/zc110747/3.USART%28%E8%BD%AE%E8%AF%A2%E6%A8%A1%E5%BC%8F%29.7z
