第三篇:RTOS 的进化 —— Tickless 模式与上下文保持

王者杯·14天创作挑战营·第9期 10w+人浏览 242人参与

前两篇我们解决了“硬件怎么省电”的问题,现在压力给到了软件这边。

引入 RTOS(实时操作系统)是现代嵌入式开发的标配,但对于低功耗设计来说,传统的 RTOS 简直是一个**“恶魔”。因为它有一个必须存在的心跳机制 (Tick)**。

这就好比你只想好好睡一觉,但你的管家(RTOS)每隔 1 毫秒就跑过来摇醒你,问一句:“老板,有事要做吗?”如果没事,他又让你接着睡。哪怕你就在这种“睡1毫秒-醒10微秒”的折磨中度过,电量也会被这个管家败光。

这一篇,我们要控制这个“管家”,引入 Tickless 模式,并探讨在深度休眠下,软件如何保存记忆(Context)。

一、 传统 SysTick:低功耗的头号公敌

在 FreeRTOS 或 uCOS 中,为了维持任务调度和时间片轮转,通常会配置一个硬件定时器(SysTick),频率通常是 1000Hz(1ms 一次)。

  • 物理代价:

    • 频繁唤醒: 每秒 1000 次唤醒。

    • 无效能耗: 每次唤醒包含:Flash 唤醒延迟 + ISR 入栈出栈 + 调度器检查 + 重新入睡。这些动作消耗的能量,往往比 CPU 真正干活的能量还多。

    • 阻碍深睡: 因为间隔只有 1ms,MCU 根本来不及进入深度的 Stop 模式(因为进入和退出的时间成本太高),只能停留在浅睡眠(Sleep Mode),此时高频时钟还在跑,功耗高达 mA 级别。

二、 救世主:Tickless Idle 模式

核心思想: “除非有事(任务到期或中断触发),否则别叫我。”

1. 工作原理

当 OS 发现当前没有就绪任务(Idle Task 正在运行)时,它会查看下一个最近的任务还需要多久才执行。

  • 场景: 现在的任务列表全是 vTaskDelay,最早的一个也要 200ms 后才醒。

  • 操作:

    1. 关闭 SysTick 中断(管家闭嘴)。

    2. 计算出需要休眠的时间:200ms。

    3. 设置一个低功耗定时器 (LPTIM/RTC),定在 200ms 后响。

    4. CPU 进入深度休眠 (WFI -> Stop Mode)。

    5. 唤醒后: 读取低功耗定时器走了多久,补偿 (Compensate) OS 的系统 tick 计数器,让系统觉得仿佛那 200ms 只是瞬间过去的。


三、 实战:STM32G0 的 Tickless 痛点

在 STM32G0 上实现 FreeRTOS 的 Tickless 并不像开启一个宏 configUSE_TICKLESS_IDLE 那么简单,这里有两个深坑

1. SysTick 在 Stop 模式下会“死”

FreeRTOS 的默认 Tickless 实现是基于 SysTick 的。但是,STM32G0 进入 Stop 模式后,HCLK(高速时钟)会停止,SysTick 也就停了。

  • 后果: 你的 CPU 睡死过去了,没人叫醒它(除非有外部中断)。

  • 解决方案:重写 vPortSuppressTicksAndSleep

    必须抛弃 SysTick,改用 LPTIM (Low Power Timer) 或 RTC Wakeup Timer。

    • LPTIM 优势: 可以使用 LSE (32.768kHz) 或 LSI 作为时钟源,在 Stop 模式下继续计数。

    • STM32G0 特性: LPTIM 支持异步计数,哪怕没有 APB 总线时钟也能工作,唤醒后再同步。

2. 时钟补偿的精度 (Drift)

当你用 LPTIM 唤醒后,你需要告诉 OS:“刚才过去了 200 个 tick”。

