日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

正点原子STM32串口通讯实验详解

發布時間:2023/12/10 编程问答 70 豆豆
生活随笔 收集整理的這篇文章主要介紹了 正点原子STM32串口通讯实验详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這幾天看完了正點原子STM32的串口通訊部分的內容,總感覺很多東西似是而非,前后花了好幾天研究了下,這篇博客很多內容是從其他博客上整理來的,并非完全原創,由于前后查了幾天好多篇博客,摘抄的誰的也不好找了,看到的可以提醒一下,只希望自己整理的內容能幫到其他的初學者。

經提醒第二部分來源于這篇博客?,感興趣的可以去原文看看。

1、實驗內容梳理

首先結合串口調試助手對實驗進行說明,以便后續結合代碼熟悉整個流程。整個實驗其實就是通過串口調試助手向單片機發送數據,然后單片機將接收到的數據返回給上位機并加以顯示。

簡單來串口調試助手說其實就是用于上位機和下位機通信用的一個橋梁軟件,功能主要有兩個這也是本實驗的兩個步驟:

  • 人工發送數據給單片機處理,即通過串口調試助手的下方窗口編輯數據,然后點擊發送按鈕,就能發送數據給單片機;
  • 接受單片機發送的數據顯示給你看,即通過串口調試助手上方窗口,將單片機發回給上位機的數據進行顯示;
  • 關于串口調試助手,還應知道:

    • 發送英文字符需要用一個字符即8位,發送漢字需要兩個字符即16位,如上圖,發送漢字“宋”實際是發送“CB(1100 1011)CE(1100 1110)”而發送英文字符S實際是發送“53(0101 0011)”,本質上沒有太大區別;
    • 勾選了下方“發送新行”后,XCOM就會再你輸入的需要發送的數據后自動加上一個回車(0X0D+0X0A),如果不勾選則我們在手動輸入完“宋S”后還需敲一個回車鍵只有這樣點擊發送后,調試助手上方窗體才能將其顯示,這是因為我們在程序的串口中斷中自定義了一個數據接收協議,即只有當接受的數據以回車結尾(0X0D+0X0A),串口才認可數據接受完畢。

    2、對于串口中斷函數(自定義數據接收協議)的理解

    正點原子的例程中通過語句USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)開啟相關中斷,當讀數據寄存器非空,即單片機一接收到數據時,便會觸發串口1的中斷函數。

    改協議的核心是定義了一個16位的變量USART_RX_STA,該變量的0-13位用于存儲接收到的數據,最后的14、15兩位作用在于,當14、15位依次接收到da0x0d和0x0a時,依次將這兩位置1,作為判斷數據是否接收完的標志位。

    以下是我看到的一個注釋比較詳細的代碼,和一個實例,結合兩者就可以很好的理解這個過程和代碼,

    void USART1_IRQHandler(void) //串口1中斷服務程序{u8 Res; //定義unsigned char型字符Resif(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾)//這里判斷發送接收完成的依據就是串口數據0x0d 0x0a,//0x0d是CR(carriage return)回車的意思,光標回到最左邊,//0x0a是LF(line feed)換行的意思,光標到達下一行,//但是在PC上回車和換行是在一起的就是按下回車按鍵//當然可以更改程序使用其他進行判斷例如使用0x2a也就是*進行結束判斷{Res =USART_ReceiveData(USART1);//(USART1->DR); //讀取接收到的數據,存放到變量Res中if((USART_RX_STA&0x8000)==0)//判斷接收是否未完成//接收完成未清除標志位,還是會不斷進入到接收中斷,所以使用標志進行判斷,//當接收完成便不會跳入到判斷,從而不執行任何指令,空等待//使用條件判斷是否已經接收完數據,這里判斷接收完的依據就是收到了0x0a;//具體判斷在后面{if(USART_RX_STA&0x4000)//如果接收到了0x0d,那么再進一步執行是否接收到0x0a的判斷{if(Res!=0x0a)USART_RX_STA=0;//沒有接收到0x0a那么說明,數據未正確傳輸或者接收錯誤,重新開始判斷,//但是這里沒有將接收到的數據進行清空,也沒有退出接收中斷,此程序只是從頭開始執行接收判斷else USART_RX_STA|=0x8000; //接收完成了,收到了0x0a那么標志位USART_RX_STA將會變成0x8000,將不再進行數據檢測與存儲}else //還沒收到0X0D,說明數據還未發送結束繼續進行數據的檢測與存儲{ if(Res==0x0d)USART_RX_STA|=0x4000;//收到了數據0x0d,標志位USART_RX_STA變成0x4000else{//如果沒有接收到數據0x0d,執行判斷是否存儲數組已滿,已滿則重新開始接收USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;//將接收到的數據寫進數組,標志位USART_RX_STA與上0X3FFF清除前兩位以防止標志位與8000和4000沖突USART_RX_STA++;//數組地址加一,向后排if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,超出數組大小,又開始接收向數組重新寫 } }} } }

    假設我們發送的數據是“abcd”經過串口調試助手加上0X0D+0X0A后發送給單片機,即單片機要接受的數據是“abcd”+“0X0D+0X0A”

    (1)當接收到“a”時讀寄存器非空,RXNE為1,第一次進入串口中斷處理函數,我們先判斷是否接是因為USART1接受到了數據產生的中斷,如果是,則將USART1接受到的一位數據“a”存入變量Res里(Res = “a”)

    if(USART_GetFlagStatus(USART1, USART_IT_RXNE) != RESET)

    (2)然后我們來判斷接收的一系列數據是否沒接收完(即)。(當然沒有啦,我們還有b、c、d三個數據沒有接收)。這個時候呢,USART_RX_STA——這個在全部函數之間實現消息傳遞的變量的值仍然為0,和0x8000相與以后為0,那么執行該if語句的內層函數。

    if((USART_RX_STA&0x8000)==0) //接收未完成

    (3)進入該if語句的內層語句后判斷語句如下,這里判斷USART_RX_STA的第14位是否為1,如果我們接收到了回車,即0x0d那么USART_RX_STA的第14位會置1。在我們接收第一個數據a時USART_RX_STA當然還為0,(我們后面的的b、c、d、3個數據還都沒接收了,當然不會收到0x0d)USART_RX_STA和0x400相與為0,該判斷語句就為假,執行下面的else語句。

    if(USART_RX_STA&0x4000)//接收到了0x0d

    (4)該else語句的內層語句是一個if-else語句

    if(Res==0x0d)USART_RX_STA|=0x4000;

    這里我就不再贅述,我們接收的是數據a,還沒有接收到0x0d,執行else語句。

    USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; // 0x3ff = 0011 1111 1111 1111USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收

    USART_RX_STA的bit0~bit13代表的是接收到的有效數據個數,這里USART_RX_STA值仍為0,USART_RX_STA & 0X3FFF = 0 ,然后USART_RX_BUF[USART_RX_STA&0X3FFF]=Res,意思就是將Res里的數據存放到USART_RX_BUF[0]里了,并且USART_RX_STA自增1。

    此時USART_RX_STA = 1這樣在接收到下一個數據b后USART_RX_STA&0X3FFF = 1,將b存入到了USART_RX_BUF[1]里,一直循環下去,直到我們接收到了0x0d(Res = 0x0d)。

    我們可以從上面程序里找到如下代碼:

    else //還沒收到0X0D{ if(Res==0x0d)USART_RX_STA|=0x4000; //再次判斷這次接收到的是不是0x0d

    當接收到0x0d并且程序執行到這一步一時USART_RX_STA = 4,此時該if語句成立,執行USART_RX_STA|=0x4000,即0000 0000 0000 0100 | 0100 0000 0000 0000 = 0100 0000 0000 0100 ,我們可以清晰的看到bit13~0位是4,代表接收到了4個數據(a,b,c,d),第14位為1,是因為接收到了數據0x0d,也和最上面給的表對上了,然后程序向下執行,接收到了0x0d(回車),那下一個就是接收0x0a(換行)了,Res = 0x0a:?????????

    if(USART_RX_STA&0x4000)//接收到了0x0d 0100 0000 0000 0000{if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始--接收到了0X0d但是沒有接受到0x0aelse USART_RX_STA|=0x8000; //接收完成了--0x0d后面是0x0a }

    這里第一個判斷語句if(USART_RX_STA&0x4000),當然為真,因為Res = 0x0a,那么執行else語句USART_RX_STA|=0x8000,即0100 0000 0000 0100 | 1000 0000 0000 0000 = 1100 0000 0000 0100。到了這一步,就說明這一串數據已經完完全全的接收完了USART_RX_STA = 1100 0000 0000 0100,最高位為1:代表接收到了0x0a,第十四位為1:代表接收到了0x0d,第0位到第13位為4,代表接收到了4位有效數據(a、b、c、d)

    3、對main函數的理解

    main函數如下,大致的執行流程為:

    int main(void) { u8 t;u8 len; u16 times=0; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置系統中斷優先級分組2delay_init(168); //延時初始化 uart_init(115200); //串口初始化波特率為115200LED_Init(); //初始化與LED連接的硬件接口 while(1){if(USART_RX_STA&0x8000){ len=USART_RX_STA&0x3fff;//得到此次接收到的數據長度printf("\r\n您發送的消息為:\r\n");while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);for(t=0;t<len;t++){USART_SendData(USART1, USART_RX_BUF[t]); //向串口1發送數據while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待發送結束}printf("\r\n\r\n");//插入換行USART_RX_STA=0;}else{times++;if(times%300==0){printf("\r\nALIENTEK 探索者STM32F407開發板 串口實驗\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); }} }
  • 完成串口的相關設置,使能相應中斷,以及初始化過程,然后進入while等待;
  • 當在串口調試助手的想窗體輸入想要發送的數據,并點擊發送后,當單片機開始接受數據時便會觸發中斷函數USART1 IRQHandler( )對數據進行接收并最終存儲進緩存RX_BUF_USART中,其在usart.h中的生命為extern u8 ?USART_RX_BUF[USART_REC_LEN]。其中USART_REC_LEN=200
  • 通過語if(USART RX STA&0x8000)判斷數據是否完成接受存入緩存數組RX_BUF_USART中,如果完成了,則利用重定向的printf函數向上位機發送語句“\r\n您發送的消息為:\r\n” , 如下圖標號1區域(此處實驗現象異常,后面會有解釋)然后通過for循環將緩存RX_BUF_USART中的數據依次通過USART_SendData()函數發給上位機,發給上位機的數據將在串口調試助手上顯示。所有數據發送完畢后,再次通過printf函數向上位機發送"\r\n\r\n"即在串口調試助手上窗體光標在原來位置的基礎上下移兩行。如下圖標號2區域所示。
  • 當數據沒有接受完,或者沒有向單片機發送數據時,單片機便會通過printf函數向上位機按照一定時間間隔發送“\r\nALIENTEK 探索者STM32F407開發板 串口實驗\r\n”、“正點原子@ALIENTEK\r\n\r\n”和“請輸入數據,以回車鍵結束\r\n”語句。
  • 注意:重定向的printf()函數本質上還是通過USART_SendData()向上位機發送數據,且此處發送的“\r\n”與中斷函數里的需要作為接受完成標志位的“\r\n”(0X0D+0X0A),只是單純的表示換行的轉義字符,電腦上位機接收到后會將光標下移兩行,視覺上就是空一行顯示在串口調試助手上。

    4、實驗異常現象及解決辦法

    上面有提到過圖片標號1的區域出現了異常實驗現象,即單片機利用printf("\r\n您發送的消息為:\r\n")語句顯示的結果中語句末尾的“\r\n”并沒有起到作用,即讓光標移至下一行后再顯示“逆光”。

    這種現象的原因是因為printf語句中的“\r\n”發送未完成,寄存器又被后面要發送的數據覆蓋,解決方法就是在USART_SendData(USART1, USART_RX_BUF[t]);前面在多加一句 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待發送結束

    for(t=0;t<len;t++){while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);USART_SendData(USART1, USART_RX_BUF[t]); //向串口1發送數據while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待發送結束}

    修改后實驗現象恢復正常如下:

    我嘗試過在printf語句的后面利用延時的方式解決問題,但是只有第一次能夠正常顯示,后面再次發送,現實的數據就會存在亂碼的問題。

    5、USART-FLAG-TXE與USART-FLAG-TC標志位

    我覺得這兩個標志位的使用應當嚴格區分開,當個附加知識記錄一下:

    USART-FLAG-TXE發送緩沖區空標志:說明可以往數據寄存器寫入數據了,但并不代碼數據發送完成了。

    USART-FLAG-TC發送完成標志:這個才是代表USART在緩沖區的數據發送完成了,即從機接收到了數據。

    這兩個標志的區別在于:它們分別表示數據在發送過程中,在兩個不同的階段中的完成情況.TXE表示數據被從發送緩沖區中取走,轉移到的移位寄存器中,此時發送緩沖是空的,可以向其中補充新的數據了。而 TC則表示最后放入發送緩沖區的數據已經完成了從移位寄存器向發送信號線Tx上的轉移。所以,判定數據最終發送完成的標志是TC,而不是IXE.

    總結

    以上是生活随笔為你收集整理的正点原子STM32串口通讯实验详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。