zephyr笔记 2.2.2 定时器

本文介绍了Zephyr操作系统中的定时器概念、关键属性及使用方法。详细解释了如何定义、初始化、启动和读取定时器状态,以及如何利用定时器进行同步操作。此外还提供了一些实用的代码示例。

1 前言

计时器是一个内核对象,它使用内核的系统时钟来度量时间的流逝。 当达到定时器的指定时间限制时,它可以执行应用程序定义的操作,或者它可以简单地记录到期并等待应用程序读取其状态。

我正在学习 Zephyr,一个很可能会用到很多物联网设备上的操作系统,如果你也感兴趣,可点此查看帖子zephyr学习笔记汇总

2 概念

可以定义任意数量的定时器。每个定时器都由地址引用。

计时器具有以下关键属性:

  • duration,指定定时器到期前的持续时间,以毫秒为单位。它必须大于零。

  • period ,指定定时器到期后的时间间隔(以毫秒为单位)。它必须是非负的。零周期意味着定时器是一次性定时器,在一次到期后停止。 (例如,如果一个定时器的启动持续时间为200,周期为75,它将首先持续200ms,然后再75ms后重复。)

  • expiry function,每当计时器到期时执行一次到期函数。该功能由系统时钟中断处理程序执行。如果不需要到期函数,则可以指定NULL函数。

  • stop function,如果定时器在运行时过早停止,则执行停止功能。该函数由停止定时器的线程执行。如果不需要停止功能,则可以指定NULL功能。

  • status,状态值,指示自从状态值上次读取以来定时器已经过期的次数。

定时器必须在使用前初始化。这指定了其到期函数和停止函数值,将定时器的状态设置为零,并使定时器进入停止状态。

定时器通过指定持续时间和周期来启动。定时器的状态被重置为零,然后定时器进入运行状态并开始到期的倒计时。

当一个正在运行的定时器到期时,它的状态会增加,如果存在到期函数的话会执行到期函数; 如果一个线程正在等待定时器,它将被解除阻塞。 如果定时器的周期为零,则定时器进入停止状态; 否则定时器会以等于其周期的新持续时间重新启动。

如果需要,正在运行的计时器可以在倒计时期间中止。定时器的状态保持不变,然后定时器进入停止状态并执行其停止函数(如果存在)。如果一个线程正在等待定时器,它将被解除阻塞。试图停止不运行的计时器是允许的,但它对定时器没有影响,因为它已经停止。

如果需要,正在运行的定时器可以在倒数计时器中重新启动。定时器的状态重置为零,然后定时器使用调用者指定的新的持续时间和周期值开始倒计时。如果一个线程正在等待定时器,它将继续等待。

可以随时直接读取定时器的状态,以确定定时器自上次读取状态以来已经过了多少次。读定时器的状态会将其值重置为零。定时器到期之前剩余的时间量也可以读取;值为零表示定时器已停止。

线程可以通过与定时器同步来间接读取定时器的状态。这会阻塞线程,直到定时器的状态为非零(表示它至少已经过期)或定时器停止;如果定时器状态已经非零或定时器已经停止,则线程继续而不等待。同步操作返回定时器的状态并将其重置为零。

注意:由于读取状态(直接或间接)会改变其值,因此只有一个用户应该检查任何给定定时器的状态。 同样,一次只有一个线程应该与给定的定时器同步。ISR不允许与定时器同步,因为ISR不允许被阻塞。

3 定时器限制

由于定时器基于系统时钟,因此使用定时器时指定的延迟值为最小值。 (请参阅zephyr笔记 2.2.1 内核时钟中的时钟限制章节。)

4 操作

4.1 定义一个计时器

定时器使用 struct k_timer 类型的变量来定义。 它必须通过调用 k_timer_init() 来初始化。

以下代码定义并初始化一个计时器。

struct k_timer my_timer;
extern void my_expiry_function(struct k_timer *timer_id);

k_timer_init(&my_timer, my_expiry_function, NULL);

或者,可以通过调用K_TIMER_DEFINE在编译时定义和初始化计时器。

以下代码与上面的代码段具有相同的效果。

K_TIMER_DEFINE(my_timer, my_expiry_function, NULL);

4.2 使用定时器到期函数

以下代码使用计时器定期执行重要操作。由于所需工作不能在中断级完成,因此计时器的到期函数将工作项提交给系统工作队列,该工作队列的线程执行工作。

void my_work_handler(struct k_work *work)
{
    /* do the processing that needs to be done periodically */
    ...
}

K_WORK_DEFINE(my_work, my_work_handler);

void my_timer_handler(struct k_timer *dummy)
{
    k_work_submit(&my_work);
}

K_TIMER_DEFINE(my_timer, my_timer_handler, NULL);

...

/* start periodic timer that expires once every second */
k_timer_start(&my_timer, K_SECONDS(1), K_SECONDS(1));

4.3 读取计时器状态

以下代码直接读取计时器的状态以确定计时器是否已过期。

K_TIMER_DEFINE(my_status_timer, NULL, NULL);

...

/* start one shot timer that expires after 200 ms */
k_timer_start(&my_status_timer, K_MSEC(200), 0);

/* do work */
...

/* check timer status */
if (k_timer_status_get(&my_status_timer) > 0) {
    /* timer has expired */
} else if (k_timer_remaining_get(&my_status_timer) == 0) {
    /* timer was stopped (by someone else) before expiring */
} else {
    /* timer is still running */
}

