正如Linux下一样,关于时间的系统函数可以分为三类:时间值、睡眠一段时间以及延迟执行。

在Zephyr上对应是什么样子呢?带着这个疑问,去了解一下这些函数。

以及他们与suspend之间的关系?

是否计入suspend时间?(计入-在到期后立即执行;不计入-需要唤醒后继续睡眠剩下时间)。

是否具备唤醒功能?如果具备,则能将将系统从suspend唤醒。

1 Zephyr的时间服务基础

Zephyr的官方文档提供了详细的模块说明和API使用方法。

Time:Kernel Clocks是系统所有基于时间服务的基础,包括Timer和Sleep。

Zephyr提供两种时钟计数,一个是高精度的32位硬件时钟计数(hardware clock),cycle表示的长短由硬件决定;

另一个是64位的tick计数(system clock),每个tick大小是由系统配置的,1ms~100ms不等。

2 Zephry的Time

我们知道Zephyr有两种clock计数:hardware clock和system clock。

system clock是内核很多基于时间服务的基础,包括timer或者其他超时服务。

当然,system clock是建立在hardware clock基础之上的。系统决定一个tick多长时间,然后换算成多少个hardware clock cycles。

通过对hardware clock编程,hardware clock倒计数然后产生中断,一个中断表示一个tick。

系统时间服务受制于tick的精度,比如10ms的tick,如果delay是25的话,会对齐到30ms。

即使是20ms delay,很有可能时间delay达到30ms,因为当前设置的delay只有到下一次tick产生才会设置在下两个tick到期。

系统时间相关服务建立在tick之上,tick建立在hardware clock之上,hardware clock具有最高精度,因此如果想要高精度时间测量,可以使用hardware clock。

不同精度时间示例。

普通精度:

s64_t time_stamp;
s64_t milliseconds_spent; /* capture initial time stamp */
time_stamp = k_uptime_get(); /* do work for some (extended) period of time */
... /* compute how long the work took (also updates the time stamp) */
milliseconds_spent = k_uptime_delta(&time_stamp);

高精度:

u32_t start_time;
u32_t stop_time;
u32_t cycles_spent;
u32_t nanoseconds_spent; /* capture initial time stamp */
start_time = k_cycle_get_32(); /* do work for some (short) period of time */
... /* capture final time stamp */
stop_time = k_cycle_get_32(); /* compute how long the work took (assumes no counter rollover) */
cycles_spent = stop_time - start_time;
nanoseconds_spent = SYS_CLOCK_HW_CYCLES_TO_NS(cycles_spent);

Tick的配置通过CONFIG_SYS_CLOCK_TICKS_PER_SEC相关API位于Clocks

3 Zephyr的Sleep

Zephyr的时间一个应用是Thread Sleeping,线程可以通过k_sleep()来延迟一段时间处理。在这段时间内,当前线程进入睡眠。在sleep时间满足并且当前进程被调度到,才会继续运行。

其他进程可以通过k_wakeup()提前唤醒处于k_sleep进程;如果进程不处于k_sleep中,k_wakeup则不起作用。

问题:那么如果睡眠过程中进入suspend,k_sleep是否计入suspend时间?

答:k_sleep是计入suspend时间的,也即suspend时间会补偿到sleep中,并且sleep到期可以唤醒suspend。

void k_sleep(s32_t duration)
Put the current thread to sleep. This routine puts the current thread to sleep for duration milliseconds. Return
N/A
Parameters
duration: Number of milliseconds to sleep. void k_wakeup(k_tid_t thread)
Wake up a sleeping thread. This routine prematurely wakes up thread from sleeping. If thread is not currently sleeping, the routine has no effect. Return
N/A
Parameters
thread: ID of thread to wake.

Busy Waiting是另一种延迟操作,k_busy_wait和k_sleep有以下不同:

  • k_busy_wait基于hardware clock进行计数,更加精确;k_sleep基于system tick。
  • k_busy_wait期间不会将CPU调度权交出去;k_sleep允许CPU调度别的线程。
  • k_busy_wait只能在非常短延时的情况下使用。
