返回首页

Zephyr 定时器学习笔记

概述

Zephyr 提供了强大的定时器机制,支持单次和周期性定时任务。定时器是实时系统中实现时间控制和任务调度的基础。

定时器类型

1. 内核定时器 (k_timer)

这是最常用的定时器,基于内核时钟。

// 定义定时器
struct k_timer my_timer;

// 定时器回调函数
void timer_handler(struct k_timer *timer)
{
    printk("Timer expired!\n");
}

// 初始化
k_timer_init(&my_timer, timer_handler, NULL);

// 启动(周期性:100ms周期,0首次延迟)
k_timer_start(&my_timer, K_NO_WAIT, K_MSEC(100));

// 启动(单次:500ms后触发)
k_timer_start(&my_timer, K_MSEC(500), K_NO_WAIT);

// 停止定时器
k_timer_stop(&my_timer);

// 检查是否正在运行
if (k_timer_is_pending(&my_timer)) {
    // 定时器正在运行
}

2. 忙等待延迟 (k_busy_wait)

用于微秒级精度的忙等待延迟。

// 忙等待 100 微秒(阻塞CPU)
k_busy_wait(100);

注意:忙等待期间CPU被占用,不执行其他任务。

3. 睡眠延迟 (k_sleep)

线程睡眠,让出CPU。

// 睡眠 100 毫秒
k_sleep(K_MSEC(100));

// 睡眠 1 秒
k_sleep(K_SECONDS(1));

// 睡眠直到绝对时间
k_sleep(K_TIMEOUT_ABS_MS(target_ms));

k_timer 详解

定时器结构

struct k_timer {
    struct k_timeout timeout;
    struct k_thread *thread;
    k_timer_expiry_t expiry_fn;
    k_timer_stop_t stop_fn;
    uint32_t period;
    uint32_t duration;
    // ... 内部字段
};

定时器回调

// 到期回调(必需)
void expiry_function(struct k_timer *timer)
{
    // 定时器到期时调用
    // 在中断上下文执行,需快速返回
}

// 停止回调(可选)
void stop_function(struct k_timer *timer)
{
    // 定时器停止时调用
}

定时器状态

// 获取剩余时间(到期前)
int64_t remaining = k_timer_remaining_get(&my_timer);

// 获取已经过时间(从启动开始)
int64_t elapsed = k_timer_elapsed(&my_timer);

// 获取到期计数
uint32_t count = k_timer_status_get(&my_timer);

// 同步等待定时器到期
k_timer_status_sync(&my_timer);

高级定时器用法

单次定时器

struct k_timer one_shot_timer;

void one_shot_handler(struct k_timer *timer)
{
    printk("One-shot timer fired!\n");
    // 单次定时器,不需要重新启动
}

void init_one_shot(void)
{
    k_timer_init(&one_shot_timer, one_shot_handler, NULL);
    
    // 10秒后触发,不重复
    k_timer_start(&one_shot_timer, K_SECONDS(10), K_NO_WAIT);
}

周期性定时器

struct k_timer periodic_timer;
int sample_count = 0;

void periodic_handler(struct k_timer *timer)
{
    sample_count++;
    
    // 执行周期性任务
    read_sensor();
    
    // 每100次后执行额外操作
    if (sample_count % 100 == 0) {
        printk("Processed %d samples\n", sample_count);
    }
}

void init_periodic(void)
{
    k_timer_init(&periodic_timer, periodic_handler, NULL);
    
    // 立即开始,每50ms执行一次
    k_timer_start(&periodic_timer, K_NO_WAIT, K_MSEC(50));
}

动态定时器(可变周期)

struct k_timer adaptive_timer;
int current_period = 100;  // 初始周期100ms

void adaptive_handler(struct k_timer *timer)
{
    // 根据处理时间调整周期
    int64_t start = k_uptime_get();
    
    do_heavy_work();
    
    int64_t duration = k_uptime_get() - start;
    
    // 动态调整:如果处理时间长,增加周期
    if (duration > current_period / 2) {
        current_period += 50;
        k_timer_start(timer, K_MSEC(current_period), K_MSEC(current_period));
    }
}

超时模式

// 定义超时
k_timeout_t timeout = K_MSEC(100);

// 等待信号量,带超时
int ret = k_sem_take(&my_sem, timeout);
if (ret == -EAGAIN) {
    // 超时发生
}

// 特殊超时值
K_NO_WAIT      // 立即返回,不等待
K_FOREVER      // 永久等待
K_MSEC(n)      // 等待n毫秒
K_SECONDS(n)   // 等待n秒
K_MINUTES(n)   // 等待n分钟
K_HOURS(n)     // 等待n小时

定时器与线程同步

使用信号量同步

struct k_timer sync_timer;
struct k_sem sync_sem;

void timer_sync_handler(struct k_timer *timer)
{
    // 定时器到期,释放信号量
    k_sem_give(&sync_sem);
}

