返回首页

Timer (Counter) 学习笔记

本笔记学习 Zephyr 的 Counter/Timer 驱动,用于定时器中断和闹钟功能。


示例路径

zephyr/samples/drivers/counter/alarm/

核心概念

Counter vs Timer

在 Zephyr 中:

具体由哪个底层定时外设实现 Counter,要看当前 SoC 和设备树配置。


核心 API

头文件

#include <zephyr/drivers/counter.h>

设备获取

#define TIMER DT_CHOSEN(counter)  // 设备树选择的 counter
const struct device *const counter_dev = DEVICE_DT_GET(TIMER);

设备就绪检查

if (!device_is_ready(counter_dev)) {
    printk("Counter device is not ready\n");
    return;
}

启动计数器

int counter_start(const struct device *dev);

获取当前计数值

uint32_t now_ticks;
int err = counter_get_value(counter_dev, &now_ticks);

计数方向检查

bool counter_is_counting_up(const struct device *dev);

获取最大值

uint32_t top_value = counter_get_top_value(counter_dev);

闹钟 (Alarm) 功能

闹钟配置结构

struct counter_alarm_cfg {
    counter_alarm_callback_t callback;  // 回调函数
    void *user_data;                     // 用户数据
    uint32_t ticks;                      // 闹钟触发计数值
    uint32_t flags;                      // 标志位
};

设置闹钟

#define ALARM_CHANNEL_ID 0

struct counter_alarm_cfg alarm_cfg;
alarm_cfg.ticks = counter_us_to_ticks(counter_dev, 2000000); // 2秒
alarm_cfg.callback = alarm_callback;
alarm_cfg.user_data = &alarm_cfg;
alarm_cfg.flags = 0;

int err = counter_set_channel_alarm(counter_dev, ALARM_CHANNEL_ID, &alarm_cfg);

闹钟回调函数

void alarm_callback(const struct device *counter_dev,
                    uint8_t chan_id,
                    uint32_t ticks,
                    void *user_data)
{
    struct counter_alarm_cfg *cfg = (struct counter_alarm_cfg *)user_data;
    
    printk("!!! Alarm !!!\n");
    
    // 设置下一个闹钟(双倍时间)
    cfg->ticks = cfg->ticks * 2;
    counter_set_channel_alarm(counter_dev, ALARM_CHANNEL_ID, cfg);
}

错误处理

if (-EINVAL == err) {
    printk("Alarm settings invalid\n");
} else if (-ENOTSUP == err) {
    printk("Alarm setting request not supported\n");
} else if (err != 0) {
    printk("Error\n");
}

单位转换

微秒转计数

uint32_t ticks = counter_us_to_ticks(counter_dev, delay_us);

计数转微秒

uint64_t us = counter_ticks_to_us(counter_dev, ticks);

秒转换

int sec = (int)(us / USEC_PER_SEC);

示例代码分析

#include <zephyr/kernel.h>
#include <zephyr/drivers/counter.h>
#include <zephyr/sys/printk.h>

#define DELAY 2000000  // 2秒

static void alarm_callback(const struct device *counter_dev,
                           uint8_t chan_id, uint32_t ticks,
                           void *user_data)
{
    struct counter_alarm_cfg *cfg = user_data;
    uint32_t now_ticks;
    
    counter_get_value(counter_dev, &now_ticks);
    if (!counter_is_counting_up(counter_dev)) {
        now_ticks = counter_get_top_value(counter_dev) - now_ticks;
    }
    
    printk("!!! Alarm !!!\n");
    printk("Now: %u sec\n", 
           (uint32_t)(counter_ticks_to_us(counter_dev, now_ticks) / USEC_PER_SEC));
    
    // 设置下一个闹钟
    cfg->ticks *= 2;
    counter_set_channel_alarm(counter_dev, 0, cfg);
}

int main(void)
{
    const struct device *const counter_dev = DEVICE_DT_GET(TIMER);
    struct counter_alarm_cfg alarm_cfg;
    
    alarm_cfg.flags = 0;
    alarm_cfg.ticks = counter_us_to_ticks(counter_dev, DELAY);
    alarm_cfg.callback = alarm_callback;
    alarm_cfg.user_data = &alarm_cfg;
    
    counter_start(counter_dev);
    counter_set_channel_alarm(counter_dev, 0, &alarm_cfg);
    
    while (1) {
        k_sleep(K_FOREVER);
    }
    return 0;
}

nRF54L15 设备树配置

/ {
    chosen {
        counter = &timer0;  // 或 rtc0
    };
};

&timer0 {
    status = "okay";
};

编译命令

cd /home/wangpei/ncs-sdk
west build -p -b nrf54l15dk/nrf54l15/cpuapp zephyr/samples/drivers/counter/alarm
west flash

运行结果

Counter alarm sample
Set alarm in 2 sec (xxxxx ticks)
!!! Alarm !!!
Now: 2
Set alarm in 4 sec (xxxxx ticks)
!!! Alarm !!!
Now: 6
Set alarm in 8 sec (xxxxx ticks)
...

应用场景

  1. 周期性任务: 设置周期性闹钟触发任务
  2. 超时检测: 设置一次性闹钟检测超时
  3. 精确计时: 使用高精度定时器计时
  4. 低功耗唤醒: 使用 RTC 作为唤醒源

注意事项

  1. 通道数: 检查 counter_get_num_of_channels() 确认支持的闹钟通道数
  2. 频率: 不同硬件频率不同,使用转换函数而非直接计算
  3. 计数方向: 有些计数器是倒计时,需检查 counter_is_counting_up()
  4. 中断上下文: 回调在中断上下文中执行,避免阻塞操作

*小白 🤖 - 2026-03-16*