USART—通用同步異步收發接收器,是一個串行通信設備,可以和外部設備進行靈活的全雙工數據交換,有別于USART還有一個UART(在原來的基礎上裁剪掉了同步通信功能(時鐘同步)),串行通信一般是以幀格式傳輸數據,一幀一幀的傳。
協議層:串口通信的一個數據包包含從起始信號開始,直到停止信號的結束起始信號:一個邏輯0數據位表示。停止信號:0.5,1,1.5或2個邏輯1的數據位表示。0.5個停止位:智能卡模式下的接收數據時使用。1個停止位:停止位的默認數值1.5個停止位:智能卡模式下的手法數據和接收數據時使用2個停止位:常規USART模式,單線模式以及調制解調器的模式。有效數據的基本長度被約定為5,6,7,8.奇偶檢驗(設置USART-CR1 的PS位)偶檢驗:數據=00110101,里面數據1的個數位為偶數位,檢驗位置“0”,當數據檢驗和偶數相同的時候,證明沒有出錯,反之則錯誤奇檢驗:數據 = 01110101,里面數據1的個數為奇檢位,檢驗位置“1”,當數據檢驗和奇數相同,則證明沒有出錯,反之錯誤。當然也會存在同時兩個位一塊出現錯誤,導致無法判斷是否位奇偶檢驗的錯誤,但發生的概率很低。
下面這張圖需要重點理解下面是對代碼的理解:首先先看這個圖,,可以看出USART_RX_STA類似與一個16位的寄存器,前14位存儲的是數據,后面兩個分別檢測0X0D和0X0A。接下里分析:
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 }
temp=(float)(pclk2*1000000)/(bound*16);這是一個計算公式,因為使能的是串口1,而串口1是在APB2ENR寄存器里面(其余串口均在寄存器APB1ENR里面),因為APB2的頻率一般位72M,而APB1的頻率一般位36M。所以這里的pclk2為72M,而bound是你需要設置的波特率。USARTX-BRR:
前四位為小數部分 ,后12位是整數部分,假設算出來的mantissa = 39.5,小數部分相當于把1分成了16份,所以相當于把0.5*16轉化為二進制存入。mantissa = temp的作用僅僅是:為了接下來將小數部分求出來 fraction=(temp-mantissa)*16; //得到小數部分 mantissa<<=4;
這兩行代碼是為將十進制的整數部分和小數部分,分別轉化為16進制。然后存入到波特率寄存器里面。緊接著使能串口1和PORTA時鐘(串口一對應的IO口是PA9,PA10,需要拿跳帽連接在一起).然后將IO口置零,然后分別進行設置成一個輸入一個輸出,USART1->CR1|=0X200C; 設置成使能串口8個字長1個停止位(USART_CR2中[13:12]默認為“0”) MY_NVIC_Init(3,3,USART1_IRQn,2)
將其分在組2里面,此時的搶占優先級:響應優先級為 = 2:2,即(00-11)四種情況,而3:3的安排選擇了組2優先級最小的一種情況。這樣可以先執行上面的波特率賦值,以及串口使能等等操作,最后再進行這行代碼運行。接下來看下一部分:u16 USART_RX_STA=0; //接收狀態標記 void USART1_IRQHandler(void) { u8 res; #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支持OS. OSIntEnter(); #endif if(USART1->SR&(1<<5)) //接收到數據 { res=USART1->DR; if((USART_RX_STA&0x8000)==0)//接收未完成 { if(USART_RX_STA&0x4000)//接收到了0x0d { if(res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始 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;//接收數據錯誤,重新開始接收 } } } }
起始階段: USART_RX_STA=0,對接受狀態的標記。先通過狀態寄存器SR的RXNE是否為1,是1則接收到了數據,反之則沒有。緊接這定義一個res變量來接收從數據寄存器的一個字節,然后此時USART_RX_STA為0,與0X8000進行&運算,結果為0,則未接受到,接著繼續進行判斷,0X4000進行與運算,看是否為0,也是判斷是否接受道路0X0D,如果沒有接受到,則將這個res變量存放在數組里面,此時的USART_RX_STA為 0 與0X3fff進行&運算,大家算算會發現,因為他的前14位是數據位,所以你會發現第一個變量就會存放在BUF[0]里面,大概邏輯是這樣的:所以每個字節都會被存放到具體的數組位上 。if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收當數組越界的時候,則會重新開始。接下來就會一直循環,當數據位存滿后,接下來res里面接受的就是0X0D,先和上面一樣判斷USART_RX_STA是否接受到了0X0A和0X0D。接著執行:if(res==0x0d)USART_RX_STA|=0x4000;
將USART_RX_STA的第十五位變為1,,接下來進行下一次循環,這一次res接受到的值為0X0A,然后進行判斷進入到if(USART_RX_STA&0x4000)//接收到了0x0d { if(res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始 else USART_RX_STA|=0x8000; //接收完成了 }
所以執行USART_RX_STA|=0x8000,使得USART_RX_STA的第十六位變為1。接下來看主函數部分:int main(void) { u8 t; u8 len; u16 times=0; Stm32_Clock_Init(9); //系統時鐘設置 delay_init(72); //延時初始化 uart_init(72,9600); //串口初始化為9600 LED_Init(); //初始化與LED連接的硬件接口 while(1) { if(USART_RX_STA&0x8000) { len=USART_RX_STA&0x3fff;//得到此次接收到的數據長度 printf(" 您發送的消息為: "); for(t=0;tDR=USART_RX_BUF[t]; while((USART1->SR&0X40)==0);//等待發送結束 } printf(" ");//插入換行 USART_RX_STA=0; }else { times++; if(times%5000==0) { printf(" ALIENTEK MiniSTM32開發板 串口實驗 "); printf("正點原子@ALIENTEK "); } if(times%200==0)printf("請輸入數據,以回車鍵結束 "); if(times%30==0)LED0=!LED0;//閃爍LED,提示系統正在運行. delay_ms(10); } } }
if(USART_RX_STA&0x8000) 判斷是否接收到了0X0Alen=USART_RX_STA&0x3fff;舉個簡單的例子此時USART_RX_STA為1100000000000011和0X3fff進行&運算,得到的結果是3,自然就表示了當前數組的大小。最后階段,重點理解以下兩行代碼:USART1->DR=USART_RX_BUF[t]; while((USART1->SR&0X40)==0);//等待發送結束
分析如下:將每個組內的信息存入到數據寄存器,此時數據寄存器將數據給TDR,發送信息的時候,是一位一位發送的,每一數據幀都有起始位,數據位,以及停止位,當檢測到數據寄存器的細信息發送完了(完全給了TDR),此時狀態寄存器的TXE便變為1,當檢測到TXE為1后,TC也會變為1(系統自動進行)。所以第二行才會檢測這個狀態寄存器的第6位是否為1來判斷是否發送成功了這個字節。由此推出,直接判斷TXE也可以判斷發送是否完成所以代碼可以改為:for(t=0;tDR=USART_RX_BUF[t]; while((USART1->SR&0X80)==0);//等待發送結束
審核編輯 :李倩
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
接收器
+關注
關注
14文章
2468瀏覽量
71873 -
STM32
+關注
關注
2270文章
10895瀏覽量
355744 -
通信設備
+關注
關注
3文章
337瀏覽量
32046
原文標題:老工程師:STM32的串口實驗(寄存器操作版本)
文章出處:【微信號:電子技術控,微信公眾號:電子技術控】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
STM32寄存器實現流水燈效果
一、實驗原理(一)寄存器地址查找STM32給不同的寄存器分配了不同的地址在《STM32中文參考手冊_V10》的第28頁,有不同
發表于 12-07 10:06
?2次下載
STM32寄存器實現流水燈效果
一、實驗原理(一)寄存器地址查找STM32給不同的寄存器分配了不同的地址在《STM32中文參考手冊_V10》的第28頁,有不同
發表于 12-07 10:21
?8次下載
【STM32—寄存器版本】實例之串口通訊
以我現在的水平還啥都不了解,所以這次主要說的就是通過USB串口和電腦通信**串口配置**:詳細配置可以參考STM32開發指南寄存器版本的5.
發表于 12-08 17:36
?0次下載
【STM32】基于cubemx+HAL庫的串口實驗(一)
stm32串口實驗一、實驗前準備二、具體項目代碼項目1:printf重定向實驗,輪詢方式通信重定向:輪詢方式:中斷方式:項目2:中斷方式的串口
發表于 12-09 13:06
?3次下載
STM32的USART串口通訊程序
文章目錄文章目錄文章目錄寄存器與固態庫一、了解串口通信及準備工作1.串口通信簡介2.STM32 的 USART 簡介3.實驗環境二、USAR
發表于 12-16 16:57
?10次下載
評論