笔者最近在拿到了个新的板子,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工具链,新版本又不稳定。也是陷入死循环了。。。
至于前面所说问题的深层次原因,笔者水平有限无法给出进一步解答,欢迎大家指正。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_63034630/article/details/148452069
|