10-软件定时器与Tickless低功耗.md 7.1 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第17~18章 + 配套代码19/20" author: "尚硅谷研究院" date: 2026-07-12 url: ""

created: 2026-07-12

软件定时器与 Tickless 低功耗


STM32 低功耗模式与 Tickless 模式示意图

软件定时器

用生活理解:软件定时器就像你手机上的"闹钟"——

  • 一次性定时器:设一个闹钟,响了就没了(比如"30分钟后关火")
  • 周期性定时器:设一个重复闹钟,每天这个点都响(比如"每天早上7点起床")

精度限制:软件定时器的精度由 tick 周期决定(如 1ms 节拍 → 最大精度 1ms),无法达到微秒级——这是与硬件定时器的本质区别。

由专用任务管理:软件定时器由调度器启动时自动创建的定时器服务任务prvTimerTask)负责管理。所有定时器命令(启动/停止/复位)通过命令队列发送给该任务。回调函数在该任务上下文中执行,因此不能调用任何阻塞 API,否则会卡住整个定时器服务。

硬件 vs 软件定时器

特性 硬件定时器 软件定时器
本质 芯片内部真实计数器 用RTOS tick模拟的"假定时器"
精度 微秒级 受任务调度影响(毫秒级)
数量 有限(STM32F1有几个TIM) 无限(只要内存够)
回调 在中断中执行 在"定时器服务任务"中执行

示例:软件定时器(项目19)

情景

  • 创建两个定时器:timer1(一次性,500ms)和 timer2(周期性,1000ms)
  • 按 KEY1 → 启动两个定时器
  • 按 KEY2 → 停止两个定时器

核心代码

#include "timers.h"

TimerHandle_t timer1_handle;  // 一次性
TimerHandle_t timer2_handle;  // 周期性

void freertos_start(void)
{
    // 创建一次性定时器:500ms后执行一次
    timer1_handle = xTimerCreate(
        "timer1",          // 名字(调试用)
        500,               // 周期 = 500个tick
        pdFALSE,           // pdFALSE = 一次性,pdTRUE = 周期性
        (void *)1,         // 定时器ID(可用于区分多个定时器)
        timer1_callback);  // 超时回调函数

    // 创建周期性定时器:每1000ms执行一次
    timer2_handle = xTimerCreate(
        "timer2", 1000, pdTRUE, (void *)2, timer2_callback);

    xTaskCreate(/* task1 */);
    vTaskStartScheduler();
}

/* ========== 控制任务 ========== */
void task1(void *pvParameters)
{
    uint8_t key = 0;
    while (1)
    {
        key = Key_Detect();
        if (key == KEY1_PRESS)
        {
            xTimerStart(timer1_handle, portMAX_DELAY);  // 启动一次性
            xTimerStart(timer2_handle, portMAX_DELAY);  // 启动周期性
        }
        else if (key == KEY2_PRESS)
        {
            xTimerStop(timer1_handle, portMAX_DELAY);   // 停止
            xTimerStop(timer2_handle, portMAX_DELAY);
        }
        vTaskDelay(500);
    }
}

/* ========== 回调函数 ========== */
void timer1_callback(TimerHandle_t xTimer)
{
    static uint16_t count = 0;
    printf("一次性定时器触发了第%d次\r\n", ++count);
    // 一次性定时器:只会打印一次
}

void timer2_callback(TimerHandle_t xTimer)
{
    static uint16_t count = 0;
    printf("周期性定时器触发了第%d次\r\n", ++count);
    // 周期性定时器:一直打印下去
}

定时器 API 速查

函数 作用 类比
xTimerCreate() 创建定时器 买一个闹钟
xTimerStart() 启动 拧上发条开始计时
xTimerStop() 停止 按掉闹钟
xTimerReset() 复位(重新开始计时) 把闹钟重设为当前时间+N
xTimerChangePeriod() 修改周期 把闹钟改成不同的时间响

⚠️ 定时器回调的约束

定时器回调函数在定时器服务任务中执行,不能调用阻塞 API

// ❌ 错误:在回调中调用阻塞函数
void timer_callback(TimerHandle_t xTimer)
{
    vTaskDelay(100);         // 禁止!会阻塞整个定时器服务任务
    xQueueReceive(queue, &val, portMAX_DELAY);  // 禁止!
    xSemaphoreTake(sem, portMAX_DELAY);         // 禁止!
}

// ✅ 正确做法:在回调里只做快速处理
void timer_callback(TimerHandle_t xTimer)
{
    printf("超时了!\r\n");        // 可以:printf通常很快
    LED_Toggle(LED1_Pin);         // 可以:GPIO操作极快
    xQueueSend(queue, &val, 0);   // 可以:超时=0不阻塞
}

Tickless 低功耗模式

用生活理解:普通模式下,系统像一个人"每秒醒来看一次有没有事做"。Tickless 模式下,没事做就直接睡大觉,直到有事了才被叫醒。省电!

工作原理

普通模式:  醒→看→没事→睡→醒→看→没事→睡→醒→看→有事→干→醒→看...
                ↑                ↑                ↑
             1秒醒一次       1秒醒一次        1秒醒一次(浪费电)

Tickless:   醒→看→没事→睡大觉→有事→醒了→干活
                ↑                          ↑
             睡很久                     有人叫醒(省电!)

核心机制:当所有任务都阻塞时,系统可以预测空闲任务的持续时长(从最近的阻塞任务超时时间计算),然后在此时间段内禁用 SysTick 中断,醒来时一次性补偿漏掉的 tick 计数。

睡觉被手机吵醒类比:"好不容易有半个小时要睡一会儿,结果你的手机一分钟响一次"——这就是不禁用 SysTick 的感受。Tickless 模式相当于睡前把手机调成静音,睡醒了再看所有未接来电。

最小空闲时长配置:可设置进入低功耗所需的最短空闲时间——如果空闲时间太短,"进入退出的开销得不偿失",就不进低功耗模式。

配置方法

// FreeRTOSConfig.h
#define configUSE_TICKLESS_IDLE    1   // 开启低功耗

// 定义"睡前处理"和"醒后处理"
#define configPRE_SLEEP_PROCESSING(x)     PRE_SLEEP_PROCESSING()
#define configPOST_SLEEP_PROCESSING(x)    POST_SLEEP_PROCESSING()

// 睡前:关掉所有外设时钟以省电
void PRE_SLEEP_PROCESSING(void)
{
    __HAL_RCC_GPIOA_CLK_DISABLE();
    __HAL_RCC_GPIOB_CLK_DISABLE();
    __HAL_RCC_GPIOC_CLK_DISABLE();
    // ... 关掉当前不需要的外设
}

// 醒后:重新打开外设时钟
void POST_SLEEP_PROCESSING(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    // ... 重新打开外设
}

STM32 低功耗模式对比

模式 功耗 唤醒方式 醒来速度 Tickless用哪个?
睡眠(Sleep) 任何中断 最快 ✅ 默认用这个
停机(Stop) EXTI/RTC/USB 中等 ⚠️ 需额外配置
待机(Standby) 极低 复位/WKUP/RTC 最慢 ❌ 用不了

配套代码

  • code/19_软件定时器/
  • code/20_低功耗模式/

后续章节:[[11-内存管理]]