1. 程式人生 > >一次GPRS除錯引出的大發現--STM32傳送字串第一個字元丟失

一次GPRS除錯引出的大發現--STM32傳送字串第一個字元丟失

這幾天一直再做gprs的除錯工作,開始從網上得到的例程,在微控制器上給GPRS發指令,微控制器可以收到模組返回的指令。講這次經歷之前,先簡單介紹下微控制器如何給GPRS發AT指令的,AT指令有很多,在每發一個AT指令後都需要微控制器給模組再發送一個回車“\r\n"字元才表示傳送完成。下面是參考的傳送指令部分:

for (; *b!='\0';b++)//*為傳送的AT指令
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET);
USART_SendData(USART2,*b);//UART2_SendData(*b);
}

USART2_Puts("\r\n");//傳送回車

然後我參考了例程的傳送指令函式移植到自己的工程裡,結果一個百思不得其解的現象出現了:自己的程式下到微控制器,然後給GPRS發指令,卻無法得到模組的反應。因為這個問題除錯了整整一天,同樣的傳送指令函式,到了自己的程式裡怎麼就無法實現了?當時的內心幾乎是崩潰的!!

其實,還是自己不夠細心(之前一直以為傳送指令函式沒問題而一直忽略)。後面的不斷除錯和檢查中,在程式中發現了一個非常重要的細節,自己寫的傳送回車字元的函UART2_SendLR();

 例程的該函式內容是:

void USART2_Puts(char * str)

  while(*str)
  {
while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET); 
USART_SendData(USART2 ,*str++);//傳送當前字元

  } 
}

而自己的函式是:

void USART2_Puts(char * str)

  while(*str)
  {
USART_SendData(USART2 ,*str++);//傳送當前字元
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE)==RESET); 
  } 
}

兩條語句的順序和標誌位不一樣,之前一直用這函式都正常,所以沒去在意,但和先前的發AT指令部分結合,問題非常明顯了,用我的函式話,傳送完AT指令,那麼未進行標誌位判斷就立馬傳送回車字元,此情況是不允許的,這樣後面的字元會把前面的一個覆蓋了,也就是AT指令的最後一個字元會消失,導致了GPRS無法識別而無返回資料。

因此將自己的函式進行了修改,問題解決了。

下面就是該問題牽引出的一個發現。由於兩函式判斷標誌位不一樣,判斷與傳送的次序也不一樣,因此出於好奇,想通過此函式傳送字串給上位機,看顯示效果是否一樣,

結果發現了一個奇怪現象:例程的函式傳送的字串,第一個字元總會在程式復位開始時會丟失。而當使用USART_FLAG_TXE來判斷時,可以正常。

通過網上查找了這兩標誌位的區別,查到了微控制器的串列埠傳送接收機制

傳送:軟體將資料寫到USARTx->DR裡面,硬體自動把USARTx->DR裡面的資料並行轉移到“傳送一位暫存器”,然後硬體自動將傳送一位暫存器中的資料通過TX引腳序列傳送出去。

接收:RX上有資料過來,則先將資料一位一位的放到“接收移位暫存器”裡面,收滿一個位元組後,硬體自動將“接收移位暫存器”裡面的資料並行轉移到USARTx->DR裡面。

while (!(USART1->SR & USART_FLAG_TC));這句是等待資料完完全全從“傳送移位暫存器”中傳送出去,while (!(USART1->SR & USART_FLAG_TXE));這句是等待資料從USARTx->DR轉移到“傳送移位暫存器”中。在後一句的寫法中,並不需等待資料完全傳送,只需要USARTx->DR裡面的資料已經被轉移走,就可以馬上進行再次寫入USARTx->DR操作,這樣是不是就可以節省很多時間了?

至於出現亂碼,你需要綜合其他的程式碼來進行分析。(注:while (!(USART1->SR & USART_FLAG_TC));相於 while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET); while (!(USART1->SR & USART_FLAG_TXE));相當於while(USART_GetFlagStatus(USART2, USART_FLAG_TXE)==RESET); )

因此,USART_FLAG_TXE會先於USART_FLAG_TC判斷,只要資料傳送,USART_FLAG_TXE就會置0;而USART_FLAG_TC的判斷會有所延遲,需要資料到了移位暫存器才能為0。這也就解釋了為什麼用USART_FLAG_TC會使得第一個資料無法判斷,而丟失。