日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

STM32 串口總線空閑檢測

 鹵煮小魚 2017-04-24

主機環(huán)境:Windows XP  SP3

開發(fā)環(huán)境:MDK 5.20

目標芯片:STM32F030C8T6

前兩天在群里看到有人在詢問有關(guān)STM32 串口總線空閑檢測的事情,根據(jù)串口總線是否空閑來判斷一幀數(shù)據(jù)是否發(fā)送完成,之前使用串口一直沒怎么注意過這一串口特性,所以后來特意去看了下手冊中有關(guān)總線空閑檢測的指示,發(fā)現(xiàn)它的確是個好特性,之前都只是在串口中斷中接收數(shù)據(jù)在主循環(huán)中不斷的讀取數(shù)據(jù)然后檢測是否是一幀完整的數(shù)據(jù),之后再進行后續(xù)處理。這樣處理有一個不是很好的問題就是在主循環(huán)讀取串口數(shù)據(jù)時需要有個超時計數(shù)器來避免無串口數(shù)據(jù)時死等在那里,但如果使用串口總線空閑檢測的話,我們就不需要超時計數(shù)器了,只需要在檢測到串口總線空閑時把收到的數(shù)據(jù)全部讀走,然后檢測是否滿足一定的格式進而處理,這樣是的主循環(huán)的時間進一步減少,加速了系統(tǒng)的處理速度。

在STM32F030C8T6的參考手冊中串口中斷狀態(tài)寄存器USARTx_ISR中有一個IDLE位來表明是否檢測到總線空閑,如下圖所示:


并且給出了如何清除該標識,STM32F1系列芯片清除該標識的方法不同,可根據(jù)參考手冊來查詢,且該標識置位后就不再置位除非RXNE位再次置位,如果上位機一次性發(fā)送了1個字節(jié)數(shù)據(jù)則RXNE置位1次,IDLE置位1次,而如果上位機一次性發(fā)送了6個字節(jié)數(shù)據(jù),則RXNE置位6次,IDLE依然置位1次,只要在CR1寄存器中使能了串口總線空閑檢測就可以使用該特性了,使用標準庫編輯了一下測試代碼,uart頭文件如下

  1. #ifndef __UART_H__  
  2. #define __UART_H__  
  3. #include <stdint.h>  
  4. #include "stm32f0xx.h"  
  5. #include <stdio.h>  
  6.   
  7.   
  8. #define USARTx                          USART1  
  9. #define USARTx_GPIO_PORT                GPIOA  
  10. #define USARTx_GPIO_CLK                 RCC_AHBPeriph_GPIOA  
  11. #define USARTx_TX_PIN                   GPIO_Pin_9  
  12. #define USARTx_TX_SOURCE                GPIO_PinSource9  
  13. #define USARTx_TX_AF                    GPIO_AF_1  
  14. #define USARTx_RX_PIN                   GPIO_Pin_10  
  15. #define USARTx_RX_SOURCE                GPIO_PinSource10  
  16. #define USARTx_RX_AF                    GPIO_AF_1  
  17. #define USARTx_IRQn                     USART1_IRQn  
  18. #define USARTx_IRQHandler               USART1_IRQHandler  
  19. #define USARTx_CLK_ENABLE()             RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE)  
  20.   
  21. void uart_init (uint32_t baud);  
  22.   
  23. #endif  

