一例離奇的STM32Cube串口數(shù)據(jù)亂碼解決過程分析

使用的開發(fā)環(huán)境是STM32CubeMX+VSCode-PlatformIO误债,用CubeMX初始化外設(shè)诗茎,然后導(dǎo)入到PlatformIO中開發(fā)實(shí)際應(yīng)用霜第。


引腳配置圖

中斷配置圖

時(shí)鐘配置圖

結(jié)果出師不利渐溶,前后折騰將近一整天雹锣,始終輸出亂碼赡麦。


亂碼

嘗試的方法如下:

  1. 降低波特率
    低波特率一般不容易出錯(cuò)堪遂,容錯(cuò)率較高秩冈。


    image.png

    依舊是亂碼蚂子。

  2. 降低系統(tǒng)頻率
    過高的CPU主頻會(huì)降低系統(tǒng)穩(wěn)定性沃测,是不是CPU跑飛了?加上LED指示燈代碼食茎,顯示依然是正常的蒂破。繼續(xù)修改系統(tǒng)頻率(原本設(shè)置為滿頻率168Mhz,后降為84Mhz)


    亂碼
  3. 嘗試避免使用中斷方式發(fā)送
    用HAL_UART_Transmit()函數(shù)發(fā)送别渔,依然亂碼附迷。

  4. 嘗試換用外接TTL轉(zhuǎn)換器
    去掉PA9、PA10到USB的跳線帽哎媚,連接外部USB-TTL喇伯,


    image.png

結(jié)果:


亂碼
  1. 懷疑是不是片內(nèi)UART部分硬件燒壞了?
    用示波器檢測PA9腳上波形拨与,測到的結(jié)果:
    image.png

    似乎看不出什么所以然來稻据,后繼續(xù)查閱前人提問資料,翻到這樣一條:
    串口通信买喧,顯示亂碼捻悯!-CSDN論壇
image.png

亂碼就一種可能,波特率不對(duì)淤毛。如果有示波器可以用示波器看一下今缚。收一個(gè)最小的脈沖,他的脈寬就是實(shí)際的波特率的倒數(shù)低淡。

讓我們記住這句話姓言。


image.png

放大看一個(gè)最小波形寬度,得到的結(jié)果是 27.1us. 按照wackestar老哥的計(jì)算方法查牌,對(duì)應(yīng)的波特率就是1/27.1*1e6=36900事期,似乎并不怎么對(duì)勁,我不是設(shè)置的115200??纸颜?

寫進(jìn)串口監(jiān)視器配置里看看輸出:


image.png
image.png

問題解決。

...

原因呢绎橘?
算一下115200和36900之間的倍率胁孙,剛好3.12倍唠倦。。涮较。好像沒啥規(guī)律呀稠鼻。


image.png

測試一下附近的標(biāo)準(zhǔn)波特率,38400狂票,同樣可以準(zhǔn)確解碼候齿。而115200和38400之間剛好差了3倍。然而還是沒搞懂哪里用錯(cuò)了闺属。翻看一下使用手冊:


image.png

也并沒有弄明白為什么慌盯。
image.png

查看HAL認(rèn)為的系統(tǒng)頻率,發(fā)現(xiàn)是這個(gè)數(shù):262.50Mhz掂器。而設(shè)定值為84Mhz亚皂,將這個(gè)數(shù)字乘3,為252Mhz国瓮。這個(gè)值是比較接近輸出值的灭必。會(huì)不會(huì)是因?yàn)橥獠繒r(shí)鐘設(shè)置沒有生效呢?
image.png

查看stm32f4xx_hal_conf.h文件乃摹,發(fā)現(xiàn)是有設(shè)置時(shí)鐘頻率的禁漓。但是沒有導(dǎo)入?


image.png

image.png

這是HAL庫的默認(rèn)配置文件孵睬,按道理來說應(yīng)該是采用項(xiàng)目中的配置播歼,但不清楚什么原因始終導(dǎo)入的是庫文件的配置。
image.png

修改為8Mhz
image.png

修改后的輸出:
image.png

這次又出現(xiàn)了亂碼肪康,不過不用慌荚恶,因?yàn)檫@時(shí)的波特率已經(jīng)發(fā)生變化,修改為115200:
image.png

完美解決磷支!

分析原因

