1. 程式人生 > >stm32串列埠通訊的一個小總結(從底層進行理解)

stm32串列埠通訊的一個小總結(從底層進行理解)

從底層理解stm32USART串列埠通訊

以前學串列埠通訊踩過很多坑,過了一段時間又有些忘了,現在問了幾個很強很強的人差不多弄懂了,現在寫一寫總結,免得以後又忘了。

基本知識:

1、TDR和RDR都是USART_DR暫存器的緩衝區,指的是USART_DR的0到8位,TDR和RDR共用一片物理空間。

2、

通過向資料暫存器寫入資料來將 TXE 位清零。

通過軟體對 USART_DR 暫存器執行讀操作將 RXNE 位清零.

3、TXEIE和TCIE的意義是,TXEIE允許在TXE標誌為1時產生中斷,而TCIE允許在TC標誌為1時產生中斷,RXNEIE同理。

4、

5、

6、

有三個主要的暫存器USART_SR,USART_DR,USART_CR1 。USART_SR的S代表STATUS,這個暫存器用來記錄串列埠是否收到了資料。含有RXNE,,TC,TXNE。USART_CR1 暫存器中含有TCIE ,TXEIE,RXNEIE等串列埠中斷使能位。

USART_DR是用來儲存資料的暫存器,它的0到8位是用來儲存資料的,叫做緩衝區,也稱為RDR,TDR,在接受的時候成為RDR,在傳送的時候稱為TDR,其實是一個東西,這也是為什麼TDR與RDR共用同一片物理空間的原因。其餘位不用考慮。同時在程式裡可以設定,傳輸的時候是8位還是9位,個人建議傳輸都用8位,因為現在編碼大多采用以位元組為單位的方式,在用9位的情況下,如果發生上溢錯誤,就會改變bit的解碼順序,造成後面傳來的資料解碼混亂。而如果用8位,丟掉一個位元組,後面的位元組還是可以正常解碼出來。

7、

8、不論是傳送資料還是接受資料,一個串列埠只有一箇中斷函式。

接受資料的過程:

       RX設定為floating浮空狀態,適於被動地在高電平低電平之間的快速轉換,它的狀態是由傳送端傳來的電平高低來決定的。

       資料通過串列埠線一位一位傳過來,先傳到移位暫存器,“移位”就是形容一位一位bit傳過來的過程,一個數據幀有起始位,資料位,校驗位,停止位。當移位暫存器識別了這個資料幀之後,通過“並行通訊”的方式“一次性”傳遞給RDR暫存器,就是把八個資料位一次性傳給RDR暫存器,我們從內部匯流排向RDR暫存器寫入資料的時候也是並行通訊。這也就是為什麼在參考手冊中講(第三張圖片),RDR暫存器在移位暫存器和內部匯流排之間提供了並行介面。

為什麼資料可以有秩序地傳輸呢?因為移位暫存器通過起始位,校驗位,停止位來“辨識”這個資料幀,一個沒有起始位,校驗位,停止位(根據通訊協議的規定而變化)的裸露資料是不可能由移位暫存器傳給RDR暫存器的,這也就是為什麼當一個數據發生錯誤的時候,後面的資料繼續傳輸不會受到前面的影響。(我之前在考慮前一個數據的後半部分與後一個數據的前半部分進行組合形成錯誤資料幀的情況,我想多了。)

當移位暫存器中的資料位傳到RDR中時,RXNE會硬體置為1,這個所謂的硬體置1就是靠微控制器“自動”完成的,就相當於提醒我們緩衝區有資料了,我們要去讀了。這個時候如果USART_CR1的RXNEIE接受中斷使能位開啟了,也就是手冊中的軟體置1,而所謂的軟體置1就是指人為去設定,那麼就會進入串列埠中斷。我們在串列埠中斷中去讀取RDR緩衝區裡面的資料內容,而一旦讀取了RDR中的內容,RXNE標誌位會硬體置0。下次還有資料傳進來的時候,RXNE又會置1。如此迴圈往復。