uart的源碼文件如下

  1. #include "uart.h"  
  2. uint8_t buffer[100];  
  3. uint8_t cnt = 0,idle_detect = 0;  
  4.   
  5. void uart_init (uint32_t baud)  
  6. {  
  7.     USART_InitTypeDef USART_InitStructure;     
  8.     GPIO_InitTypeDef GPIO_InitStructure;       
  9.     NVIC_InitTypeDef NVIC_InitStructure;  
  10.   
  11.     //初始化串口時鐘以及串口端口時鐘  
  12.     RCC_AHBPeriphClockCmd(USARTx_GPIO_CLK, ENABLE);  
  13.     USARTx_CLK_ENABLE();  
  14.   
  15.     GPIO_PinAFConfig(USARTx_GPIO_PORT, USARTx_TX_SOURCE, USARTx_TX_AF);  
  16.     GPIO_PinAFConfig(USARTx_GPIO_PORT, USARTx_RX_SOURCE, USARTx_RX_AF);  
  17.   
  18.     GPIO_InitStructure.GPIO_Pin = USARTx_TX_PIN| USARTx_RX_PIN;                  
  19.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;         
  20.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      
  21.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;         
  22.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;    
  23.     GPIO_Init(USARTx_GPIO_PORT, &GPIO_InitStructure);    
  24.   
  25.     USART_InitStructure.USART_BaudRate            = baud ;            //設(shè)置波特率  
  26.     USART_InitStructure.USART_WordLength          = USART_WordLength_8b;  //8位數(shù)據(jù)位  
  27.     USART_InitStructure.USART_StopBits            = USART_StopBits_1;     //1位停止位  
  28.     USART_InitStructure.USART_Parity              = USART_Parity_No;     //無校驗位  
  29.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  //無硬件控制  
  30.     USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;   //發(fā)送與接收兩種方式  
  31.     USART_Init(USARTx, &USART_InitStructure);         
  32.   
  33.     USART_ITConfig(USARTx,USART_IT_RXNE,ENABLE);    //使能接收中斷,在接收移位寄存器中有數(shù)據(jù)時產(chǎn)生  
  34.     USART_ITConfig(USARTx,USART_IT_PE,ENABLE);  
  35.     USART_ITConfig(USARTx,USART_IT_ERR,ENABLE);  
  36.     USART_ITConfig(USARTx,USART_IT_IDLE,ENABLE);    //使能總線空閑檢測中斷  
  37.       
  38.     /* 使能 USARTx 中斷 */  
  39.     NVIC_InitStructure.NVIC_IRQChannel = USARTx_IRQn;  
  40.     NVIC_InitStructure.NVIC_IRQChannelPriority=0;  
  41.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
  42.     NVIC_Init(&NVIC_InitStructure);   
  43.   
  44.     USART_Cmd(USARTx, ENABLE);   
  45. }  
  46.   
  47.   
  48. int fputc(int ch, FILE *f)  
  49. {  
  50.     USART_SendData(USARTx,(uint8_t)ch);  
  51.     while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE) != SET);  
  52.     return ch;  
  53. }  
  54.   
  55. void USARTx_IRQHandler(void)  
  56. {  
  57.     uint8_t temp = 0;  
  58.       
  59.     if(USART_GetFlagStatus(USARTx,USART_FLAG_ORE) != RESET)  
  60.     {  
  61.         temp = USART_ReceiveData(USARTx);  
  62.         (void)temp;  
  63.         USART_ClearFlag(USARTx,USART_FLAG_ORE);  
  64.     }  
  65.     if(USART_GetFlagStatus(USARTx,USART_FLAG_NE) != RESET)  
  66.     {  
  67.         USART_ClearFlag(USARTx,USART_FLAG_NE);  
  68.     }  
  69.     if(USART_GetFlagStatus(USARTx,USART_FLAG_FE) != RESET)  
  70.     {  
  71.         USART_ClearFlag(USARTx,USART_FLAG_FE);  
  72.     }  
  73.     if(USART_GetFlagStatus(USARTx,USART_FLAG_PE) != RESET)  
  74.     {  
  75.         USART_ClearFlag(USARTx,USART_FLAG_PE);  
  76.     }  
  77.       
  78.     if(USART_GetITStatus(USARTx, USART_IT_RXNE) != RESET)//判斷寄存器中是否有數(shù)據(jù)  
  79.     {  
  80.         buffer[cnt++]=USART_ReceiveData(USARTx);  //讀取數(shù)據(jù),讀數(shù)據(jù)的同時清空了接收中斷標志;  
  81.         if(cnt >= 100)  
  82.         {  
  83.             cnt = 0;  
  84.         }  
  85.         USART_ClearITPendingBit(USARTx, USART_IT_RXNE);  
  86.     }  
  87.     if(USART_GetITStatus(USARTx, USART_IT_IDLE) != RESET)  
  88.     {  
  89.         //清除總線空閑中斷標志位  
  90.         USART_ClearITPendingBit(USARTx, USART_IT_IDLE);  
  91.         idle_detect = 1;  
  92.     }  
  93.     return;  
  94. }  

