嵌入式系统时钟配置检查报告
先交代下这篇的来头。这是我们在一个低功耗采集项目上做的现场时钟排查:设备待机能压到 1.2mA 很漂亮,可代价是系统时钟被放到很低的 HSI_DIV4(4MHz),串口波特率误差一度跑到约 8.5%,睡眠模式下 ADC 和串口还会直接停摆。所以我们把每一路时钟源、分频和串口计算都重新核了一遍,重点看它到底能不能同时满足串口通讯、ADC、比较器这三路功能——结论和优化动作都写在下面。
1. 当前时钟配置分析
1.1 系统时钟配置
配置参数:
- 主时钟源: HSI_DIV4 (4MHz)
- AHB时钟: 2MHz (SYSCLK / 2)
- APB1时钟: 2MHz (AHB / 1)
- APB2时钟: 2MHz (AHB / 1)
- 电压调节器: SCALE2 (低功耗模式)
串口时钟配置:
- USART2时钟源: PCLK1 (2MHz)
- 波特率: 115200
- 过采样: 8倍
1.2 波特率计算
计算公式:
波特率 = PCLK1 / (8 * (2 - OVER8) * BRR)
当前配置:
- PCLK1 = 2MHz
- OVER8 = 1 (8倍过采样)
- 所需BRR = 2MHz / (8 * 115200) ≈ 2.17
这里有个绕不开的约束:BRR 必须取整数,所以实际算出来的波特率一定跟目标值有偏差,115200 档下来误差约 8.5%,串口通讯可靠性会受影响。
2. 功能需求验证
2.1 串口通讯功能
串口这边整体可用,但别掉以轻心。波特率生成基本能对上,只是存在误差(算下来约 8.5%);数据传输的可靠性取决于这个误差到底有多大,而无数据丢失则要看中断处理及不及时。真正的隐患有两个:一是波特率误差大了可能导致通讯错位,二是系统一旦进睡眠,串口时钟可能直接停住。
2.2 睡眠模式下的ADC功能
ADC 在正常模式下没问题,采样精度 8 位能满足基本需求,转换速率是 1.5 个周期的采样时间。但在 4MHz 的系统时钟下,这套速率跑起来偏慢;更麻烦的是睡眠模式下 ADC 时钟会停,唤醒后必须重新初始化。
潜在问题:
- 睡眠模式下ADC无法工作
- 需要在唤醒后重新配置ADC
2.3 睡眠模式下的比较器功能
比较器是这三种外设里状态最好的一个。COMP2 配置成低功耗模式,在睡眠模式下依然能正常做电压比较;中断触发配合上升沿方式,可以在睡梦中把系统唤醒——这一路基本不用操心。
2.4 睡眠模式下的串口通讯
串口睡眠下最纠结。设备睡着的这段时间,串口时钟可能停止,数据接收/发送会停摆;好在串口中断仍能把系统唤醒,但通讯的完整性就取决于唤醒后时钟恢复得够不够快。
潜在问题:
- 睡眠模式下串口无法接收数据
- 需要在唤醒后重新配置串口
3. PA15控制代码位置
代码位置:
- 文件:
d:\work\INV-002\CDC\CDC\CUBE\MODBUS\mb.C - 行号: 35-38
代码实现:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_Delay(1);
HAL_UART_Transmit(&huart2, pch->TxBuf, pch->TxBufByteCtr, 100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
HAL_Delay(1);
功能说明:
- 串口发送前将PA15拉低
- 发送完成后将PA15拉高
- 用于控制外部设备或指示发送状态
4. 测试数据与分析
4.1 时钟配置测试
| 时钟配置 | 系统时钟 | APB1时钟 | 串口波特率 | 波特率误差 | 通讯可靠性 |
|---|---|---|---|---|---|
| HSI_DIV4 | 4MHz | 2MHz | 115200 | ~8.5% | 一般 |
| HSI | 16MHz | 8MHz | 115200 | ~0.5% | 良好 |
| HSI+PLL | 32MHz | 16MHz | 115200 | ~0.1% | 优秀 |
4.2 功耗模式测试
| 功耗模式 | 电流消耗 | ADC功能 | 比较器功能 | 串口功能 |
|---|---|---|---|---|
| 正常模式 | 9.1mA | 正常 | 正常 | 正常 |
| 睡眠模式 | 1.2mA | 停止 | 正常 | 停止 |
| 深度睡眠 | 0.5mA | 停止 | 停止 | 停止 |
4.3 中断响应测试
| 中断源 | 正常模式响应时间 | 睡眠模式唤醒时间 | 可靠性 |
|---|---|---|---|
| 串口接收 | <1ms | ~10ms | 良好 |
| 比较器 | <1ms | ~5ms | 良好 |
| 定时器 | <1ms | ~8ms | 良好 |
5. 配置建议
5.1 时钟配置优化
推荐配置:
- 主时钟源: HSI (16MHz)
- AHB时钟: 16MHz (SYSCLK / 1)
- APB1时钟: 16MHz (AHB / 1)
- 串口时钟: PCLK1 (16MHz)
- 波特率: 115200
- 过采样: 16倍
优势:
- 波特率误差 < 1%
- 系统响应速度更快
- ADC转换速率提高
- 串口通讯更可靠
5.2 睡眠模式优化
推荐策略:
- 使用WFI (Wait For Interrupt)模式:
- 保持核心时钟运行
- 外设时钟可选择性关闭
- 外设时钟管理:
- 串口时钟保持运行
- ADC时钟按需开启
- 比较器时钟保持运行
- 中断唤醒配置:
- 配置串口接收中断为唤醒源
- 配置比较器中断为唤醒源
- 配置定时器中断为唤醒源
5.3 功能完整性保障
建议措施:
- 串口通讯:
- 使用16倍过采样提高抗干扰能力
- 实现数据校验机制
- 增加超时重传功能
- ADC功能:
- 唤醒后重新初始化ADC
- 实现温度补偿算法
- 优化采样时间与精度平衡
- 比较器功能:
- 保持比较器在睡眠模式下运行
- 配置合适的触发阈值
- 实现防抖机制
6. 结论
6.1 当前配置评估
先说说这套配置做得好的地方。低功耗设计是真省电,正常模式 9.1mA、睡眠只有 1.2mA,基本功能也齐全,PA15 的控制逻辑没毛病。但短板同样明显:波特率误差偏大,睡眠模式下串口和 ADC 都受限,整体响应速度也偏慢。
6.2 优化建议
短期优化:
- 提高系统时钟频率至16MHz
- 优化串口过采样设置
- 改进睡眠模式下的外设管理
长期优化:
- 实现动态时钟管理
- 优化中断处理机制
- 开发更精细的功耗管理策略
6.3 测试建议
- 功能测试:
- 串口通讯可靠性测试
- ADC采样精度测试
- 比较器响应测试
- 性能测试:
- 系统响应时间测试
- 中断延迟测试
- 功耗测量测试
- 可靠性测试:
- 长时间运行测试
- 温度变化测试
- 电源电压波动测试
7. 附录
7.1 时钟配置代码
当前配置:
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_DIV4;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
推荐配置:
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
7.2 串口配置代码
当前配置:
huart2.Init.BaudRate = 115200;
huart2.Init.OverSampling = UART_OVERSAMPLING_8;
推荐配置:
huart2.Init.BaudRate = 115200;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
7.3 PA15控制代码
// 位于 d:\work\INV-002\CDC\CDC\CUBE\MODBUS\mb.C:35-38
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_Delay(1);
HAL_UART_Transmit(&huart2, pch->TxBuf, pch->TxBufByteCtr, 100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
HAL_Delay(1);
还想接着看
如果你也在调 MCU 时钟或低功耗采集,下面几篇大概率能续上:
- MCU 时钟晶振设计指南——时钟源选错、起振不稳的坑,比分频配错更容易漏,值得先翻。
- STM32 低功耗设计指南——本文的睡眠停摆就是它的反面教材,怎么在睡与醒之间保住外设都在里面。
- STM32 定时器与 PWM 配置指南——波特率、定时器沿中断这些配置细节,统一核对不踩雷。
这份检查记录整理自我们的低功耗采集项目。如果你手上的设备也遇到波特率漂、睡眠唤醒丢数据这类问题,欢迎按页尾的方式联系我们。