4.4 使用定时器状态同步

以下代码执行定时器状态同步以允许线程执行有用的工作,同时确保一对协议操作按指定的时间间隔分隔。

K_TIMER_DEFINE(my_sync_timer, NULL, NULL);

...

/* do first protocol operation */
...

/* start one shot timer that expires after 500 ms */
k_timer_start(&my_sync_timer, K_MSEC(500), 0);

/* do other work */
...

/* ensure timer has expired (waiting for expiry, if necessary) */
k_timer_status_sync(&my_sync_timer);

/* do second protocol operation */
...
注意:如果线程没有其他工作要做,它可以简单地在两个协议操作之间休眠,而不使用定时器。

5 建议用法

使用定时器在指定的时间后启动异步操作。

使用计时器确定是否已经过了指定的时间量。

使用计时器执行其他工作,同时执行涉及时间限制的操作。

注意:如果一个线程在等待时间通过时没有其他工作要执行,它应该调用k_sleep() 。 如果线程需要测量执行操作所需的时间,它可以直接读取系统时钟或硬件时钟,而不是使用定时器。

6 配置选项

7 APIs

下列定时器API,都在 kernel.h 中提供了:

K_TIMER_DEFINE
k_timer_init()
k_timer_start()
k_timer_stop()
k_timer_status_get()
k_timer_status_sync()
k_timer_remaining_get()

End


<think>根据用户需求,用户希望查找使用Zephyr编写定时器延时代码的方法,并请求示例代码。 结合引用内容: 引用[1]提到定时器是一种内核对象,用于定时执行任务,并举例智能温控系统中的使用。 引用[3]提到计时器可以配置为各种模式,EmbOS-Ultra利用定时器计数到零或到指定值的模式,在需要时触发中断。 虽然引用中没有直接给出Zephyr定时器延时代码示例,但我们可以根据Zephyr官方文档和常见用法提供示例。 在Zephyr中,实现延时通常有两种方式: 1. 使用内核定时器(k_timer)结合信号量或回调函数。 2. 使用简单的忙等待(k_busy_wait)或睡眠(k_sleep/k_msleep)函数,但注意睡眠函数可能会让出CPU。 考虑到用户明确要求“定时器延时”,这里我们使用k_timer来实现一个非阻塞的延时,并在延时结束后执行回调函数。 步骤: a. 定义定时器变量 b. 初始化定时器,设置回调函数 c. 启动定时器(设置延时时间) d. 在回调函数中处理延时结束后的操作 另外,也可以使用k_sleep()函数进行简单的阻塞延时,但这不是定时器对象,而是线程睡眠。 下面给出两个示例: 示例1:使用k_timer实现非阻塞延时(带回调函数) 示例2:使用k_msleep实现阻塞延时(简单延时,线程睡眠) 根据用户请求的是“定时器延时代码”,我们优先展示定时器对象的使用(非阻塞方式)。 </think>### Zephyr定时器延时代码示例 在Zephyr RTOS中,定时器延时可通过内核定时器(`k_timer`)实现,支持阻塞和非阻塞两种模式。以下是具体实现方法: #### 1. **阻塞延时(线程睡眠)** ```c #include <zephyr/kernel.h> void main(void) { printk("开始延时...\n"); k_msleep(1000); // 阻塞延时1000ms(线程主动让出CPU) printk("1秒延时结束\n"); } ``` **适用场景**:需要简单延时且无需精确回调的场景,如传感器数据采集间隔[^1]。 --- #### 2. **非阻塞延时(定时器回调)** ```c #include <zephyr/kernel.h> // 定义定时器结构体和回调函数 struct k_timer my_timer; void timer_expiry_cb(struct k_timer *timer_id) { printk("定时器到期!执行任务...\n"); // 此处添加定时任务(如读取传感器、控制设备) } void main(void) { k_timer_init(&my_timer, timer_expiry_cb, NULL); // 初始化定时器 printk("启动定时器...\n"); k_timer_start(&my_timer, K_MSEC(2000), K_NO_WAIT); // 2秒后触发回调(非阻塞) while (1) { k_msleep(500); // 主线程可继续执行其他任务 } } ``` **关键参数说明**: - `k_timer_start(timer, duration, period)`: - `duration`:首次触发时间(例:`K_MSEC(2000)`表示2000ms) - `period`:周期触发时间(`K_NO_WAIT`表示单次触发) **适用场景**:需要异步执行的任务,如智能温控系统定期调节空调状态[^1][^3]。 --- #### 3. **高精度忙等待延时(CPU阻塞)** ```c #include <zephyr/kernel.h> void main(void) { printk("开始高精度延时...\n"); k_busy_wait(500 * 1000); // 500微秒延时(CPU忙等待) printk("延时结束\n"); } ``` **注意**:仅适用于微秒级短延时,长时间忙等待会阻塞整个系统。 --- ### 关键特性总结 1. **灵活触发模式** - 单次触发(`period = K_NO_WAIT`) - 周期触发(例:`period = K_SECONDS(1)`每秒触发)[^3] 2. **低功耗支持** 定时器到期前自动进入空闲状态,适用于电池供电设备(如可穿戴设备)[^2] 3. **跨平台兼容** 相同API支持所有Zephyr硬件平台(如nRF52、STM32) > 引用说明: > [^1] 定时器用于智能温控系统等周期性任务场景。 > [^2] 低功耗设计在可穿戴设备中优化续航。 > [^3] 定时器支持自由计数模式实现精确调度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值