這里檢測到IDLE標識置位后置位idle_detect變量,cnt變量標記了本次接收到的字節(jié)個數(shù),主函數(shù)測試代碼如下:

  1. #include "uart.h"                    
  2.   
  3. extern uint8_t idle_detect,cnt;  
  4. extern uint8_t buffer[100];  
  5. int main (void)  
  6. {  
  7.     uint8_t i = 0;  
  8.     uart_init(115200);  
  9.   
  10.     while(1)  
  11.     {  
  12.         if(1 == idle_detect)  
  13.         {  
  14.             idle_detect = 0;  
  15.             printf("\r\ncnt:%X,ctx:",cnt);  
  16.             for(i = 0; i < cnt; i++)  
  17.             {  
  18.                 printf("%02X ",buffer[i]);  
  19.             }  
  20.             cnt = 0;  
  21.         }  
  22.     }  
  23. }  
代碼比較簡單,這里使用printf來輸出本次接收到的字節(jié)數(shù)以及字節(jié)內(nèi)容,運行結(jié)果如下:


可以看到檢測結(jié)果是正常的,只是在開機后有一次cnt為0的結(jié)果,應(yīng)該是上電之后總線默認就是空閑的,的確也沒有收到數(shù)據(jù),因此就想把cnt為0的結(jié)果去掉,這里我遇到了一個很糾結(jié)的問題,在判斷idle_detect變量的同時也檢測cnt是否大于0,測試代碼更改如下:

  1. #include "uart.h"                    
  2.   
  3. extern uint8_t idle_detect,cnt;  
  4. extern uint8_t buffer[100];  
  5. int main (void)  
  6. {  
  7.     uint8_t i = 0;  
  8.     uart_init(115200);  
  9.   
  10.     while(1)  
  11.     {  
  12.         if(1 == idle_detect && cnt > 0)  
  13.         {  
  14.             idle_detect = 0;  
  15.             printf("\r\ncnt:%X,ctx:",cnt);  
  16.             for(i = 0; i < cnt; i++)  
  17.             {  
  18.                 printf("%02X ",buffer[i]);  
  19.             }  
  20.             cnt = 0;  
  21.         }  
  22.     }  
  23. }  

感覺邏輯是對的,再次運行,結(jié)果如下:


這個時候發(fā)現(xiàn)cnt變量的值輸出一直為1,但輸出的字節(jié)內(nèi)容卻是對的,一直想不通這是為啥?思來想去邏輯是沒啥問題的,后來又把cnt是否為0的條件判斷不放在跟idle_detect變量檢測同一水平,而是放在它里面,更改測試代碼如下:

  1. #include "uart.h"                    
  2.   
  3. extern uint8_t idle_detect,cnt;  
  4. extern uint8_t buffer[100];  
  5. int main (void)  
  6. {  
  7.     uint8_t i = 0;  
  8.     uart_init(115200);  
  9.   
  10.     while(1)  
  11.     {  
  12.         if(1 == idle_detect)  
  13.         {  
  14.             idle_detect = 0;  
  15.             if(cnt == 0)  
  16.                 continue;  
  17.             printf("\r\ncnt:%X,ctx:",cnt);  
  18.             for(i = 0; i < cnt; i++)  
  19.             {  
  20.                 printf("%02X ",buffer[i]);  
  21.             }  
  22.             cnt = 0;  
  23.         }  
  24.     }  
  25. }  

這個時候再次運行代碼,結(jié)果如下:


