STM32串口通信
1.STM32串口簡介:
STM32 的串口資源相當豐富的,功能也相當強勁。 ALIENTEK 精英 STM32 開發板所使用的 STM32F103ZET6 最多可提供 5 路串口,有分數波特率發生器、支持同步單線通信和半雙工單線通訊、支持 LIN、 支持調制解調器操作、 智能卡協議和 IrDA SIR ENDEC 規範、具有 DMA等。
串口最基本的設置,就是波特率的設置。 STM32 的串口使用起來還是蠻簡單的,只要你開啓了串口時鐘,並設置相應 IO 口的模式,然後配置一下波特率,數據位長度,奇偶校驗位等信息,就可以使用了。下面,我們就簡單介紹下這幾個與串口基本配置直接相關的寄存器。
1,串口時鐘使能
串口作爲 STM32 的一個外設,其時鐘由外設時鐘使能寄存器控制,這裏我們使用的串口 1是在 APB2ENR寄存器的第 14位。只是說明一點,就是除了串口 1 的時鐘使能在 APB2ENR 寄存器,其他串口的時鐘使能位都在 APB1ENR 寄存器。
2,串口復位
當外設出現異常的時候可以通過復位寄存器裏面的對應位設置,實現該外設的復位,然後重新配置這個外設達到讓其重新工作的目的。一般在系統剛開始配置外設的時候,都會先執行復位該外設的操作。串口 1 的復位是通過配置 APB2RSTR 寄存器的第 14 位來實現的。 APB2RSTR 寄存器的各位描述如圖 9.1.1 所示
從圖 9.1.1 可知串口 1 的復位設置位在 APB2RSTR 的第 14 位。通過向該位寫 1 復位串口 1,寫 0 結束復位。其他串口的復位位在 APB1RSTR 裏面。
3,串口波特率設置。
每個串口都有一個自己獨立的波特率寄存器 USART_BRR,通過設置該寄存器就可以達到配置不同波特率的目的。具體實現方法。
4,串口控制
STM32 的每個串口都有 3 個控制寄存器 USART_CR1~3,串口的很多配置都是通過這 3 個寄存器來設置的。這裏我們只要用到 USART_CR1 就可以實現我們的功能了,該寄存器的各位描述如圖 9.1.2 所示
該寄存器的高 18 位沒有用到,低 14 位用於串口的功能設置。 UE 爲串口使能位,通過該位置 1,以使能串口。 M 爲字長選擇位,當該位爲 0 的時候設置串口爲 8 個字長外加 n 個停止位,停止位的個數(n)是根據 USART_CR2 的[13:12]位設置來決定的,默認爲 0。 PCE 爲校驗使能位,設置爲 0,則禁止校驗,否則使能校驗。 PS 爲校驗位選擇,設置爲 0 則爲偶校驗,否則爲奇校驗。 TXIE 爲發送緩衝區空中斷使能位,設置該位爲 1,當 USART_SR 中的 TXE 位爲1 時,將產生串口中斷。 TCIE 爲發送完成中斷使能位,設置該位爲 1,當 USART_SR 中的 TC位爲 1 時,將產生串口中斷。RXNEIE 爲接收緩衝區非空中斷使能,設置該位爲 1,當 USART_SR中的 ORE 或者 RXNE 位爲 1 時,將產生串口中斷。 TE 爲發送使能位,設置爲 1,將開啓串口的發送功能。 RE 爲接收使能位,用法同 TE。其他位的設置,這裏就不一一列出來了,《STM32 中文參考手冊》有詳細介紹,在這裏我們就不列出來了。
5,數據發送與接收
STM32 的發送與接收是通過數據寄存器 USART_DR 來實現的,這是一個雙寄存器,包含了 TDR 和 RDR。當向該寄存器寫數據的時候,串口就會自動發送,當收到數據的時候,也是存在該寄存器內。該寄存器的各位描述如圖 9.1.3 所示:
可以看出,雖然是一個 32 位寄存器,但是隻用了低 9 位(DR[8: 0]),其他都是保留。DR[8: 0]爲串口數據,包含了發送或接收的數據。由於它是由兩個寄存器組成的,一個給發送用(TDR),一個給接收用(RDR),該寄存器兼具讀和寫的功能。 TDR 寄存器提供了內部總線和輸出移位寄存器之間的並行接口。 RDR 寄存器提供了輸入移位寄存器和內部總線之間的並行接口。當使能校驗(USART_CR1 中 PCE 位被置位)進行發送時,寫到 MSB 的值(根據數據的長度不同, MSB 是第 7 位或者第 8 位)會被後來的校驗位取代。當使能校驗位進行接收時,讀到的 MSB 位是接收到的校驗位。
6,串口狀態。
串口的狀態可以通過狀態寄存器 USART_SR 讀取。 USART_SR 的各位描述如圖 9.1.4 所示:
這裏我們關注一下兩個位,第 5、 6 位 RXNE 和 TC。RXNE(讀數據寄存器非空),當該位被置 1 的時候,就是提示已經有數據被接收到了,並且可以讀出來了。這時候我們要做的就是儘快去讀取 USART_DR,通過讀 USART_DR 可以將
該位清零,也可以向該位寫 0,直接清除。
TC(發送完成),當該位被置位的時候,表示 USART_DR 內的數據已經被髮送完成了。如果設置了這個位的中斷,則會產生中斷。該位也有兩種清零方式: 1)讀 USART_SR,寫USART_DR。 2)直接向該位寫 0。
2、代碼實現
//初始化 IO 串口 1
//pclk2:PCLK2 時鐘頻率(Mhz)
//bound:波特率
void uart_init(u32 pclk2,u32 bound)
{
float temp;
u16 mantissa;
u16 fraction;
temp=(float)(pclk2*1000000)/(bound*16);//得到 USARTDIV
mantissa=temp; //得到整數部分
fraction=(temp-mantissa)*16; //得到小數部分
mantissa<<=4;
mantissa+=fraction;
RCC->APB2ENR|=1<<2; //使能 PORTA 口時鐘
RCC->APB2ENR|=1<<14; //使能串口時鐘
GPIOA->CRH&=0XFFFFF00F; //IO 狀態設置
GPIOA->CRH|=0X000008B0; //IO 狀態設置
RCC->APB2RSTR|=1<<14; //復位串口 1
RCC->APB2RSTR&=~(1<<14); //停止復位
//波特率設置
USART1->BRR=mantissa; // 波特率設置
USART1->CR1|=0X200C; //1 位停止,無校驗位.
#if EN_USART1_RX //如果使能了接收
//使能接收中斷
USART1->CR1|=1<<5; //接收緩衝區非空中斷使能
MY_NVIC_Init(3,3,USART1_IRQn,2); //組 2,最低優先級
#endif
}
從該代碼可以看出,其初始化串口的過程,和我們前面介紹的一致。 先計算得到
USART1->BRR 的內容。然後開始初始化串口引腳,然之後設置波特率和奇偶校驗等。
這裏需要注意一點,因爲我們使用到了串口的中斷接收,必須在 usart.h 裏面設置EN_USART1_RX 爲 1 (默認設置就是 1 的)。該函數纔會配置中斷使能,以及開啓串口 1 的 NVIC
中斷。這裏我們把串口 1 中斷放在組 2,優先級設置爲組 2 裏面的最低。串口 1 的中斷服務函數 USART1_IRQHandler。
介紹完了這兩個函數,我們回到 test.c,在 test.c 裏面編寫如下代碼:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
int main(void)
{
u16 t; u16 len; u16 times=0;
Stm32_Clock_Init(9); //系統時鐘設置
uart_init(72,115200); //串口初始化爲 115200
delay_init(72); //延時初始化
LED_Init(); //初始化與 LED 連接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3FFF;//得到此次接收到的數據長度
printf("\r\n 您發送的消息爲:\r\n\r\n");
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待發送結束
}
printf("\r\n\r\n");//插入換行
USART_RX_STA=0;
}
else
{
times++;
if(times%5000==0)
{
printf("\r\n 精英 STM32 開發板 串口實驗\r\n");
printf("正點原子@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("請輸入數據,以回車鍵結束\r\n");
if(times%30==0)LED0=!LED0;//閃爍 LED,提示系統正在運行.
delay_ms(10);
}
}
}
這段代碼比較簡單,重點看下以下兩句:
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待發送結束
第一句,其實就是發送一個字節到串口,通過直接操作寄存器來實現的。第二句呢,就是我們在寫了一個字節在 USART1->DR 之後,要檢測這個數據是否已經被髮送完成了,通過檢測USART1->SR 的第 6 位,是否爲 1 來決定是否可以開始第二個字節的發送。