平時(shí)工作中程序員在調(diào)試時(shí)總會(huì)用到串口打印數(shù)據(jù)以及一些標(biāo)志位查看程序是否出現(xiàn)問題。但是在使用時(shí)總會(huì)遇到各種各樣的問題,最常見的就是輸出亂碼問題(指的是有收到數(shù)據(jù)但數(shù)據(jù)顯示的是一堆不認(rèn)識(shí)的漢字或字符),下面就我遇到過的串口亂碼問題的相關(guān)幾種解決方法:
方法一:檢查GND線連接情況
電子系統(tǒng)接地非常重要,接地不當(dāng)往往導(dǎo)致電子系統(tǒng)不能穩(wěn)定工作。
在串口通信時(shí)地線是必須接的,比如串行數(shù)據(jù)通信接口標(biāo)準(zhǔn)(RS—232)的3線TX、RX、GND。雖然在使用RS-485總線工業(yè)標(biāo)準(zhǔn)時(shí)接兩線TX、RX也能實(shí)現(xiàn)通信,但接Gnd 有利抑制干擾。RS-485的前身RS—422也一樣。
一般建議在使用串口通信時(shí)需要把GND連接上,尤其是在長(zhǎng)距離傳輸時(shí)。
方法二:查看串口助手和源程序就傳輸協(xié)議設(shè)置是否一致
串口通信最重要的參數(shù)是波特率、數(shù)據(jù)位、停止位和奇偶校驗(yàn)。在使用串口時(shí)這些參數(shù)必須要保持一致。下面大致介紹一些這些參數(shù)的意義:
一、波特率
波特率(bandrate)是衡量符號(hào)傳輸速率的參數(shù),指的是串口通信的速率,也就是串口通信時(shí)每秒鐘可以傳輸多少個(gè)二進(jìn)制位。比如串口常用波特率9600指的是串口每秒鐘可以傳輸9600個(gè)二進(jìn)制(傳輸一個(gè)二進(jìn)制位需要的時(shí)間是1/9600秒,也就是104us)。
注意波特率和比特率的區(qū)別:
情形一:一個(gè)信號(hào)碼元有且僅有兩種狀態(tài):0或1
此時(shí)每種狀態(tài)含一位二進(jìn)制數(shù)(0或1),在這種情況下比特率值=波特率值
情形二:一個(gè)碼元有4種不同的狀態(tài):01、11、00或10
此時(shí)每種狀態(tài)含兩位二進(jìn)制數(shù)(00、01、10、11),在這種情況下比特率值=波特率值×2
以此類推可以得到:比特率=波特率 * 單個(gè)調(diào)制狀態(tài)對(duì)應(yīng)的二進(jìn)制位數(shù)
二、數(shù)據(jù)位
數(shù)據(jù)位是衡量通信中實(shí)際數(shù)據(jù)的參數(shù)。當(dāng)計(jì)算機(jī)發(fā)送一個(gè)信息包,實(shí)際的數(shù)據(jù)不一定是8位的(標(biāo)準(zhǔn)的值是6、7和8位),如何設(shè)置取決于你想傳送的信息。
比如,標(biāo)準(zhǔn)的ASCII碼是0~127(7位),擴(kuò)展的ASCII碼是0~255(8位)。如果數(shù)據(jù)使用標(biāo)準(zhǔn) ASCII碼,那么每個(gè)數(shù)據(jù)包使用7位數(shù)據(jù);如果數(shù)據(jù)使用擴(kuò)展 ASCII碼,那么每個(gè)數(shù)據(jù)包使用8位數(shù)據(jù)(每個(gè)包是指一個(gè)字節(jié),包括開始/停止位,數(shù)據(jù)位和奇偶校驗(yàn)位)。
三、停止位
停止位是用于表示單個(gè)包的最后一位。典型的值為1,1.5和2位。
由于數(shù)據(jù)是在傳輸線上定時(shí)的,并且每一個(gè)設(shè)備有其自己的時(shí)鐘,很可能在通信中兩臺(tái)設(shè)備間出現(xiàn)了小小的不同步。因此停止位不僅僅是表示傳輸?shù)慕Y(jié)束,并且提供計(jì)算機(jī)校正時(shí)鐘同步的機(jī)會(huì)。適用于停止位的位數(shù)越多,不同時(shí)鐘同步的容忍程度越大,但是數(shù)據(jù)傳輸率同時(shí)也相應(yīng)越慢。
四、奇偶校驗(yàn)位
奇偶校驗(yàn)位是串口通信中在數(shù)據(jù)位后面加一位用于簡(jiǎn)單地檢查數(shù)據(jù)發(fā)送是否有錯(cuò)。有四種檢錯(cuò)方式:偶、奇、高和低。當(dāng)然沒有校驗(yàn)位也是可以的。
對(duì)于偶和奇校驗(yàn)的情況,串口會(huì)設(shè)置校驗(yàn)位(數(shù)據(jù)位后面的一位),用一個(gè)值確保傳輸?shù)臄?shù)據(jù)有偶個(gè)或者奇?zhèn)€邏輯高位。
例如,如果傳輸?shù)臄?shù)據(jù)是011,那么對(duì)于偶校驗(yàn)(校驗(yàn)位為0),則此時(shí)數(shù)據(jù)位 + 校驗(yàn)位的數(shù)據(jù)為:0110,保證邏輯高的位數(shù)是偶數(shù)個(gè)。如果是奇校驗(yàn)(校驗(yàn)位為1),則此時(shí)數(shù)據(jù)位 + 校驗(yàn)位的數(shù)據(jù)為:0110,這樣就有3個(gè)邏輯高位。高位和低位不真正的檢查數(shù)據(jù),簡(jiǎn)單置位邏輯高或者邏輯低校驗(yàn)。這樣使得接收設(shè)備能夠知道一個(gè)位的狀態(tài),有機(jī)會(huì)判斷是否有噪聲干擾了通信或者是否傳輸和接收數(shù)據(jù)是否不同步。
在實(shí)際使用中要確保這四個(gè)參數(shù)在源程序和助手設(shè)置是一致的。