void k_busy_wait(u32_t usec_to_wait)
Cause the current thread to busy wait. This routine causes the current thread to execute a “do nothing” loop for usec_to_wait microseconds. Return
N/A

4 Zephyr的Timer

Zephyr时间另一应用是Timers,包括几个要素duration(第一次定时器)、period(第一次超时之后的周期性定时器)、expiry function(超时函数)、stop function(提前结束Timer)、status(Timer的状态)。

Timer使用之前必须先初始化,如果period不为0,则第一次从超时后会重新起一个period的timer。timer执行过程中可以被停止或者重新触发。Timer的状态可以随时随地读取。

Zephyr Timer还有另一种同步读取状态的功能,这个功能会将当前进程阻塞,直到timer状态非零(即timer已经发生过超时,最起码一次)或者timer被停止。如果已经非零或者被停止,则当前线程不用等待。

Timer初始化:

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(my_timer, my_expiry_function, NULL);

使用Timer:

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));

获取Timer状态:

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(), ); /* do work */
... /* check timer status */
if (k_timer_status_get(&my_status_timer) > ) {
/* timer has expired */
} else if (k_timer_remaining_get(&my_status_timer) == ) {
/* timer was stopped (by someone else) before expiring */
} else {
/* timer is still running */
}

同步获取Timer状态(还具有delay的功能):

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(), ); /* do other work */
... /* ensure timer has expired (waiting for expiry, if necessary) */
k_timer_status_sync(&my_sync_timer);
/* do second protocol operation */
...

停止Timer:

k_timer_stop(&my_timer)

5 system tick机制

timer的中断触发,_sys_idle_elapsed_ticks在没有使能Tickless的情况下一般是1.

.word _timer_int_handler(vector_table.S)-->
_timer_int_handler(cortex_m_systick.c)-->
_sys_clock_tick_announce(cortex_m_systick.c)-->
_nano_sys_clock_tick_announce(_sys_idle_elapsed_ticks)-->
handle_timeouts-->遍历_timeout_q,执行超时timer的函数。
handle_time_slicing static inline void handle_timeouts(s32_t ticks)
{
sys_dlist_t expired;
unsigned int key; /* init before locking interrupts */
sys_dlist_init(&expired); key = irq_lock(); struct _timeout *head =
(struct _timeout *)sys_dlist_peek_head(&_timeout_q);---------------取_timeout_q的头
...
head->delta_ticks_from_prev -= ticks;----------------------------------减去逝去tick数 /*
* Dequeue all expired timeouts from _timeout_q, relieving irq lock
* pressure between each of them, allowing handling of higher priority
* interrupts. We know that no new timeout will be prepended in front
* of a timeout which delta is 0, since timeouts of 0 ticks are
* prohibited.
*/
sys_dnode_t *next = &head->node;
struct _timeout *timeout = (struct _timeout *)next; _handling_timeouts = ; while (timeout && timeout->delta_ticks_from_prev <= 0) {------------当前timeout已经超时 sys_dlist_remove(next);-----------------------------------------将next(即当前timeout)从列表移除 /*
* Reverse the order that that were queued in the timeout_q:
* timeouts expiring on the same ticks are queued in the
* reverse order, time-wise, that they are added to shorten the
* amount of time with interrupts locked while walking the
* timeout_q. By reversing the order _again_ when building the
* expired queue, they end up being processed in the same order
* they were added, time-wise.
*/
sys_dlist_prepend(&expired, next);------------------------------将next加入到expired列表 timeout->delta_ticks_from_prev = _EXPIRED;----------------------将timeout(next)的delta_ticks_from_prev设置为_EXPIRED irq_unlock(key);
key = irq_lock(); next = sys_dlist_peek_head(&_timeout_q);------------------------重新取_timeout_q的头
timeout = (struct _timeout *)next;
} irq_unlock(key); _handle_expired_timeouts(&expired);--------------------------------遍历_timeout_q列表,选出超时timer到expired之后,然后在_handle_expired_timeouts中一个一个执行。 _handling_timeouts = ;
}