由于stm32f4xx_hal_conf.h文件錯(cuò)誤導(dǎo)入谒撼,導(dǎo)致STM32Cube庫錯(cuò)誤的計(jì)算了系統(tǒng)時(shí)鐘震蕩頻率,進(jìn)而算出了錯(cuò)誤的串口時(shí)鐘雾狈,從而在PC端上使用預(yù)定的波特率無法正常接收消息廓潜,表現(xiàn)為數(shù)據(jù)亂碼。
修改系統(tǒng)庫內(nèi)配置文件為正確的晶振頻率后善榛,輸出一切正常辩蛋。

使用printf函數(shù)進(jìn)行串口輸出?

順便提一下(也是本次探索的副產(chǎn)品)如何使用print函數(shù)來實(shí)現(xiàn)串口輸出吧移盆,畢竟printf函數(shù)在調(diào)試當(dāng)中非常方便悼院。
由于PlatformIO IDE使用的編譯器比較特別,是用的arm-eabi-none標(biāo)準(zhǔn)嵌入式設(shè)備GCC編譯器咒循,所以和STM32CubeIDE以及Keil MDK編譯器特性都不相同据途,所以無論網(wǎng)上多見的fputc()函數(shù)绞愚,或者__io_putchar()乃至_write()函數(shù)都無法實(shí)現(xiàn)print函數(shù)的串口重定向。順便放出來供參考(親測對(duì)PlatformIO附帶的編譯器無效):

#include <stdio.h>
__IO ITStatus USART1Ready = RESET;

/* USER CODE BEGIN PV */
#ifdef __GNUC__
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

PUTCHAR_PROTOTYPE
{
    HAL_UART_Transmit_IT(&huart1 , (uint8_t *)&ch, 1);
    while (USART1Ready != SET)
  {
  }
    USART1Ready = RESET;
    return ch;
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
  if(huart->Instance==USART1)
  {
    USART1Ready = SET;
    // HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);
  }
}
/* USER CODE END PV */
int _write(int fd, char *ptr, int len)  
{
  UNUSED(fd);
  HAL_UART_Transmit_IT(&huart1 , (uint8_t *)ptr, len);
  while (USART1Ready != SET) {
  }
  USART1Ready = RESET;
  HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);
  return 0;
}

這里定義USART1Ready 變量起到防止同步讀寫出現(xiàn)亂碼的作用颖医。
而且printf代碼是包含在lib庫中的位衩,所以無法看到其具體實(shí)現(xiàn),因此也很難追溯其調(diào)用中間步驟熔萧。
所以才有了這里的曲線救國方案:

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
void print(const char *fmt, ...);

void vprint(const char *fmt, va_list argp)
{
    char string[200];
    if(0 < vsprintf(string, fmt, argp)) // build string
    {
        HAL_UART_Transmit(&huart1, (uint8_t*)string, strlen(string), 0xffffff); // send message via UART
    }
}

void print(const char *fmt, ...) // custom printf() function
{
    va_list argp;
    va_start(argp, fmt);
    vprint(fmt, argp);
    va_end(argp);
}

測試代碼:

while (1)
  {
    /* USER CODE END WHILE */
    HAL_Delay(500);
    HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_9);
    HAL_Delay(250);
    // uint8_t str[] = {32, 33, 34, 35, 36};
    uint8_t str[] = "Hello, 嚶嚶嚶糖驴。\n";
    // HAL_UART_Transmit_IT(&huart1, str, sizeof(str));
    print("Hello\n");
    print((char*)str);
    
    print("double: %lf\n", a=a+0.01);
    print("SysClock Freq: %.2f Mhz\n", (float)HAL_RCC_GetSysClockFreq()/(float)1e6);
    print("HCLK1 Freq: %.2f Mhz\n", (float)HAL_RCC_GetHCLKFreq()/(float)1e6);
    print("PCLK1 Freq: %.2f Mhz\n", (float)HAL_RCC_GetPCLK1Freq()/(float)1e6);
    print("PCLK2 Freq: %.2f Mhz\n", (float)HAL_RCC_GetPCLK2Freq()/(float)1e6);

    uint32_t pllm, pllvco, pllp, sysclockfreq;
    pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
    if(__HAL_RCC_GET_PLL_OSCSOURCE() != RCC_PLLSOURCE_HSI)
    {
      /* HSE used as PLL clock source */
      pllvco = (uint32_t) ((((uint64_t) HSE_VALUE * ((uint64_t) ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)))) / (uint64_t)pllm);
    }
    else
    {
      /* HSI used as PLL clock source */
      pllvco = (uint32_t) ((((uint64_t) HSI_VALUE * ((uint64_t) ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)))) / (uint64_t)pllm);
    }
    pllp = ((((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >> RCC_PLLCFGR_PLLP_Pos) + 1U) *2U);

    sysclockfreq = pllvco/pllp;
    print("HSE_VALUE, pllm, pllvco, pllp, sysclockfreq = %u \t %u \t %u \t %u \t %u\n", HSI_VALUE, pllm, pllvco, pllp, sysclockfreq);
    print("sysclockfreq: %.2f Mhz\n", (float)sysclockfreq/(float)1e6);

    /* USER CODE BEGIN 3 */
  }

