OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 1345|回复: 1

STM32F407移植FreeRTOS之后delay_us()延时不准的问题

[复制链接]

0

主题

6

帖子

0

精华

新手上路

积分
37
金钱
37
注册时间
2019-9-25
在线时间
8 小时
发表于 2026-6-16 22:07:59 | 显示全部楼层 |阅读模式
1金钱
本帖最后由 azhaoxin 于 2026-6-16 22:13 编辑

STM32F407探索者开发板,我是用STM32CubeMX生成的FreeRTOS项目,然后加入正点原子的sys/delay/usart三给文件,各种修改都按照《FreeRTOS开发指南_V1.3》上面的要求改的。
跑了一个LED的程序,延时那里用osDelay就一切正常,但是用delay_us的延时时间明显比实际长2到3倍左右。

使用 volatile uint32_t sysClk= HAL_RCC_GetSysClockFreq();查看实际频率也是168000000。
在delay_us中打断点,定时500*1000,返回的时候 tcnt 值一般都是比500*1000*168多个十几。

不知哪里的问题?请大佬们帮忙分析一下,谢谢!
//Led任务
void LedTask(void *argument)
{
  LED0(1);
  LED1(1);
  while (1)
  {
    LED1(0);
    delay_us(500*1000);
    LED1(1);
    osDelay(500);
  }
}//延时函数
void delay_us(uint32_t nus)
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = SysTick->LOAD;        /* LOAD的值 */
    ticks = nus * g_fac_us;                 /* 需要的节拍数 */

    told = SysTick->VAL;                    /* 刚进入时的计数器值 */
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
            {
                tcnt += told - tnow;        /* 这里注意一下SYSTICK是一个递减的计数器就可以了 */
            }
            else
            {
                tcnt += reload - tnow + told;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                break;                      /* 时间超过/等于要延迟的时间,则退出 */
            }
        }
    }
}





回复

使用道具 举报

0

主题

107

帖子

0

精华

高级会员

Rank: 4

积分
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 的资源冲突。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-8-5 00:15

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表