从handle_timeouts可知,_timeout_q上的times排列是按照超时顺序排列的。所以从头开始遍历,能按照超时顺序执行超时函数。

那么这是如何实现的呢?就要看插入_timeout_q列表操作了。

k_sleep-->_add_thread_timeout-->
k_timer_init-->_time_expiration_handler-->
k_timer_start-->
k_delayed_work_submit_to_queue-->
_add_timeout--------------------------------_add_timeout是操作_timeout_q的核心 static inline void _add_timeout(struct k_thread *thread,
struct _timeout *timeout,
_wait_q_t *wait_q,
s32_t timeout_in_ticks)
{
__ASSERT(timeout_in_ticks > , ""); timeout->delta_ticks_from_prev = timeout_in_ticks;
timeout->thread = thread;
timeout->wait_q = (sys_dlist_t *)wait_q; K_DEBUG("before adding timeout %p\n", timeout);
_dump_timeout(timeout, );
_dump_timeout_q(); s32_t *delta = &timeout->delta_ticks_from_prev;
struct _timeout *in_q;
...
SYS_DLIST_FOR_EACH_CONTAINER(&_timeout_q, in_q, node) {----------遍历_timeout_q列表,找出合适的位置:delta的值小于等于下一节点,大于前一节点。
if (*delta <= in_q->delta_ticks_from_prev) {
in_q->delta_ticks_from_prev -= *delta;
sys_dlist_insert_before(&_timeout_q, &in_q->node,--------将新节点timeout插入到in_q之前
&timeout->node);
goto inserted;
} *delta -= in_q->delta_ticks_from_prev;-----------------------初始delta是和当前时间的差值,在寻找插入位置的过程中会逐渐递减,相对时间参考点逐渐后移。一直以前一个timer超时点为基准。
} sys_dlist_append(&_timeout_q, &timeout->node); inserted:
K_DEBUG("after adding timeout %p\n", timeout);
_dump_timeout(timeout, );
_dump_timeout_q(); ...
}

suspend对系统tick影响,通过k_tick_add将suspend时间补偿到_sys_clock_tick_count,同时更新_timeout_q:

_sys_soc_suspend-->
k_tick_add--> void k_tick_add(u32_t time)
{
u32_t tick;
struct _timeout *timeout; tick = _ms_to_ticks(time); _sys_clock_tick_count += tick;---------------------------系统Tick计数值 timeout = (struct _timeout *)sys_dlist_peek_head(&_timeout_q);
if (timeout)
timeout->delta_ticks_from_prev -= tick;-------------------------????只补偿了链表头,是否有必要补偿所有节点。
}

补充:关于Zephyr的链表《zephyr学习笔记---双向链表dlist》。

