> 技术文档 > STM32 CubeMx 串口输出乱码问题总结_stm32串口助手乱码

STM32 CubeMx 串口输出乱码问题总结_stm32串口助手乱码

        笔者最近在拿到了个新的板子,MCU是意法半导体的STM32H723VGT6,板载晶振是24MHz。个人习惯拿到新板子先打印个hello world,于是CubeMx启动。

        配置串口1异步模式,波特率115200,其余保持默认。

        Debug选SW,RCC用外部高速时钟HSE,接着配置时钟树如下。

        为了方便进行串口重定向,使用ARM-MDK工具链,接着是Keil 磺ision5启动。首先在点击魔术棒,勾选使用微库以便对printf重定向至串口。

        接着打开 usart.c,在最后面添加将 fputc 重定向至串口。并在 usart.h 中将 stdio.h 头文件包含进来(对应位置看注释)。

/* uart.h *//* USER CODE BEGIN Includes */#include \"stdio.h\"/* USER CODE END Includes *//* uart.c *//* USER CODE BEGIN 1 */int fputc(int ch, FILE *f){ HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff); return ch;}/* USER CODE END 1 */

        接着便可以直接使用 printf 打印并用串口助手查看了。

 /* USER CODE BEGIN WHILE */ while (1) { printf(\"hello world\\r\\n\"); HAL_Delay(500); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */

        事与愿违,打开串口助手查看结果如下。

        如图所示收到了乱码,那么显然波特率应该是哪里有点问题,但是我们CubeMx配置的明明是115200,从 usart.c 中也可以看到,波特率确确实实是 115200。

        究竟是哪里有问题呢?我们需要先想明白问题是什么,接着再找是什么的问题。为了方便排查,我们打印十六进制数据看看。我们修改代码输出个0x01和0x11。

 /* USER CODE BEGIN WHILE */ uint8_t data[] = {0x01}; while (1) {// printf(\"hello world\\r\\n\");HAL_UART_Transmit(&huart1, data, 1, HAL_MAX_DELAY);HAL_Delay(500); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */

        串口助手输出结果如下,01输出结果为80,由于上面已经推测出波特率有问题,我们这个数据又比较特殊。根据串口低位先行,盯帧一下可以做出猜想,是不是实际波特率减半了?

        于是开个逻辑分析仪看看数字波形,如下所示,可以看到开始位后,确确实实是发了个高电平1,符合低位先行。可以看到高电平持续时间大概是17.35us,换算比特率就是57636,接近57600,好像符合我们猜想?

        那么,我们直接将波特率设置为57600,可以看到逻辑分析仪的数据也对上了。

        接着使用 printf 输出字符串继续验证。将串口助手的波特率改为 57600,终于正确打印出了我们预期的字符串。

        于是我们弄清楚了问题:实际波特率和设置的波特率差了一半。为什么呢?通过查找手册,可以找到串口1挂载在 APB2 总线上。

        查看CubeMX时钟树也可以看见,USART1、6、9、10这里都用的是 PCLK2,根据配置是120MHz。

         我们检查一下串口的时钟源对不对。

 /* USER CODE BEGIN WHILE */ printf(\"hello world\\r\\n\"); while (1) { uint32_t uart_clk_source = __HAL_RCC_GET_USART1_SOURCE(); switch(uart_clk_source){case RCC_USART1CLKSOURCE_D2PCLK2:printf(\"USART1 clock source: PCLK2\\n\");break;case RCC_USART1CLKSOURCE_PLL2:printf(\"USART1 clock source: PLL2\\n\");break;case RCC_USART1CLKSOURCE_PLL3:printf(\"USART1 clock source: PLL3\\n\");break;case RCC_USART1CLKSOURCE_HSI:printf(\"USART1 clock source: HSI\\n\");break;case RCC_USART1CLKSOURCE_CSI:printf(\"USART1 clock source: CSI\\n\");break;case RCC_USART1CLKSOURCE_LSE:printf(\"USART1 clock source: LSE\\n\");break;default:printf(\"USART1 clock source: unknown\\n\");} HAL_Delay(2000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */}

        输出结果如下,可以看到确实是PCLK2。

        接着我们检查一下各个时钟的频率是否正确。

 /* USER CODE BEGIN WHILE */ printf(\"hello world\\r\\n\"); while (1) {uint32_t sysclk = HAL_RCC_GetSysClockFreq();uint32_t hclk = HAL_RCC_GetHCLKFreq();uint32_t pclk2 = HAL_RCC_GetPCLK2Freq();printf(\"SYSCLK: %d MHz\\n\", sysclk/1000000);printf(\"HCLK: %d MHz\\n\", hclk/1000000);printf(\"PCLK2: %d MHz\\n\", pclk2/1000000); HAL_Delay(2000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }

      ​​​​

        可以看到,SYSCLK和HCLK均一致,但是PCLK2明明我们配置的是120MHz,而获取到的却是240MHz。为什么呢?对于波特率 Baud = f_PCLK2 / 16 x div,div的值分别以整数和小数部分存储在BRR的低十六位中,通过Debug设断点查看BRR的值。

        可以看到BRR=0x823,其中0x82是整数部分,0x3是小数部分,所以 div = 130.1875。按照获取到的时钟频率 240 MHz计算得波特率为115200,但是实际的波特率却是115200/2=57600;按照设置的时钟频率 120 MHz计算可得波特率为57600,但是实际获取的时钟频率却是240MHz。

        加之笔者的CubeMx是刚更新的6.13.0(本来更新的是最新版的6.14.0因为不能联网而回退到1.13),至此开始怀疑是CubeMx生成的代码有问题(试过FW使用1.11.2和1.12.0均有问题),可能其中有几步并没有正确配置到。

        于是换回低版本的6.10.0,FW使用1.11.1,按照最开始的流程,运行结果如下,一切顺利。

        此外,还试了一下用6.13的cubemx和1.11.1的FW包生成代码,问题同前面所述。
        综上,新版本的CubeMx(6.13)可能不太稳定,想要稳定运行的话可以用稍微旧一些版本的CubeMx,但是旧版本的CubeMx又不支持CMake工具链,新版本又不稳定。也是陷入死循环了。。。

        至于前面所说问题的深层次原因,笔者水平有限无法给出进一步解答,欢迎大家指正。