新的一年开始了!上班后的第一周不太忙,处理了一个年前遗留的问题。
某工程师移植了一个FreeRTOS操作系统,运行了3个任务
MainTask: 间隔10秒(不是非常精确,大概9秒左右) LCD数值加一。
JcqTask: 间隔1秒翻转LED指示灯
ModbusTask:等待信号量(按键中断),刷新LCD的数据。
该工程师反馈跑了一夜后,第2个任务的LED灯不闪烁了,出现异常后,其它两个任务都还正常,就LED灯闪烁的任务不正常。
这个问题听起来挺诡异,我在年前也复现出来了,确实需要好几个小时才能复现出来。
起初怀疑是栈溢出,后来通过增加调试信息,发现并没有。
另外这个出问题的任务优先级还是最高,排除是优先级引起的问题。
后来看了看他移植的代码,他用到了FreeRTOS的低功耗tickless模式,它的核心逻辑是:让系统在没有任务需要处理时,直接进入深度睡眠,而不是靠“定时器中断”来维持心跳。
这样的好处是省电,避免了无意义的定时器中断,让CPU在需要时才运行工作。
在 FreeRTOS中,Tickless模式通常通过配置宏 configUSE_TICKLESS_IDLE来开启。
#define configUSE_TICKLESS_IDLE 2
当系统进入idle 状态下,调用portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime )这个函数。
这是由移植者根据MCU平台要实现的一个关键底层函数。它的核心功能是计算休眠时间、关闭系统节拍定时器、让CPU进入低功耗模式,并在正确的时间唤醒系统。唤醒系统要靠一个低功耗定时器来实现。
想来想去,大概率问题会出在这里。
后来咨询了一下VsCode Copilot AI,它快速帮我定位到了问题,问题出在下面这个代码
将其修改为如下代码就没问题了。
原因是当 LPTIM 是一个16位的计数器,如果不加 & 0xFFFFUL 掩码,当溢出时会导致计算出的差值为一个很大的负数(当做 uint32_t 时就是个巨大的正数),传给 vTaskStepTick()函数就会引起异常现象。
扫码加入嵌入式交流群:
219