zephyr笔记 2.2.2 定时器

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


Zephyr里使用ESP32-C3的定时器,需要使用Zephyr提供的API来配置和控制该定时器。以下是使用Zephyr API配置ESP32-C3定时器的一些步骤: 1. 引入头文件 在你的代码中,你需要引入Zephyr提供的头文件,如下所示: ```c #include <zephyr.h> #include <device.h> #include <drivers/timer/esp_timer.h> ``` 2. 定义定时器 定义一个定时器的结构体变量,如下所示: ```c struct esp_timer { struct device dev; esp_timer_isr_t isr; void *user_data; }; ``` 其中,`dev`字段是定时器设备对象,`isr`字段是中断服务函数指针,`user_data`字段是用户数据指针。 3. 配置定时器 使用`device_get_binding()`函数获取定时器设备对象,并使用`esp_timer_start()`函数启动定时器,如下所示: ```c static struct esp_timer timer = { .dev = DEVICE_DT_GET(DT_NODELABEL(timer)), }; void timer_callback(struct esp_timer *timer, void *user_data) { /* 定时器超时后的回调函数 */ // TODO: do something } void timer_init(void) { esp_timer_isr_register(&timer, timer_callback, NULL); esp_timer_start(&timer, ESP_TIMER_MODE_PERIODIC, 1000); // 定时器周期为1秒 } ``` 在这个例子中,`esp_timer_isr_register()`函数用于注册定时器中断服务函数,`esp_timer_start()`函数用于启动定时器,参数`ESP_TIMER_MODE_PERIODIC`指定了定时器的工作模式为周期性定时器,参数`1000`指定了定时器的周期为1秒。 4. 处理定时器中断 定时器中断服务函数由`timer_callback()`函数实现,当定时器超时后,会自动调用该函数。在该函数中,你可以执行一些操作,比如更新显示、发送数据等。 ```c void timer_callback(struct esp_timer *timer, void *user_data) { /* 定时器超时后的回调函数 */ // TODO: do something } ``` 以上就是使用Zephyr API配置ESP32-C3定时器的一些基本步骤,你可以根据具体的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值