方法三:在源程序中查看主頻設(shè)置是否有誤
上面兩個(gè)是一般會(huì)出現(xiàn)亂碼的原因,除此之外在源程序里面也會(huì)出現(xiàn)一些大意操作導(dǎo)致串口亂碼(最難且不容易想到的出錯(cuò)點(diǎn))。我目前尚未遇到過這種情況導(dǎo)致串口亂碼的但在不少大佬的博客和文章中看見有遇到過,這里也引用下來大家可以看看是否能解決你的串口亂碼問題。
STM32F407系統(tǒng)時(shí)鐘配置不準(zhǔn)確導(dǎo)致串口發(fā)送數(shù)據(jù)亂碼、定時(shí)器定時(shí)不準(zhǔn)問題。
https://blog.csdn.net/lqj11/article/details/108058008
stm32f407等芯片(HAL庫(kù))時(shí)鐘頻率修改(亂碼)
https://blog.csdn.net/bulefire2009/article/details/119633701?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-119633701-blog-108058008.pc_relevant_recovery_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-119633701-blog-108058008.pc_relevant_recovery_v2&utm_relevant_index=2
方法四:Printf重定向函數(shù)
關(guān)于重定向?qū)е聰?shù)據(jù)輸出亂碼我遇到過幾次,關(guān)于重定向引用一篇大佬的文章printf重定向原文鏈接:https://blog.csdn.net/RONG_YAO/article/details/115746940。這能解決大家關(guān)于重定向的一些疑惑。但我遇到過另外一種重定向?qū)е螺敵鰜y碼的情況。
在調(diào)試一個(gè)項(xiàng)目時(shí)一直出現(xiàn)亂碼問題,雖然能接到數(shù)據(jù)但一直顯示“IIIIIIIt”,看著不像亂碼但與實(shí)際要顯示的內(nèi)容天差地別(不管輸出的是漢字還是ASCII都一樣),前面幾個(gè)解決方法都檢查過沒有問題,但亂碼現(xiàn)象依然沒有改變。后面仔細(xì)研究源代碼發(fā)現(xiàn)問題出在printf重定向上,不是沒有添加重定向函數(shù),而是添加了兩個(gè)重定向函數(shù)(分別添加在了sys.c和usart.c中)
在sys.c函數(shù)中定義了一次:
#pragma import(__use_no_semihosting)
struct __FILE {
int handle;
};
#if( defined DEBUG)
int fputc( int c, FILE *f )
{
#if DEBUG == Debug_UART0
while( R8_UART0_TFC == UART_FIFO_SIZE ); /* 等待數(shù)據(jù)發(fā)送 */
R8_UART0_THR = c; /* 發(fā)送數(shù)據(jù) */
#elif DEBUG == Debug_UART1
while( R8_UART1_TFC == UART_FIFO_SIZE ); /* 等待數(shù)據(jù)發(fā)送 */
R8_UART1_THR = c; /* 發(fā)送數(shù)據(jù) */
#elif DEBUG == Debug_UART2
while( R8_UART2_TFC == UART_FIFO_SIZE ); /* 等待數(shù)據(jù)發(fā)送 */
R8_UART2_THR = c; /* 發(fā)送數(shù)據(jù) */
#elif DEBUG == Debug_UART3
while( R8_UART3_TFC == UART_FIFO_SIZE ); /* 等待數(shù)據(jù)發(fā)送 */
R8_UART3_THR = c; /* 發(fā)送數(shù)據(jù) */
#endif
return( c );
}
#endif
在usart.c中又重復(fù)添加了一個(gè)重定向函數(shù):
#pragma import(__use_no_semihosting)
struct __FILE {
int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
x = x;
}
int fputc(int ch, FILE *f)
{
while(R8_UART1_TFC != UART_FIFO_SIZE)
{
R8_UART1_THR = ch;
}
return ch;
}
有人會(huì)覺得兩次重定向了程序應(yīng)該會(huì)報(bào)錯(cuò),但不好意思程序真沒報(bào)錯(cuò)。將sys.c中下面這段代碼刪除后再測(cè)試串口收發(fā)數(shù)據(jù)正常。文章來源:http://www.zghlxwxcb.cn/news/detail-785404.html
#pragma import(__use_no_semihosting)
struct __FILE {
int handle;
};
目前就遇到過這么幾種常見(或不常見)的情況,后面會(huì)持續(xù)更新遇到的串口問題。文章來源地址http://www.zghlxwxcb.cn/news/detail-785404.html
到了這里,關(guān)于串口輸出亂碼問題的解決方法匯總(持續(xù)更新):的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!