串口出現亂碼,實際波特率如何測量?

與非網 發佈 2020-02-25T18:10:59+00:00

常見的通訊傳輸中,用0V 表示數字 0, 5V 表示數字 1,那麼一個碼元可以表示兩種狀態 0 和 1,所以一個碼元等於一個二進位比特位,此時波特率的大小與比特率一致。

平時使用串口列印出現亂碼的絕大部分原因是串口波特率沒對。那麼我們怎麼測量實際的波特率呢?在這之前,順便一起回顧一下波特率的概念。

什麼是波特率、比特率?

比特率(Bitrate)表示每秒鐘傳輸的二進位位數,單位為比特每秒(bit/s)。

波特率(Baudrate)表示每秒鐘傳送的碼元符號的個數,是衡量數據傳送速率的指標。

碼元是通訊信號調製的概念,通訊中常用時間間隔相同的符號來表示一個二進位數字,這樣的信號稱為碼元。

常見的通訊傳輸中,用 0V 表示數字 0, 5V 表示數字 1,那麼一個碼元可以表示兩種狀態 0 和 1,所以一個碼元等於一個二進位比特位,此時波特率的大小與比特率一致。

如果在通訊傳輸中,有 0V、2V、 4V 以及 6V 分別表示二進位數 00、 01、 10、 11,那麼每個碼元可以表示四種狀態,即兩個二進位比特位,所以碼元數是二進位比特位數的一半,這個時候的波特率為比特率的一半。

因為很多常見的通訊(比如串口通訊)中一個碼元都是表示兩種狀態,所以大家常常直接以波特率來表示比特率 。

串口通訊協議

在串口通訊的協議層中,規定了數據包的內容,它由啟始位、主體數據、校驗位以及停止位組成,通訊雙方的數據包格式要約定一致才能正常收發數據,其數據幀組成如下:

下面我們來實際驗證一下其數據幀是不是真的是這樣的。編寫如下代碼:

代碼很簡單,就是使用串口不斷地往外發數據 0xAA(當然發送其它數據也是可以的) 。我們的串口配置如下:

我們可以使用示波器或者邏輯分析儀抓取實際信號看看數據是不是符合上面的幀格式。這裡,我們使用邏輯分析儀抓取 USART1 的發送信號線(TX):

從實際結果中我們可以看到的確是按幀格式來發的。這裡可能會有人有疑問,上面那個數據幀的圖片中有個空閒狀態,這個又是什麼呢?空閒、空閒,當然是沒有在發數據時候的狀態呀,我們把我們的代碼改為:

在初始化完成之後只發送一次 0xAA,邏輯分析儀抓到的數據為:

可見,空閒狀態是個高電平。在上一個的範例中,我們一直在 while 循環中發送數據 0xAA,所以就沒有空閒狀態。

在這個實驗中我們需要知道的是兩個點是:

串口發送數據是低位先發的。我們單片機發 0xAA(10101010B),所以邏輯分析儀抓到的有效數據是 01010101B。

單片機的串口使用的是 TTL 電平,為正邏輯電平信號。邏輯分析儀抓到的數據 0 對應著實際電壓 0~0.5V,數據 1 對應著實際電壓 2.4V-5V,

經常與 TTL 電平標準做對比的是 RS-232 電平標準,如:

常見的電子電路中常使用 TTL 的電平標準,理想狀態下,使用 5V 表示二進位邏輯 1,使用 0V 表示邏輯 0;而為了增加串口通訊的遠距離傳輸及抗干擾能力,RS-232 電平標準使用 -15V 表示邏輯 1, +15V 表示邏輯 0。

在舊式的台式計算機中一般會有 RS-232 標準的 COM 口(也稱 DB9 接口) :

在這個示例程序中,我們設置的串口波特率為 115200bps。在串口通訊中,碼元只用 1 個二進位數來表示(即只有 0 和 1 兩種狀態),所以波特率與比特率在數值上是相等的。

而比特率表示的是每秒鐘傳輸的二進位位數,那我們知道傳一位數據的時間豈不是就可以反推出波特率是多少了嗎?從邏輯分析儀中,我們可以知道發送一位數據的時間如下:

發送一位數據的時間大約為 8.667us,所以 1 秒鐘發送多少位數據是可以算出來的:

算出來的波特率為 115380bps,與 115200bps 很相近。最終肯定是有一定的誤差,這個誤差產生的原因包括邏輯分析儀的質量及我們的測量環境等等因素。但是這個誤差也是在允許的範圍內的,可以看看串口助手接收到的數據是不是正確的:

可見,數據接收正確,也就是波特率對的上了。

串口波特率對不上怎麼解決?

在實際中。我們可能會遇到這樣的情況,代碼里配置的波特率與串口助手上設置的波特率一樣了,但還是出現異常情況。

異常情況如我們往串口助手發送字符串,串口助手上本該顯示的字符串出現了亂碼。或者我們往串口助手發送一個數據,發現數據移位了。

出這種情況大多是波特率對應不上,我們就得自己檢查我們的底層文件了,代碼中的某個與波特率計算相關的值(時鐘)與實際不匹配了,就會出現這樣的現象,比如之前我的一位同事就遇到這樣的情況就是這個原因導致的。

我們用 STM32 的時候,一般都是使用外部晶振,比如 STM32F103 系列,可輸入的外部晶振的範圍是 4~16MHz:

經驗值往往是 8MHz,而且一般的 demo 工程底層代碼里默認的也是設置為 8MHz,比如:

但是,如果實際晶振貼的不是 8M 的話,就出問題了(比如串口波特率就不正確了)。追根溯源,串口波特率是配進 USART_Init 函數中的,打開這個函數:

計算串口波特率需要一個 apbclock 變量,而這個值得來源從 RCC_GetClocksFreq 函數來,再打開這個函數:

所以要注意的是,HSE_VALUE 這個值要與實際做對應。

遇到這種問題找誰說理去。。經驗就是不斷采坑不斷積累的一個過程,早點遇到坑可能也是一件好事。像類似底層的問題很少遇到,但是一旦遇到那就得比較棘手的問題了,需要很有耐心地去查找。

能用穩定的晶片是一件很幸福的事情,用不穩定、不成熟的晶片的時候,那個才是真的難啊,遇到問題真是讓人懷疑人生啊,軟體、硬體、晶片都可能有問題。

關鍵字: