【STM32】串口收发主要程序代码分析
文章目錄
- 數(shù)據(jù)發(fā)送與接收
- 串口狀態(tài)
- 開啟串口響應(yīng)中斷
- 獲取相應(yīng)中斷狀態(tài)
- main.c
- usart.c
- usart.h
串口設(shè)置的一般步驟可以總結(jié)為如下幾個(gè)步驟:
下表為幾個(gè)與串口基本配置直接相關(guān)的幾個(gè)固件庫函數(shù),這些函數(shù)和定義主要分布在 stm32f10x_usart.h 和 stm32f10x_usart.c 文件中。
| 1. 串口時(shí)鐘使能 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1) |
| 2. 串口復(fù)位 | void USART_DeInit(USART_TypeDef* USARTx); // 串口復(fù)位 |
| USART_DeInit(USART1); // 復(fù)位串口 1 | |
| 3. 串口參數(shù)初始化 | void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct); |
| 4. 數(shù)據(jù)發(fā)送與接收(發(fā)送) | void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); |
| 4. 數(shù)據(jù)發(fā)送與接收(接收) | uint16_t USART_ReceiveData(USART_TypeDef* USARTx); |
| 5. 串口狀態(tài) | FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); |
| USART_GetFlagStatus(USART1, USART_FLAG_RXNE); | |
| USART_GetFlagStatus(USART1, USART_FLAG_TC); | |
| 6. 串口使能 | USART_Cmd(USART1, ENABLE); //使能串口 |
| 7. 開啟串口響應(yīng)中斷 | void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState) |
| USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //開啟中斷,接收到數(shù)據(jù)中斷 | |
| USART_ITConfig(USART1,USART_IT_TC,ENABLE); | |
| 8. 獲取相應(yīng)中斷狀態(tài) | ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT) |
| USART_GetITStatus(USART1, USART_IT_TC) |
數(shù)據(jù)發(fā)送與接收
STM32 的發(fā)送與接收是通過數(shù)據(jù)寄存器 USART_DR 來實(shí)現(xiàn)的,這是一個(gè)雙寄存器,包含了 TDR 和 RDR。當(dāng)向該寄存器寫數(shù)據(jù)的時(shí)候,串口就會(huì)自動(dòng)發(fā)送,當(dāng)收到數(shù)據(jù)的時(shí)候,也是存在該寄存器內(nèi)。
STM32 庫函數(shù)操作 USART_DR 寄存器發(fā)送數(shù)據(jù)的函數(shù)是:void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 通過該函數(shù)向串口寄存器 USART_DR 寫入一個(gè)數(shù)據(jù)。
STM32 庫函數(shù)操作 USART_DR 寄存器讀取串口接收到的數(shù)據(jù)的函數(shù)是:uint16_t USART_ReceiveData(USART_TypeDef* USARTx); 通過該函數(shù)可以讀取串口接收到的數(shù)據(jù)。
串口狀態(tài)
串口的狀態(tài)可以通過狀態(tài)寄存器 USART_SR 讀取。狀態(tài)寄存器 USART_SR 的各位描述如下圖:
RXNE(讀數(shù)據(jù)寄存器非空)。當(dāng)該位被置 1 的時(shí)候,就是提示已經(jīng)有數(shù)據(jù)被接收到了,并且可以讀出來了。這時(shí)候我們要做的就是盡快去讀取 USART_DR,通過讀 USART_DR 可以將該位清零,也可以向該位寫 0,直接清除。
TC(發(fā)送完成)。當(dāng)該位被置位的時(shí)候,表示 USART_DR 內(nèi)的數(shù)據(jù)已經(jīng)被發(fā)送完成了。如果設(shè)置了這個(gè)位的中斷, 則會(huì)產(chǎn)生中斷。 該位也有兩種清零方式: 1 )讀 USART_SR , 寫USART_DR。2)直接向該位寫 0。
在我們固件庫函數(shù)里面,讀取串口狀態(tài)的函數(shù)是FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); 這里第二個(gè)入口參數(shù)非常關(guān)鍵,它是標(biāo)示我們要查看串口的哪種狀態(tài)。
例如我們要判斷讀寄存器是否非空(RXNE),操作庫函數(shù)的方法為:
USART_GetFlagStatus(USART1, USART_FLAG_RXNE);
判斷發(fā)送是否完成(TC),操作庫函數(shù)的方法為:
USART_GetFlagStatus(USART1, USART_FLAG_TC);
開啟串口響應(yīng)中斷
有時(shí)候我們還需要開啟串口中斷,那么我們還需要使能串口中斷,使能串口中斷的函數(shù)是:void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState); 這個(gè)函數(shù)的第二個(gè)入口參數(shù)是標(biāo)示使能串口的類型,也就是使能哪種中斷,因?yàn)榇诘闹袛囝愋陀泻芏喾N。
比如我們?cè)诮邮盏綌?shù)據(jù)的時(shí)候(RXNE讀數(shù)據(jù)寄存器非空),我們要產(chǎn)生中斷,那么我們開啟中斷的方法是:USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 開啟中斷,接收到數(shù)據(jù)中斷
要在發(fā)送數(shù)據(jù)結(jié)束的時(shí)候(TC,發(fā)送完成)產(chǎn)生中斷的方法是:USART_ITConfig(USART1, USART_IT_TC, ENABLE);
獲取相應(yīng)中斷狀態(tài)
當(dāng)我們使能了某個(gè)中斷的時(shí)候,當(dāng)改中斷發(fā)生了,就會(huì)設(shè)置狀態(tài)寄存器中的某個(gè)標(biāo)志位。經(jīng)常我們?cè)谥袛嗵幚砗瘮?shù)中,要判斷該中斷是哪種中斷,使用的函數(shù)是:ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
比如我們使能了串口發(fā)送完成中斷,那么當(dāng)中斷發(fā)生了,我們便可以在中斷處理函數(shù)中調(diào)用這個(gè)函數(shù)來判斷到底是否是串口發(fā)送完成中斷,方法是:USART_GetITStatus(USART1, USART_IT_TC); 返回值是 SET,說明是串口發(fā)送完成中斷發(fā)生。
main.c
#include "sys.h" #include "usart.h" #include "delay.h" #include "led.h" int main(void) {u8 t; u8 len;u16 times=0;delay_init(); //延時(shí)函數(shù)初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設(shè)置中斷分組uart_init(9600); //串口初始化為 9600LED_Init(); //初始化與 LED 連接的硬件接口while(1){if(USART_RX_STA&0x8000){len=USART_RX_STA&0x3fff; //得到此次接收到的數(shù)據(jù)長度printf("\r\n 您發(fā)送的消息為:\r\n");for(t=0;t<len;t++){USART1->DR=USART_RX_BUF[t];while((USART1->SR&0X40)==0); //等待發(fā)送結(jié)束}printf("\r\n\r\n");//插入換行USART_RX_STA=0;}else{times++;if(times%5000==0){printf("\r\nALIENTEK MiniSTM32 開發(fā)板 串口實(shí)驗(yàn)\r\n");printf("正點(diǎn)原子@ALIENTEK\r\n\r\n\r\n");}if(times%200==0)printf("請(qǐng)輸入數(shù)據(jù),以回車鍵結(jié)束\r\n");if(times%30==0)LED0=!LED0; //閃爍 LED,提示系統(tǒng)正在運(yùn)行.delay_ms(10);}} }usart.c
#include "sys.h" #include "usart.h" #if EN_USART1_RX //如果使能了接收 //串口1中斷服務(wù)程序 //注意,讀取USARTx->SR能避免莫名其妙的錯(cuò)誤 u8 USART_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個(gè)字節(jié). //接收狀態(tài) //bit15, 接收完成標(biāo)志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字節(jié)數(shù)目 u16 USART_RX_STA=0; //接收狀態(tài)標(biāo)記 void uart_init(u32 bound){//GPIO端口設(shè)置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時(shí)鐘//USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.10 //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優(yōu)先級(jí)3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優(yōu)先級(jí)3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據(jù)指定的參數(shù)初始化VIC寄存器//USART 初始化設(shè)置USART_InitStructure.USART_BaudRate = bound; //串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字長為8位數(shù)據(jù)格式USART_InitStructure.USART_StopBits = USART_StopBits_1; //一個(gè)停止位USART_InitStructure.USART_Parity = USART_Parity_No; //無奇偶校驗(yàn)位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //無硬件數(shù)據(jù)流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發(fā)模式USART_Init(USART1, &USART_InitStructure); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //開啟串口接受中斷USART_Cmd(USART1, ENABLE); //使能串口1 }void USART1_IRQHandler(void) //串口1中斷服務(wù)程序{u8 Res;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數(shù)據(jù)必須是0x0d 0x0a結(jié)尾){Res = USART_ReceiveData(USART1); //讀取接收到的數(shù)據(jù)if((USART_RX_STA&0x8000)==0) //接收未完成{if(USART_RX_STA&0x4000) //接收到了0x0d{if(Res!=0x0a)USART_RX_STA=0; //接收錯(cuò)誤,重新開始else USART_RX_STA|=0x8000; //接收完成了 }else //還沒收到0X0D{ if(Res==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數(shù)據(jù)錯(cuò)誤,重新開始接收 } }} } } #endif查看手冊(cè)得知,配置全雙工的串口 1,那么 TX(PA9) 管腳需要配置為推挽復(fù)用輸出,RX(PA10)管腳配置為浮空輸入或者帶上拉輸入。
這里需要注意一點(diǎn), 因?yàn)槲覀兪褂玫搅舜诘闹袛嘟邮?#xff0c; 必須在 usart.h 里面設(shè)置 EN_USART1_RX 為 1(默認(rèn)設(shè)置就是 1 的) 。該函數(shù)才會(huì)配置中斷使能,以及開啟串口 1 的 NVIC 中斷。
我們之前進(jìn)行了開啟串口響應(yīng)中斷,接下來還要編寫中斷服務(wù)函數(shù)。
串口 1 的中斷服務(wù)函數(shù)為 USART1_IRQHandler。之前已講過,在【STM32】SYSTEM文件夾介紹,delay,sys,usart。從之前的代碼可以看出,其初始化串口的過程,先計(jì)算得到 USART1->BRR 的內(nèi)容,然后開始初始化串口引腳,接著把 USART1 復(fù)位,之后設(shè)置波特率和奇偶校驗(yàn)等。
重點(diǎn)看一下兩句函數(shù):
USART_SendData(USART1, USART_RX_BUF[t]); // 向串口 1 發(fā)送數(shù)據(jù) while(USART_GetFlagStatus(USART1, USART_FLAG_TC)!=SET);第一句,其實(shí)就是發(fā)送一個(gè)字節(jié)到串口。
第二句,就是我們?cè)诎l(fā)送一個(gè)數(shù)據(jù)到串口之后,要檢測(cè)這個(gè)數(shù)據(jù)是否已經(jīng)被發(fā)送完成了。
USART_FLAG_TC 是宏定義的數(shù)據(jù)發(fā)送完成標(biāo)識(shí)符。
usart.h
#ifndef __USART_H #define __USART_H #include "stdio.h" #include "sys.h" #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收 #define USART_REC_LEN 200 //定義最大接收字節(jié)數(shù) 200extern u8 USART_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個(gè)字節(jié).末字節(jié)為換行符 extern u16 USART_RX_STA; //接收狀態(tài)標(biāo)記 //如果想串口中斷接收,請(qǐng)不要注釋以下宏定義 void uart_init(u32 bound); #endif總結(jié)
以上是生活随笔為你收集整理的【STM32】串口收发主要程序代码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【STM32】按键检测实验主要程序代码分
- 下一篇: 【Paper】2018_Nonlinea