但 LSE 的频率是 32.768kHz,而 SysTick 可能是 64MHz 分频来的。两者之间存在偏差。

  • 算法挑战: 必须处理好整数换算和余数。如果长期运行,微小的误差累积会导致系统时间变慢(Drift)。


四、 实战:RL78 的“傻瓜式” Tickless

相比于 STM32 需要自己写驱动去替换 SysTick,RL78 在硬件架构上对 Tickless 更友好。

1. Interval Timer (IT) 的天然优势

RL78 通常直接就用 Interval Timer (IT) 来做系统 Tick,而不是像 ARM 那样有一个内核级的 SysTick。

  • 无缝衔接: IT 挂在 fIL (15kHz) 上。无论 CPU 是全速跑 (Run) 还是深度睡 (Stop),IT 的时钟源从未改变

  • 实现:

    1. OS 发现要睡 200ms。

    2. 直接修改 IT 的比较寄存器,从 1ms (常规 tick) 改为 200ms 的计数值。

    3. 执行 STOP 指令。

    4. 唤醒后,把寄存器改回 1ms。

  • 优势: 不需要像 STM32 那样在两个定时器(SysTick 和 LPTIM)之间倒腾和换算,逻辑极简。


五、 深度进阶:上下文保持 (Context Restore)

如果为了极致省电,我们进入了 Standby (STM32) 模式,事情就变得复杂了。

1. 问题的本质:失忆

  • Stop 模式 (G0/RL78): RAM 数据保持不动,寄存器状态保持。唤醒 = 继续执行。这是做 Tickless 的首选。

  • Standby 模式 (G0): 只有 Backup Registers (几十个字节)部分 SRAM (若配置保持) 活着。CPU 寄存器全部丢失。唤醒 = 复位 (Reset)。

2. 如何在 Standby 下“伪装”成没关机?

如果你的产品要在电池下跑 10 年,Stop 模式的 3µA 可能还是太高,你需要 Standby (0.1µA)。这时候 RTOS 怎么跑?

  • 技术路线:

    1. 入睡前 (Pre-Sleep): 将 RTOS 的关键数据(如 xTickCount,下一个任务的指针,堆栈指针 SP)保存到 Backup SRAMBackup Registers 中。设置 RTC 闹钟。

    2. 唤醒复位 (Reset_Handler):

      • 芯片复位第一件事,检查 PWR->SR 寄存器的 SBF (Standby Flag) 标志位。

      • 如果是冷启动: 跑正常的 main() 初始化。

      • 如果是唤醒: 跳过 C 库初始化(不要把 RAM 清零!),直接从 Backup 区域恢复 SP 指针,跳转回调度器。

  • 难度: 极高。需要精通汇编启动文件 (.s)。通常不建议在跑复杂 RTOS 时使用 Standby,除非你的应用非常简单。


六、 总结:软件策略的选择

场景传统 SysTickTickless (Stop模式)Tickless (Standby模式)
平均电流mA 级µA 级nA 级
响应速度中 (有唤醒延迟)慢 (相当于重启)
RAM 数据保持保持丢失 (需手动备份)
STM32G0 实现默认需改写 vPortSuppress 用 LPTIM极难,需改启动文件
RL78/G23 实现默认简单,直接改 Interval Timer简单 (RAM 默认保持)

推荐策略:

  • 对于 STM32G0:使用 Tickless + Stop Mode 1。利用 LPTIM 唤醒。这是开发难度与功耗的最佳平衡点。

  • 对于 RL78/G23:使用 Tickless + Stop Mode。得益于 RL78 在 Stop 模式下全 RAM 保持且功耗极低,通常不需要考虑更复杂的“掉电恢复”逻辑。

/******************************************
 * 本文为作者《嵌入式开发基础与工程实践》系列文章之一。
 * 关注即可订阅后续内容更新,翻阅往期信息,采用异步推送机制,无需主动轮询。
 * 转发本文可视为一次网络广播,有助于更多节点接收该信息。
 *****************************************/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一路往蓝-Anbo

与其打赏不如转发

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值