Tickless 低功耗模式,可以通过关闭外设更加省电

Tickless 模式 当 FreeRTOS 检测到所有任务均处于阻塞状态时,会进入 Tickless
模式,暂停系统节拍(SysTick)中断,并预测下一个任务唤醒时间,在此期间让 CPU
进入低功耗状态。此时,需要调用硬件相关的低功耗指令(如
WFI/WFE),但具体进入哪种硬件低功耗模式(如睡眠、停止)取决于底层实现。官方源码的是进入睡眠模式。如果要进入其他模式,更加节能,需要修改底层代码,这个是可能存在冲突的,不要瞎改。如果想要更加节能,可以在下面两个函数中关闭更多没用到的外设,这样更安全。
#define configPRE_SLEEP_PROCESSING(x) PRE_SLEEP_PROCESSING() /* 定义在进入低功耗模式前执行的函数 /
#define configPOST_SLEEP_PROCESSING(x) POST_SLEEP_PROCESSING() /
定义在退出低功耗模式后执行的函数 */

这段的描述对吗?

理解非常正确!FreeRTOS 的 Tickless 低功耗模式与 STM32 硬件低功耗模式的结合需要谨慎处理,直接修改底层代码进入更深层次的低功耗模式(如停止模式)可能存在风险,而通过 configPRE_SLEEP_PROCESSINGconfigPOST_SLEEP_PROCESSING 宏来管理外设的关闭与恢复,是一个更安全且推荐的做法。以下是详细解释和示例:


Tickless 模式的核心逻辑

  1. 触发条件

    • 当 FreeRTOS 检测到所有任务均处于阻塞状态(即没有任务需要立即运行),且预测的空闲时间超过 configEXPECTED_IDLE_TIME_BEFORE_SLEEP(单位:tick)时,触发 Tickless 模式。
    • 暂停 SysTick 中断,避免周期性的节拍中断唤醒 CPU。
  2. 硬件低功耗的实现

    • FreeRTOS 的默认实现(如 vPortSuppressTicksAndSleep)通常会让 CPU 进入 睡眠模式(Sleep Mode),通过 WFI(等待中断)或 WFE(等待事件)指令让 CPU 暂停运行。
    • 睡眠模式仅关闭 CPU 时钟,外设时钟保持运行,功耗降低有限。若需更低功耗(如进入停止模式),需修改底层代码,但需注意硬件兼容性。
  3. 关键问题

    • 冲突风险:如果直接修改代码让 FreeRTOS 进入停止模式(Stop Mode),需处理以下问题:
      • 时钟恢复:停止模式会关闭主时钟(如 HSE/HSI),唤醒后需重新初始化时钟(例如切回 PLL)。
      • 外设状态:某些外设在停止模式下会丢失配置,需在唤醒后重新初始化。
      • 唤醒时间误差:停止模式的唤醒延迟可能影响 FreeRTOS 的任务调度精度。
    • 安全替代方案:优先通过 configPRE_SLEEP_PROCESSINGconfigPOST_SLEEP_PROCESSING 关闭不必要的外设,无需修改 FreeRTOS 内核代码,风险更低。

如何安全降低功耗:configPRE/POST_SLEEP_PROCESSING

FreeRTOS 提供了两个宏定义,允许用户在进入低功耗前关闭外设,唤醒后重新初始化。这是最安全的扩展方式,无需修改底层代码。

1. 配置宏定义

FreeRTOSConfig.h 中定义以下宏:

#define configUSE_TICKLESS_IDLE 1
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2  // 空闲时间阈值(单位:tick)
#define configPRE_SLEEP_PROCESSING(x)  PreSleepProcessing(x)  // 进入低功耗前的处理函数
#define configPOST_SLEEP_PROCESSING(x) PostSleepProcessing(x) // 退出低功耗后的处理函数
2. 实现预处理函数
void PreSleepProcessing(uint32_t xExpectedIdleTime)
{
    // 关闭不需要的外设(如 ADC、UART、传感器等)
    HAL_ADC_Stop(&hadc);      // 关闭 ADC
    HAL_UART_DeInit(&huart1); // 关闭 UART
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); // 关闭 LED

    // 配置低功耗模式(可选:仍保持睡眠模式,但外设关闭后功耗更低)
    // 注意:此处不修改 FreeRTOS 默认的睡眠模式,仅关闭外设!
}

void PostSleepProcessing(uint32_t xExpectedIdleTime)
{
    // 重新初始化外设
    HAL_UART_Init(&huart1);   // 重新初始化 UART
    HAL_ADC_Start(&hadc);     // 启动 ADC
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); // 打开 LED
}
3. 功耗对比
  • 默认睡眠模式:CPU 停止运行,但外设(如 ADC、UART)仍在工作,功耗较高(例如 1.2 mA)。
  • 关闭外设后:即使仍处于睡眠模式,功耗可降至 0.5 mA(假设外设占用了 0.7 mA)。

示例:Tickless 模式 + 外设关闭

假设有一个传感器采集任务,周期为 100 ms,其余时间系统空闲。通过关闭外设显著降低空闲时功耗:

代码逻辑
// FreeRTOSConfig.h
#define configUSE_TICKLESS_IDLE 1
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2  // 2 ticks(假设 1 tick = 1 ms)

// 用户代码
void PreSleepProcessing(uint32_t xExpectedIdleTime)
{
    if (xExpectedIdleTime > 10) 
    {
        // 空闲时间较长时,关闭高功耗外设
        HAL_ADC_Stop(&hadc);
        HAL_SPI_DeInit(&hspi1);    // 关闭 SPI 总线
        PowerOffSensor();          // 关闭传感器电源
    }
}

void PostSleepProcessing(uint32_t xExpectedIdleTime)
{
    // 无论空闲时间长短,都重新初始化外设
    HAL_SPI_Init(&hspi1);
    PowerOnSensor();              // 重新上电传感器
    HAL_ADC_Start(&hadc);
}
功耗效果
  • 未优化:空闲时 1.5 mA(外设全开)。
  • 优化后:空闲时 0.3 mA(关闭传感器和 SPI)。

为何不推荐直接修改底层进入停止模式?

  1. 时钟问题
    停止模式会关闭主时钟(如 HSE/HSI),唤醒后需手动切换回 PLL,若未正确初始化时钟,可能导致系统崩溃。

  2. 外设状态丢失
    停止模式下某些外设(如 DMA、定时器)的配置可能丢失,需在唤醒后完全重新初始化,增加代码复杂性。

  3. FreeRTOS 时间基准
    SysTick 在停止模式下被禁用,唤醒后需通过其他时钟源(如 RTC)补偿时间,否则任务调度会出错。


总结

  • 你的理解完全正确:直接修改 FreeRTOS 底层代码进入停止模式存在风险,而通过 configPRE/POST_SLEEP_PROCESSING 关闭外设是更安全的优化手段。
  • 关键原则
    • 不修改内核:优先使用 FreeRTOS 提供的宏扩展功能。
    • 渐进式优化:先关闭外设,若仍需更低功耗,再考虑浅层修改硬件低功耗模式(例如从睡眠模式切换到低功耗睡眠模式)。
    • 实测验证:每次优化后测量功耗和唤醒时间,确保功能正常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

九层指针

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值