Zephyr 的 Counter alarm

一、概述

Zephyr 的 Counter 驱动支持"相对闹钟"和"绝对闹钟"两种触发语义 。区别在于 struct counter_alarm_cfg 里的 flagsticks 的含义。

关键结构体:

复制代码
struct counter_alarm_cfg {
        counter_alarm_callback_t callback;  //回调
        uint32_t ticks;		//不同模式下含义不同(见下)
        void *user_data;	//给callback做回调参数
        uint32_t flags;		//标志闹钟模式
};

常用标志位(flags

  • alarm_cfg.flags = COUNTER_ALARM_CFG_ABSOLUTE
    绝对闹钟ticks 表示计数器周期空间内的绝对计数值(例如下一次的目标计数点)。需要你自己处理取模(wrap)。
  • 缺省(alarm_cfg.flags = 0
    相对闹钟ticks 表示相对当前的延迟,即"从现在开始延迟多少 ticks 后触发"。

设定闹钟函数

c 复制代码
int counter_set_channel_alarm(const struct device *dev,
                              uint8_t channel_id,
                              const struct counter_alarm_cfg *alarm_cfg);

二、不同模式下 ticks 的含义

2.1 相对闹钟(alarm_cfg.flags = 0

  • ticks 表示从现在起ticks后触发 (相对当前计数值),通常允许的最大延迟受 计数器的拓展/回卷周期(top) 以及驱动"保护期(guard period)"限制。

  • 使用情况:需求是"每隔固定时长触发"(周期性任务),最简单 且不需要考虑取模。注意不要累加延迟。

  • 参考代码实现,通过信号量释放while循环阻塞,1s触发一次:

c 复制代码
#include <zephyr/drivers/counter.h>

static struct counter_alarm_cfg alarm_cfg;
static uint32_t period_ticks;
static struct k_sem sem;

static void alarm_cb(const struct device *dev, uint8_t chan_id,
                     uint32_t ticks, void *user_data)
{
    struct counter_alarm_cfg *config = user_data;
    // 周期性:固定相对延迟为 period_ticks(不要累加)
    // config.ticks = period_ticks; 这个不用动
    int err = counter_set_channel_alarm(dev, chan_id, &config);
    if (err) {
        printk("re-arm failed: %d\n", err);
    }

    k_sem_give(&sem);
}

void main(void)
{
    const struct device *dev = DEVICE_DT_GET(TIMER); // 你的计时器设备
    if (!device_is_ready(dev)) {
        printk("counter not ready\n");
        return;
    }
    k_sem_init(&sem, 0, 1);

    // 计算 1 秒对应的 ticks
    period_ticks = counter_us_to_ticks(dev, 1000000U);

    //(可选)设置保护期,避免过近设置失败
    //counter_set_guard_period(dev, counter_us_to_ticks(dev, 200U), COUNTER_GUARD_PERIOD_LATE);

    // 启动计数器
    counter_start(dev);

    // 首次设置:相对 1 秒后触发
    alarm_cfg.flags = 0;                // 相对模式
    alarm_cfg.ticks = period_ticks;     // 相对延迟
    alarm_cfg.callback = alarm_cb;
    alarm_cfg.user_data = &alarm_cfg;   // 让回调能访问配置
    int err = counter_set_channel_alarm(dev, 0, &alarm_cfg);
    if (err) {
        printk("set alarm failed: %d\n", err);
    }

    while (1) {
        k_sem_take(&sem, K_FOREVER);
        // 可在此做一些cycle性质的工作
    }
}

2.2 绝对闹钟( alarm_cfg.flags = COUNTER_ALARM_CFG_ABSOLUTE

  • ticks 表示计数器周期空间 内的绝对目标值(比如"当计数值到 N 时触发")。

  • 使用情况:

    需要保证你设置的下一个ticks值落在 [0, top)区间内。所以需要注意自己取模。如果不做取模,传入的绝对值超出范围,驱动一般会返回 -EINVAL

    如果计数器是向下计数 ,你可以用原始硬件计数空间或把它转换成统一的"向上空间",但要一致

  • 参考代码实现

c 复制代码
#include <zephyr/drivers/counter.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

static struct counter_alarm_cfg alarm_cfg;
static uint32_t period_ticks;
static uint32_t top;
static struct k_sem sem;

static void alarm_cb(const struct device *dev, uint8_t chan_id,
                     uint32_t fired_ticks, void *user_data)
{
    ARG_UNUSED(user_data);

    // fired_ticks 是这次触发的绝对计数值(在周期空间内)
    // 下一个绝对触发点 = 本次触发点 + 周期(取模 top)
    uint64_t next = (uint64_t)fired_ticks + (uint64_t)period_ticks;
    uint32_t next_ticks = (uint32_t)(next % top);

    printk("Alarm fired (absolute). now=%u next=%u\n", fired_ticks, next_ticks);

    alarm_cfg.ticks = next_ticks;
    alarm_cfg.flags = COUNTER_ALARM_CFG_ABSOLUTE;
    int err = counter_set_channel_alarm(dev, chan_id, &alarm_cfg);
    if (err) {
        printk("re-arm failed: %d\n", err);
    }

    k_sem_give(&sem);
}

void main(void)
{
    const struct device *dev = DEVICE_DT_GET(TIMER);
    if (!device_is_ready(dev)) {
        printk("counter not ready\n");
        return;
    }
    k_sem_init(&sem, 0, 1);

    period_ticks = counter_us_to_ticks(dev, 1000000U);
    top = counter_get_top_value(dev);

    counter_start(dev);

    // 读取当前绝对计数值作为起点
    uint32_t now;
    int err = counter_get_value(dev, &now);
    if (err) {
        printk("get value failed: %d\n", err);
        return;
    }

    // 第一次触发点 = 当前值 + 周期(取模)
    uint32_t first = (now + period_ticks) % top;

    alarm_cfg.flags = COUNTER_ALARM_CFG_ABSOLUTE;
    alarm_cfg.ticks = first;            // 绝对目标值
    alarm_cfg.callback = alarm_cb;
    alarm_cfg.user_data = &alarm_cfg;

    err = counter_set_channel_alarm(dev, 0, &alarm_cfg);
    if (err) {
        printk("set alarm failed: %d\n", err);
    }

    while (1) {
        k_sem_take(&sem, K_FOREVER);
        // 与回调同步
    }
}
相关推荐
ScilogyHunter16 天前
Zephyr串口驱动开发及构建完全指南
驱动开发·uart·zephyr
ScilogyHunter16 天前
Zephyr Hello World应用开发构建完全指南
zephyr·hello world
ScilogyHunter16 天前
Zephyr Twister测试框架完全指南
zephyr·twister
ScilogyHunter17 天前
west init 命令详解
init·zephyr·west
ScilogyHunter17 天前
使用Kconfig配置Zephyr工程完全指南
kconfig·zephyr
ScilogyHunter17 天前
Zephyr设备树完全指南
zephyr
ScilogyHunter18 天前
Zephyr项目按需配置完全指南
zephyr
ScilogyHunter18 天前
Zephyr最简工程配置指南
zephyr
ScilogyHunter18 天前
Zephyr主仓库目录结构完全指南
zephyr
ScilogyHunter18 天前
Zephyr工程配置完全指南
zephyr