STM32——串口通信
一、異步串口通信協(xié)議
? ? ? ? STM32 的串口非常強(qiáng)大,它不僅支持最基本的通用串口同步、異步通信,還具有 LIN 總線功能(局域互聯(lián)網(wǎng))、IRDA 功能(紅外通信)、SmartCard 功能。一般我們利用串口打印調(diào)試信息。
?
二、串口工作過程分析
? ? ? ? 我們只需要大概了解串口發(fā)送的過程即可。從下至上,我們看到串口外設(shè)主要由三個(gè)部分組成,分別是波特率控制、收發(fā)控制和數(shù)據(jù)存儲轉(zhuǎn)移。
1、 波特率
? ? ? ? 波特率,即每秒傳輸?shù)亩M(jìn)制位數(shù),用 b/s (bps)表示,通過對時(shí)鐘的控制可以改變波特率。在配置波特率時(shí),我們向波特比率寄存器 USART_BRR 寫入?yún)?shù),修改了串口時(shí)鐘 的分 頻 值USARTDIV 。USART_BRR 寄存 器 包括 兩 部分 , 分別 是 DIV_Mantissa(USARTDIV 的整數(shù)部分)和 DIV_Fraction(USARTDIV 的小數(shù))部分,最終,計(jì)算公式為 USARTDIV=DIV_Mantissa+(DIV_Fraction/16)。
USARTDIV 是對串口外設(shè)的時(shí)鐘源進(jìn)行分頻的,對于 USART1,由于它掛載在 APB2總線上,所以它的時(shí)鐘源為 f PCLK2 ;而 USART2、3 掛載在 APB1 上,時(shí)鐘源則為 fPCLK1,串口的時(shí)鐘源經(jīng)過 USARTDIV 分頻后分別輸出作為發(fā)送器時(shí)鐘及接收器時(shí)鐘,控制發(fā)送和接收的時(shí)序。
2、 收發(fā)控制
? ? ? ? 寄存器 :CR1、CR2、CR3 和 SR,即USART 的三個(gè)控制寄存器(Control Register)及一個(gè)狀態(tài)寄存器(Status Register)。通過向寄存器寫入各種控制參數(shù)來控制發(fā)送和接收,如奇偶校驗(yàn)位、停止位等,還包括對USART 中斷的控制;串口的狀態(tài)在任何時(shí)候都可以從狀態(tài)寄存器中查詢得到。具體的控制和狀態(tài)檢查,我們都是使用庫函數(shù)來實(shí)現(xiàn)的。
3、 數(shù)據(jù)存儲轉(zhuǎn)移
? ? ? ? 收發(fā)控制器根據(jù)我們的寄存器配置,對數(shù)據(jù)存儲轉(zhuǎn)移部分的移位寄存器進(jìn)行控制。當(dāng)我們需要發(fā)送數(shù)據(jù)時(shí),內(nèi)核或 DMA 外設(shè)(一種數(shù)據(jù)傳輸方式,在后面介紹)把數(shù)據(jù)從內(nèi)存(變量)寫入到發(fā)送數(shù)據(jù)寄存器 TDR 后,發(fā)送控制器將適時(shí)地自動(dòng)把數(shù)據(jù)從 TDR 加載到發(fā)送移位寄存器,然后通過串口線 Tx,把數(shù)據(jù)一位一位地發(fā)送出去,當(dāng)數(shù)據(jù)從 TDR轉(zhuǎn)移到移位寄存器時(shí),會(huì)產(chǎn)生發(fā)送寄存器 TDR 已空事件 TXE,當(dāng)數(shù)據(jù)從移位寄存器全部發(fā)送出去時(shí),會(huì)產(chǎn)生數(shù)據(jù)發(fā)送完成事件 TC,這些事件可以在狀態(tài)寄存器中查詢到。
而接收數(shù)據(jù)則是一個(gè)逆過程,數(shù)據(jù)從串口線 Rx 一位一位地輸入到接收移位寄存器,然后自動(dòng)地轉(zhuǎn)移到接收數(shù)據(jù)寄存器 RDR,最后用內(nèi)核指令或 DMA
?
三、UART
串口配置流程:
(1)??????使能串口1的時(shí)鐘
(2)??????配置串口1的I/O
(3)??????配置串口1的工作模式,具體為波特率為 115200 、8 個(gè)數(shù)據(jù)位、1 個(gè)停止位、無硬件流控制。即 115200 8-N-1。
void?USART1_Config(void) { ??GPIO_InitTypeDef?GPIO_InitStructure;?//?串口IO結(jié)構(gòu)體 ??USART_InitTypeDef?USART_InitStructure; ??/*?配置串口時(shí)鐘?*/ ??RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1?|?RCC_APB2Periph_GPIOA,?ENABLE); ??/*?TX?--PA9?--?通用推挽式輸出?--?50MHZ?*/ ??GPIO_InitStructure.GPIO_Pin?=?GPIO_Pin_9; ??GPIO_InitStructure.GPIO_Mode?=?GPIO_Mode_AF_PP; ??GPIO_InitStructure.GPIO_Speed?=?GPIO_Speed_50MHz; ??GPIO_Init(GPIOA,?&GPIO_InitStructure); ??/*?RX?--PA10?--?輸入*/ ??GPIO_InitStructure.GPIO_Pin?=?GPIO_Pin_10; ??GPIO_InitStructure.GPIO_Mode?=?GPIO_Mode_IN_FLOATING; ??GPIO_Init(GPIOA,?&GPIO_InitStructure); ??/*?串口初始化?--?115200-8-1?*/ ??USART_InitStructure.USART_BaudRate?=?115200;?//?波特率115200 ??USART_InitStructure.USART_WordLength?=?USART_WordLength_8b;?//?8位數(shù)據(jù)位 ??USART_InitStructure.USART_StopBits?=?USART_StopBits_1;?//?1位停止位 ??USART_InitStructure.USART_Parity?=?USART_Parity_No?;?//?無奇偶校驗(yàn)位 ??USART_InitStructure.USART_HardwareFlowControl?=?USART_HardwareFlowC?ontrol_None;?//?無硬件流 ??USART_InitStructure.USART_Mode?=?USART_Mode_Rx?|?USART_Mode_Tx;?//?配置串口的模式。為了配置雙線全雙工通信,需要把?Rx?和?Tx?模式都開啟。 ??USART_Init(USART1,?&USART_InitStructure);?//?填充完結(jié)構(gòu)體,?向寄存器寫入配置參數(shù) ??USART_Cmd(USART1,?ENABLE);?//?使能?USART1?外設(shè) }
? ? ? ? 要想 printf() 函數(shù)工作的話,我們需要把 printf() 重新定向到串口中。為了實(shí)現(xiàn)重定向 printf() 函數(shù),我們需要重寫 fputc() 這個(gè) C 標(biāo)準(zhǔn)庫函數(shù),因?yàn)?printf()在C 標(biāo)準(zhǔn)庫函數(shù)中實(shí)質(zhì)是一個(gè)宏,最終是調(diào)用了 fputc() 這個(gè)函數(shù)。
//?重定向到串口 int?fputc(int?ch,?FILE?*f) { ??/*?發(fā)送一個(gè)字節(jié)數(shù)據(jù)到?串口?*/ ??USART_SendData(USART1,?(uint8_t)?ch); ??/*?等待發(fā)送完畢?*/ ??while?(USART_GetFlagStatus(USART1,?USART_FLAG_TC)?==?RESET); ??return?(ch); }