043、FreeRTOS任务调度与优先级设计
一次让我熬夜到凌晨三点的优先级反转
去年做四轴飞控的传感器融合模块,IMU数据采集任务优先级设得比姿态解算高,结果飞行器在急加速时姿态解算居然卡顿了整整200ms——飞机差点翻。当时我盯着逻辑分析仪上的波形,发现高优先级的IMU任务占着CPU不放,低优先级的姿态解算拿不到锁,中间还有个共享I2C总线的磁力计任务在捣乱。这就是典型的优先级反转,FreeRTOS默认的抢占式调度在这种场景下反而成了坑。
任务调度的底层逻辑:别被“抢占”两个字骗了
FreeRTOS的调度器本质是个基于优先级的抢占式时间片轮转系统。每个任务都有个优先级数值,0最低,configMAX_PRIORITIES-1最高。调度器每次Tick中断(通常是1ms)都会检查就绪列表里最高优先级的任务,如果当前运行的任务不是最高优先级的,立刻触发PendSV中断进行上下文切换。
这里有个容易忽略的细节:同优先级任务之间才是时间片轮转。不同优先级之间是纯粹的抢占,高优先级任务只要就绪,低优先级任务立刻被踢出CPU。飞控里如果传感器采集任务优先级高于控制计算任务,采集任务里但凡有个while循环等待数据,控制任务就永远拿不到CPU。
我习惯在FreeRTOSConfig.h里把configUSE_TIME_SLICING设为1,但时间片长度别用默认值。飞控场景下,同优先级任务的时间片建议设成2-5个Tick,太短会导致频繁切换浪费在上下文保存恢复上,太长又会让低优先级任务饿死。