如何print輸出浮點(diǎn)數(shù)float以及double?

這里又有一個(gè)坑...因?yàn)檎G闆r你的輸出格式應(yīng)該長這樣:


image.png

也就是無法對(duì)浮點(diǎn)數(shù)進(jìn)行格式輸出佛致,這是由于printf庫內(nèi)容很大贮缕,所以嵌入式版本默認(rèn)進(jìn)行了縮減,所以直接使用是無法進(jìn)行浮點(diǎn)數(shù)格式化的晌杰。
解決辦法是引入一個(gè)浮點(diǎn)數(shù)printf庫跷睦,而這個(gè)庫在編譯器中是自帶的,只是需要選擇性加載進(jìn)來:

build_flags = 
    -Wl,-u_printf_float

platformio.ini文件中添加上面的代碼肋演,就會(huì)看到前面的輸出結(jié)果了抑诸。實(shí)測可以很好的進(jìn)行浮點(diǎn)數(shù)格式處理,包括截?cái)嗵幚淼狻⒎?hào)處理蜕乡、補(bǔ)全處理等等,同時(shí)也支持雙精度小數(shù)的輸出梗夸,到目前為止基本使用起來得心應(yīng)手了层玲。

小彩蛋

測試得到的36900的波特率與設(shè)置的波特率115200剛好差3.12倍。
而測試得到的系統(tǒng)頻率262.50Mhz與設(shè)定的頻率84.00Mhz相差也是3.125倍反症。
晶振設(shè)置頻率25000000 Hz與實(shí)際的8000000 Hz相差也是 25/8=3.125倍辛块。

如果對(duì)你有所幫助,歡迎點(diǎn)贊铅碍。?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末润绵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胞谈,更是在濱河造成了極大的恐慌尘盼,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烦绳,死亡現(xiàn)場離奇詭異卿捎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)径密,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門午阵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人享扔,你說我怎么就攤上這事趟庄±ㄏ福” “怎么了伪很?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵戚啥,是天一觀的道長。 經(jīng)常有香客問我锉试,道長猫十,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任呆盖,我火速辦了婚禮拖云,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘应又。我一直安慰自己宙项,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布株扛。 她就那樣靜靜地躺著尤筐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洞就。 梳的紋絲不亂的頭發(fā)上盆繁,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音旬蟋,去河邊找鬼油昂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛倾贰,可吹牛的內(nèi)容都是我干的冕碟。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼匆浙,長吁一口氣:“原來是場噩夢啊……” “哼安寺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吞彤,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤我衬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后饰恕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挠羔,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年埋嵌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了破加。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡雹嗦,死狀恐怖范舀,靈堂內(nèi)的尸體忽然破棺而出合是,到底是詐尸還是另有隱情,我是刑警寧澤锭环,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布聪全,位于F島的核電站,受9級(jí)特大地震影響辅辩,放射性物質(zhì)發(fā)生泄漏难礼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一玫锋、第九天 我趴在偏房一處隱蔽的房頂上張望蛾茉。 院中可真熱鬧,春花似錦撩鹿、人聲如沸谦炬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽键思。三九已至,卻和暖如春散劫,著一層夾襖步出監(jiān)牢的瞬間稚机,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工获搏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赖条,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓常熙,卻偏偏與公主長得像纬乍,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子裸卫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 今天感恩節(jié)哎仿贬,感謝一直在我身邊的親朋好友。感恩相遇墓贿!感恩不離不棄茧泪。 中午開了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,551評(píng)論 0 11
  • 彩排完聋袋,天已黑
    劉凱書法閱讀 4,187評(píng)論 1 3
  • 表情是什么队伟,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息幽勒。高興了當(dāng)然就笑了嗜侮,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 124,164評(píng)論 2 7