Zephyr的Time、Timer、sleep的更多相关文章

  1. Zephyr学习(四)系统时钟

    每一个支持多进程(线程)的系统都会有一个滴答时钟(系统时钟),这个时钟就好比系统的“心脏”,线程的休眠(延时)和时间片轮转调度都需要用到它. Cortex-M系列的内核都有一个systick时钟,这个 ...

  2. Zephyr学习专题

    1 前言 本来想学习Zyphyr的Power Management,但是看着看着就被带进去了. 你看功耗,里面的suspend涉及到时间补偿相关的吧,然后就涉及到了Kernel Clocks/Time ...

  3. Zephyr的Power Management

    1 关于Zephyr Zephyr是Linux基金会维护的微内核项目,来源于WindRiver向Zephyr捐赠的Rocket RTOS内核.主要用于开发针对物联网设备的实时操作系统. Zephyr操 ...

  4. Zephyr OS 简介

    最新发布的开源 Zephyr Project™(Zephyr 项目)是一款小型且可伸缩的实时操作系统,尤其适用于资源受限的系统,可支持多种架构:该系统高度开源,对于开发人员社区完全开放,开发人员可根据 ...

  5. C# - 计时器Timer

    System.Timers.Timer 服务器计时器,允许指定在应用程序中引发事件的重复时间间隔. using System.Timers: // 在应用程序中生成定期事件 public class ...

  6. winform 用户控件、 动态创建添加控件、timer控件、控件联动

    用户控件: 相当于自定义的一个panel 里面可以放各种其他控件,并可以在后台一下调用整个此自定义控件. 使用方法:在项目上右键.添加.用户控件,之后用户控件的编辑与普通容器控件类似.如果要在后台往窗 ...

  7. 【WPF】 Timer与 dispatcherTimer 在wpf中你应该用哪个?

    源:Roboby 1.timer或重复生成timer事件,dispatchertimer是集成到队列中的一个时钟.2.dispatchertimer更适合在wpf中访问UI线程上的元素 3.Dispa ...

  8. STM32F10xxx 之 System tick Timer(SYSTICK Timer)

    背景 研究STM32F10xxx定时器的时候,无意间看到了System tick Timer,于是比较深入的了解下,在此做个记录. 正文 System tick Timer是Cotex-M内核的24位 ...

  9. 本地数据Store。Cookie,Session,Cache的理解。Timer类主要用于定时性、周期性任务 的触发。刷新Store,Panel

    本地数据Store var monthStore = Ext.create('Ext.data.Store', { storeId : 'monthStore', autoLoad : false, ...

随机推荐

  1. [总结]jQuery之选择器集合

    jQuery 的选择器可谓之强大无比,常用的元素查找方法: $("#myELement") //选择id值等于myElement的元素,id值不能重复在文档中只能有一个id值是my ...

  2. element UI实现表格中添加开关控制按钮

    我使用的是element ui V1.4.3 如下图是我要实现的效果: <template> <div> <el-button type="text" ...

  3. CSS字体超出两行省略

    text-overflow: -o-ellipsis-lastline;overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-w ...

  4. 自己搭建git 代码服务器

    使用git服务器的工程师都需要生成一个ssh的公钥 ~/.ssh$ ssh-keygen Generating public/private rsa key pair. …………………… ……………… ...

  5. Apktool(1)——Apktool的安装

    Apktool是google提供的apk的编译工具,有了它就可以做很多事情.比如获取apk的源码,apk汉化,对手机rom包做一些美化. 首先来看看apktool的安装(配置): 以下内容主要翻译字A ...

  6. @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 前台request 获取body的格式是正确的 (2018-03-23 16:44:22) 但是Java 后台却格式化成了yyyy-MM-dd的格式 巨坑(@InitBinder搞得贵)

    最近做项目时,同事写的功能总是格式化时间不正确,Java类属性明明注解了@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")  但就是硬 ...

  7. Python进阶点

    1. 模块化设计,分而治之 2. 组合数据类型 2.1 集合类型:list.set(无序/不重复),用于数据去重 2.2 序列类型:字符串.元组.列表(有序) 2.3 字典类型:根据字典中 k/v 来 ...

  8. 根据学习廖雪峰老师的git教程做的笔记

    根据学习廖雪峰老师的git教程做的笔记 安装git 进行git的配置 配置您的用户名和邮箱地址,使用--global 这个参数表明了在此台机器上的所有仓库都会使用该配置 $ git config -- ...

  9. (转载)Oracle 树操作(select…start with…connect by…prior)

    转载地址:https://www.cnblogs.com/linjiqin/p/3152674.html 备注:如有侵权,请立即联系删除. oracle树查询的最重要的就是select…start w ...

  10. mybatis学习--缓存(一级和二级缓存)

    声明:学习摘要! MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级) ...