void worker_thread(void)
{
    k_sem_init(&sync_sem, 0, 1);
    k_timer_init(&sync_timer, timer_sync_handler, NULL);
    
    while (1) {
        // 等待定时器或超时
        if (k_sem_take(&sync_sem, K_SECONDS(1)) == 0) {
            // 定时器触发
            do_periodic_work();
        } else {
            // 超时,执行备用操作
            handle_timeout();
        }
    }
}

多定时器管理

#define NUM_TIMERS 3

struct timer_context {
    struct k_timer timer;
    int id;
    const char *name;
};

struct timer_context timers[NUM_TIMERS] = {
    { .id = 0, .name = "sensor" },
    { .id = 1, .name = "display" },
    { .id = 2, .name = "network" },
};

void multi_timer_handler(struct k_timer *timer)
{
    struct timer_context *ctx = CONTAINER_OF(timer, struct timer_context, timer);
    
    switch (ctx->id) {
    case 0:
        read_sensors();
        break;
    case 1:
        update_display();
        break;
    case 2:
        send_heartbeat();
        break;
    }
}

void init_multi_timers(void)
{
    for (int i = 0; i < NUM_TIMERS; i++) {
        k_timer_init(&timers[i].timer, multi_timer_handler, NULL);
    }
    
    // 不同周期启动
    k_timer_start(&timers[0].timer, K_NO_WAIT, K_MSEC(100));   // sensor: 100ms
    k_timer_start(&timers[1].timer, K_NO_WAIT, K_MSEC(500));   // display: 500ms
    k_timer_start(&timers[2].timer, K_NO_WAIT, K_SECONDS(30)); // network: 30s
}

高精度定时

使用硬件定时器

// 获取高精度时间戳(微秒)
int64_t now = k_uptime_get();
int64_t now_us = k_uptime_get_32();  // 32位微秒

// 计算时间差
int64_t start = k_uptime_get();
do_work();
int64_t elapsed = k_uptime_get() - start;
printk("Work took %lld ms\n", elapsed);

// 获取CPU周期计数(最高精度)
uint32_t cycles = k_cycle_get_32();

忙等待 vs 睡眠

// 场景1:需要精确延迟,不在乎CPU占用
void precise_delay(void)
{
    k_busy_wait(100);  // 精确100微秒
}

// 场景2:可以容忍一些误差,需要低功耗
void power_efficient_delay(void)
{
    k_sleep(K_USEC(100));  // 近似100微秒,允许调度
}

定时器配置

# 系统时钟配置
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=32768
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000

# 定时器数量限制
CONFIG_TIMER_RANDOM_INITIAL_STATE=y
CONFIG_TIMERS=y

# 低功耗模式下的定时器
CONFIG_SYSTEM_CLOCK_SLOPPY_IDLE=y

定时器精度

方法精度CPU占用适用场景
k_busy_wait微秒级100%短延迟、时序关键
k_sleep毫秒级0%常规延迟、低功耗
k_timer毫秒级周期性任务
k_cycle_get纳秒级-性能测量

最佳实践

1. 回调函数设计

void good_timer_handler(struct k_timer *timer)
{
    // ✅ 快速执行,设置标志或提交工作项
    k_work_submit(&timer_work);
}

void bad_timer_handler(struct k_timer *timer)
{
    // ❌ 不要在回调中执行长时间操作
    heavy_computation();
    // ❌ 不要在回调中阻塞
    k_sem_take(&sem, K_FOREVER);
}

2. 动态定时器管理

// 安全地重启定时器
void safe_restart(struct k_timer *timer, k_timeout_t period)
{
    k_timer_stop(timer);
    k_timer_start(timer, period, period);
}

// 修改周期
void change_period(struct k_timer *timer, k_timeout_t new_period)
{
    // 先停止再启动(不能使用动态修改)
    k_timer_stop(timer);
    k_timer_start(timer, new_period, new_period);
}

3. 长时间运行定时器

// 处理32位时间戳回绕
int64_t safe_time_diff(int64_t start, int64_t end)
{
    return end - start;  // Zephyr 使用64位时间戳,无需担心回绕
}

常见陷阱

  1. 回调上下文:定时器回调在中断上下文,限制较多
  2. 栈溢出:长时间运行的回调可能导致中断栈溢出
  3. 精度损失:k_sleep 的精度取决于系统滴答率
  4. 竞态条件:停止和重启定时器时的竞争

实用示例:软件看门狗

struct k_timer watchdog_timer;
volatile int watchdog_kicks = 0;

void watchdog_handler(struct k_timer *timer)
{
    if (watchdog_kicks == 0) {
        // 没有被踢,系统可能死锁
        printk("Watchdog timeout! Rebooting...\n");
        sys_reboot(SYS_REBOOT_COLD);
    }
    watchdog_kicks = 0;
}

void watchdog_init(void)
{
    k_timer_init(&watchdog_timer, watchdog_handler, NULL);
    k_timer_start(&watchdog_timer, K_SECONDS(5), K_SECONDS(5));
}

void watchdog_kick(void)
{
    watchdog_kicks++;
}

参考链接


*学习笔记创建时间: 2026-03-19*