如果串列埠中斷的時間太久了,或者說遲遲沒有讀取RDR暫存器的內容,那麼就會發生上溢錯誤。上溢錯誤在第3張圖中有簡介,這個時候移位暫存器不會把資料位通過並行通訊傳給RDR,而是被後面傳入的資料所覆蓋,這裡的覆蓋是一位一位的,有人可能會問,如果資料位有一半被後面傳來的資料覆蓋了,同時這個時候軟體進行了讀取,RXNE置為0,那麼這一半的資料位還會進入RDR嗎?我認為不會出現這樣的,如果整個資料幀有一位被上溢錯誤給破壞,那麼整個資料幀都會被丟棄,從下一個完整地資料幀開始傳送。理由是這樣不會干擾到後面的資料傳輸。

傳送資料的過程:

TX引腳設定為推輓輸出模式。傳送資料類似於我們從RDR暫存器讀取資料,我們發資料就是往TDR暫存器中寫資料(這個賦值語句是通過並行通訊實現的),RDR和TDR暫存器表示的物理空間是一樣的。然後TDR暫存器會通過並行通訊把資料傳給移位暫存器。這個時候TXE會置1,再次向TDR寫入資料會使TXE置0,TDR進入移位暫存器之後,會一位一位傳送,如果移位暫存器把資料幀傳送完了,同時TDR為空,即TXE仍為1(意味著不僅移位暫存器沒事幹了,DR暫存器也沒事幹了,全弄完了),那麼TC標誌位置1。

具體的過程是:所有位傳送結束時(送出停止位後)硬體會設定TC標誌,當移位暫存器把資料幀的停止位送出之後,去檢測TXE是否為1,如果TXE1,則TC標誌位置1,產生中斷

TXE--寫暫存器DR清零,(完全由硬體控制,有東西就是0,沒東西就是1,無法軟體清零)

 TC--  第一種方法,讀SR暫存器後寫暫存器DR清零(好麻煩有木有),第二種方法,也可軟體手動清零,直接手動暴力清零。

1、只利用TC中斷來發送資訊

void USART_Config()//開啟中斷,開啟串列埠

{

  USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete後,才產生中斷. TC中斷必須放在這裡,否則還是會丟失第一位元組

  USART_Cmd(USART1, ENABLE); //使能USART1

}

void USART_SendDataString( u8 *pData )//傳送資訊的函式。{     pDataByte = pData;        USART_ClearFlag(USART1, USART_FLAG_TC);//清除傳輸完成標誌位,否則可能會丟失第1個位元組的資料.網友提供.//在傳送之前先把標誌位清空一下,保險一點,沒有太大含義    USART_SendData(USART1, *(pDataByte++) ); //必須要++,不然會把第一個字元t傳送兩次。附加註釋:這句話作為一個引線來觸發這個串列埠中斷。

}

void USART1_IRQHandler(void){     if( USART_GetITStatus(USART1, USART_IT_TC) == SET  )//SR暫存器    {         if( *pDataByte == '\0' )

            USART_ClearFlag(USART1, USART_FLAG_TC);發完了 把標誌位手動清零,因為之後不會再向DR暫存器裡面寫內容了,無法通過第一種方式將TC標誌位清零。    

else            USART_SendData(USART1, *pDataByte++ );//DR暫存器寫內容,同時

TC標誌位會清零。    } }

extern u8 *pDataByte;在主函式中寫好這個指標變數,在USART.C檔案中作為外部變數引進來。

C語言小提醒: *p = *(p+1); 而且是後加加。

2、只利用TXE中斷來發送資訊

void USART_SendDataString( u8 *pData ){     pDataByte = pData;     USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要傳送暫存器為空,就會一直有中斷,因此,要是不傳送資料時,把傳送中斷關閉,只在開始傳送時,才打開。     }  

void USART1_IRQHandler(void){     if( USART_GetITStatus(USART1, USART_IT_TXE) == SET  )     {         if( *pDataByte == '\0' )//待發送的位元組發到末尾NULL            USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因為是 傳送暫存器空 的中斷,所以發完字串後必須關掉,否則只要空了,就會進中斷        else             USART_SendData(USART1, *pDataByte++ );     } }

個人感覺第二種傳送方式好一點點。通常的協議一般是8位傳送,一位停止位,無資料流控制,無校驗位。

程式碼參考自:https://blog.csdn.net/zyboy2000/article/details/7566647