這個時候輸出的結(jié)果是正確的,難道兩個條件檢測放在一起會有問題嗎?這個是不應(yīng)該的,因為idle_detect為0時CPU是不會再去檢測cnt變量的,只有idle_detect為1時才去檢測cnt變量,因此cnt的條件檢測放在里面和放在外面應(yīng)該是一樣的效果才對。后來想是否是printf引起的問題,就把printf去掉改成了自己的輸出函數(shù),更改測試代碼如下:

  1. #include "uart.h"                    
  2.   
  3. extern uint8_t idle_detect,cnt;  
  4. extern uint8_t buffer[100];  
  5. int main (void)  
  6. {  
  7.     uint8_t i = 0;  
  8.     uart_init(115200);  
  9.   
  10.     while(1)  
  11.     {  
  12.         if(1 == idle_detect && cnt > 0)  
  13.         {  
  14.             idle_detect = 0;  
  15.             uart_puts("\r\ncnt:");  
  16.             uart_char(0x30+cnt);  
  17.             uart_puts(",ctx:");  
  18.             uart_write(buffer,cnt);  
  19.             cnt = 0;  
  20.         }  
  21.     }  
  22. }  

這里cnt的輸出是有問題的,但我測試時保證cnt不會大于10,因此不影響測試結(jié)果,在編譯時把微庫去掉,運行結(jié)果如下:


結(jié)果發(fā)現(xiàn)在數(shù)據(jù)小于等于6時結(jié)果是正確的,而當數(shù)據(jù)大于6時cnt一直為6但字節(jié)內(nèi)容同樣是正確的,很是費解,在后來我又測試過當發(fā)送的字節(jié)為16時,輸出的數(shù)據(jù)內(nèi)容會少幾個字節(jié),總而言之把cnt變量的判斷放在idle_detect變量檢測的后面就會導致結(jié)果不對,而把cnt變量的判斷放在里面就不會有問題,所以應(yīng)該不是printf引起的問題,感覺像是if條件語句引起的問題,嘗試過很多方法也沒搞定該問題,雖然最后我們也能回避這個問題,但沒找到原因真糾結(jié),如果有人知道是啥問題的話,麻煩告知一下。

最后串口總線空閑檢測這一特性的確很有用,尤其是對收到的數(shù)據(jù)是不定長時更有效果,大家以后可以嘗試使用一下該特性。

PS:

今天下午不死心又進行了一些測試,還懷疑過是否是時序的問題,對比了一下cnt判斷所在位置不同的匯編代碼,對比結(jié)果如下:


左側(cè)是cnt判斷和idle_detect判斷在同一水平上,右側(cè)是cnt判斷放在了idle_detect條件檢測里面,兩者差別不大,再次證明我們的代碼邏輯是沒有問題的,后來又想起使用printf和使用我們自己的輸出函數(shù)cnt的值會有變化,在使用printf時cnt一直為1,而使用我們自己的輸出函數(shù)時cnt在數(shù)據(jù)長度小于等于6時正確,兩者的區(qū)別就是printf會使用微庫,延遲不同,所以增加了一個延遲函數(shù),如下:

  1. void delay(void)  
  2. {  
  3.     uint32_t t = 5000;  
  4.     while(t)  
  5.     {  
  6.         __NOP();__NOP();__NOP();__NOP();__NOP();  
  7.         t-=1;  
  8.     }  
  9. }  

更改主函數(shù)的測試代碼,delay()函數(shù)可以有兩處放置,如下:

  1. int main (void)  
  2. {  
  3.     uint8_t i = 0;  
  4.     uart_init(115200);  
  5.   
  6.     while(1)  
  7.     {  
  8.         delay();  
  9.         if(1 == idle_detect && cnt > 0)   
  10.         {  
  11.             idle_detect = 0;  
  12.             //delay();  
  13.             printf("\r\ncnt:%X,ctx:",cnt);  
  14.             for(i = 0; i < cnt; i++)  
  15.             {  
  16.                 printf("%02X ",buffer[i]);  
  17.             }  
  18.             cnt = 0;  
  19.         }  
  20.     }  
  21. }  

加上delay()函數(shù)后代碼運行的結(jié)果就正確了,區(qū)別是delay()函數(shù)如果放在if條件語句里面時當上位機發(fā)送的數(shù)據(jù)量多時需要的延遲就會長些才能保證結(jié)果的正確性,而delay()函數(shù)放在if條件語句的外面則不會這樣,總算找到問題了,但還是推薦把cnt的判斷放在if條件的里面,這樣就不需要增加延遲函數(shù)了,就這樣吧。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多