STM32——串口通信

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 來實現的,這是一個雙寄存器,包含了 TDRRDR。當向該寄存器寫數據的時候,串口就會自動發送,當收到數據的時候,也是存在該寄存器內。該寄存器的各位描述如圖 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 來決定是否可以開始第二個字節的發送。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章