高级会员

- 积分
- 545
- 金钱
- 545
- 注册时间
- 2026-1-29
- 在线时间
- 37 小时
|
发表于 2026-7-28 09:19:15
|
显示全部楼层
根据你的描述和代码分析,问题可能出现在以下几个方面:
一、关键原因分析
1. g_fac_us 计算错误
delay_us 的核心公式是 ticks = nus * g_fac_us,其中 g_fac_us 应表示 每微秒对应的 SysTick 计数次数。
如果 g_fac_us 计算错误(例如误用了 HCLK/8 或其他分频后的时钟),会导致实际延时时间偏离预期。
验证方法:
// 确保 g_fac_us 的正确性(以 168MHz 为例):
g_fac_us = SystemCoreClock / 1000000; // 即 168 (SystemCoreClock=168000000)
请检查你的 g_fac_us 是否按上述方式计算,且 SystemCoreClock 确实为 168MHz。
2. SysTick 被 FreeRTOS 占用导致的冲突
FreeRTOS 默认使用 SysTick 作为系统节拍源(tick interrupt),而你的 delay_us 也依赖 SysTick 计数器。
潜在问题:
FreeRTOS 的 tick 中断会定期重置 SysTick 计数器(通过 vPortStepProcessorCount),导致 SysTick->VAL 的值被突然修改。
当 delay_us 在循环中读取 SysTick->VAL 时,若恰好遇到 FreeRTOS 的 tick 中断触发,会导致 tnow 和 told 的差值计算出现跳跃,从而 多累加了无效的计数值。
现象解释:
你观察到 tcnt 比预期值多十几个,正是因为 FreeRTOS 的 tick 中断干扰了 SysTick 计数器的连续性。
二、解决方案
方案 1:改用硬件定时器实现高精度延时
推荐做法:使用独立的硬件定时器(如 TIM2~TIM7)实现 delay_us,避免与 SysTick 冲突。
示例代码:
// 初始化定时器(假设使用 TIM6)
void Timer_Init(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM_TimeBaseStruct.TIM_Prescaler = 168 - 1; // 1MHz (1μs 计数一次)
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStruct.TIM_Period = 0xFFFF; // 最大自动重载值
TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStruct);
TIM_ARRPreloadConfig(TIM6, ENABLE);
TIM_Cmd(TIM6, ENABLE);
}
// 延时函数
void delay_us(uint32_t nus) {
TIM_SetAutoreload(TIM6, nus);
TIM_SetCounter(TIM6, 0);
while (TIM_GetCounter(TIM6) < nus);
}
方案 2:调整 FreeRTOS 配置以兼容 SysTick
降低 FreeRTOS 的 Tick 频率:
将 configTICK_RATE_HZ 从默认的 1000Hz 改为较低值(如 100Hz),减少对 SysTick 的中断频率。
#define configTICK_RATE_HZ 100 // 在 FreeRTOSConfig.h 中修改
注意:此方法会牺牲任务调度的响应速度,仅适用于对实时性要求不高的场景。
三、补充排查建议
确认时钟树配置:
使用 HAL_RCC_GetSysClockFreq() 获取的 sysClk 必须严格等于 168MHz。
检查 PLL 配置、AHB/APB 预分频器是否正确(特别是 APB1 和 APB2 的时钟是否超限)。
关闭编译器优化:
在 delay_us 函数前添加 __attribute__((optimize("O0"))),防止编译器优化掉循环变量或寄存器读写操作。
使用示波器实测:
通过 GPIO 翻转配合示波器测量实际延时时间,排除软件仿真误差。
四、总结
问题根源 解决方法 适用场景
SysTick 被 FreeRTOS 干扰 改用独立硬件定时器 高精度延时需求
g_fac_us 计算错误 修正 g_fac_us = SystemCoreClock / 1e6 通用场景
编译器优化导致误差 强制关闭优化 极端调试场景
建议优先采用 方案 1(独立硬件定时器),既能保证精度,又能彻底规避与 FreeRTOS 的资源冲突。 |
|