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);
        // 与回调同步
    }
}
相关推荐
Amonter9 天前
Zephyr OS驱动0.96OLED
oled·ssd1306·zephyr
Amonter11 天前
Ubuntu下搭建zephyrOS开发环境
rtos·zephyr·esp32c3
驱动探索者18 天前
Zephyr 获取 cpu 占用率异常bug分析
bug·rtos·zephyr
jz-炸芯片的zero2 个月前
【Zephyr存储专题】16_内存泄露检测可视化脚本自动化
自动化·嵌入式·ai编程·zephyr
jz-炸芯片的zero2 个月前
【Zephyr电源与功耗专题】15_功耗优化测试工具与手段
嵌入式·zephyr·功耗
jz-炸芯片的zero3 个月前
【Zephyr电源与功耗专题】14_BMS电池管理算法(三重验证机制实现高精度电量估算)
单片机·物联网·算法·zephyr·bms电源管理算法
jz-炸芯片的zero3 个月前
【Zephyr炸裂知识系列】11_手撸内存泄露监测算法
驱动开发·算法·iot·rtos·内存泄露·zephyr
mftang4 个月前
Zephyr 中的 bt_le_per_adv_set_data 函数的介绍和应用方法
嵌入式硬件·nordic·zephyr
lepton_yang4 个月前
Zephyr下控制ESP32S3的GPIO口
linux·嵌入式硬件·esp32·zephyr