时钟节拍

  • 时钟节拍是特定的周期中断,可以看是系统心跳,中断之间的时间间隔取决于不同的应用,一般是 1ms–100ms,时钟节拍率越快,系统的额外开销就越大,从系统启动开始计数的时钟节拍数称为系统时间。
  • RT-Thread 中,时钟节拍的长度可以根据 RT_TICK_PER_SECOND 的定义来调整,等于 1/RT_TICK_PER_SECOND 秒。

时间节拍实现方式

  • 时钟节拍由配置为中断触发模式的硬件定时器产生,当中断到来时,将调用一次:void rt_tick_increase(void),通知操作系统已经过去一个系统时钟
  • /**
    * This is the timer interrupt service routine.
    *
    */
    void SysTick_Handler(void)
    {
    /* enter interrupt */
    rt_interrupt_enter(); rt_tick_increase(); /* leave interrupt */
    rt_interrupt_leave();
    }
  • 在中断函数中调用 rt_tick_increase() 对全局变量 rt_tick 进行自加

  • /**
    * This function will notify kernel there is one tick passed. Normally,
    * this function is invoked by clock ISR.
    */
    void rt_tick_increase(void)
    {
    struct rt_thread *thread; /* increase the global tick */
    ++ rt_tick; /* check time slice */
    thread = rt_thread_self(); -- thread->remaining_tick;
    if (thread->remaining_tick == )
    {
    /* change to initialized tick */
    thread->remaining_tick = thread->init_tick; /* yield */
    rt_thread_yield();
    } /* check timer */
    rt_timer_check();
    }
  • 全局变量 rt_tick 在每经过一个时钟节拍时,值就会加 1,rt_tick 的值表示了系统从启动开始总共经过的时钟节拍数,即系统时间。此外,每经过一个时钟节拍时,都会检查当前线程的时间片是否用完,以及是否有定时器超时。
  • 中断中的 rt_timer_check() 用于检查系统硬件定时器链表,如果有定时器超时,将调用相应的超时函数。且所有定时器在定时超时后都会从定时器链表中被移除,而周期性定时器会在它再次启动时被加入定时器链表。

获取时钟节拍

  • 全局变量 rt_tick 在每经过一个时钟节拍时,值就会加 1,通过调用 rt_tick_get 会返回当前 rt_tick 的值,即可以获取到当前的时钟节拍值。此接口可用于记录系统的运行时间长短,或者测量某任务运行的时间。
  • rt_tick_t rt_tick_get(void)

定时器管理

  • 定时器有硬件定时器和软件定时器之分。硬件定时器由芯片中断控制器产生时钟中断;软件定时器是由操作系统提供的一个接口;

RT-Thread定时器介绍

  • RT-Thread的定时器分为单次触发定时器和周期触发定时器;
  • 根据超时函数执行时所处的上下文环境,RT-Thread 的定时器可以分为 HARD_TIMER 模式与 SOFT_TIMER 模式;

HARD_TIMER 模式

  • HARD_TIMER 模式的定时器超时函数在中断上下文环境中执行,可以在初始化 / 创建定时器时使用参数 RT_TIMER_FLAG_HARD_TIMER 来指定。
  • 在中断上下文环境中执行时,对于超时函数的要求与中断服务例程的要求相同:执行时间应该尽量短,执行时不应导致当前上下文挂起、等待。例如在中断上下文中执行的超时函数它不应该试图去申请动态内存、释放动态内存等。
  • RT-Thread 定时器默认的方式是 HARD_TIMER 模式,即定时器超时后,超时函数是在系统时钟中断的上下文环境中运行的。在中断上下文中的执行方式决定了定时器的超时函数不应该调用任何会让当前上下文挂起的系统函数;也不能够执行非常长的时间,否则会导致其他中断的响应时间加长或抢占了其他线程执行的时间。

SOFT_TIMER 模式

  • SOFT_TIMER 模式可配置,通过宏定义 RT_USING_TIMER_SOFT 来决定是否启用该模式。该模式被启用后,系统会在初始化时创建一个 timer 线程,然后 SOFT_TIMER 模式的定时器超时函数在都会在 timer 线程的上下文环境中执行。可以在初始化 / 创建定时器时使用参数 RT_TIMER_FLAG_SOFT_TIMER 来指定设置 SOFT_TIMER 模式。

定时器工作机制

  • 在 RT-Thread 定时器模块中维护着两个重要的全局变量:系统经过的 tick 时间 rt_tick、定时器链表 rt_timer_list。系统新创建并激活的定时器都活按照以超时时间排序的方式插入到rt_timer_list链表中;
  • 假设系统当前 tick 值为 20,在当前系统中已经创建并启动了三个定时器,分别是定时时间为 50 个 tick 的 Timer1、100 个 tick 的 Timer2 和 500 个 tick 的 Timer3,这三个定时器分别加上系统当前时间 rt_tick=20,从小到大排序链接在 rt_timer_list 链表中,形成如图所示的定时器链表结构
  • rt_tick 随着硬件定时器的触发一直在增长,50 个 tick 以后,rt_tick 从 20 增长到 70,与 Timer1 的 timeout 值相等,这时会触发与 Timer1 定时器相关联的超时函数,Timer2和Timer3同理

  • 如果系统当前定时器状态在 10 个 tick 以后(rt_tick=30)有一个任务新创建了一个 tick 值为 300 的 Timer4 定时器,由于 Timer4 定时器的 timeout=rt_tick+300=330, 因此它将被插入到 Timer2 和 Timer3 定时器中间,形成如下图所示链表结构:

定时控制块

  • 定时器控制块由结构体struct rt_timer定义并形成定时器内核对象,再链接到内核对象容器中进行管理,list 成员则用于把一个激活的(已经启动的)定时器链接到 rt_timer_list 链表中。它是操作系统用于管理定时器的一个数据结构,会存储定时器的一些信息,例如初始节拍数,超时时的节拍数,也包含定时器与定时器之间连接用的链表结构,超时回调函数等。定义在 rtdef.h 中
  • /**
    * timer structure
    */
    struct rt_timer
    {
    struct rt_object parent; /**< inherit from rt_object */ rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL]; /**< 定时器链表节点 */ void (*timeout_func)(void *parameter); /**< timeout function */
    void *parameter; /**< timeout function's parameter */ rt_tick_t init_tick; /**< timer timeout tick */
    rt_tick_t timeout_tick; /**< timeout tick */
    };
    typedef struct rt_timer *rt_timer_t;

定时器跳表(Skip List)算法

  • 跳表是一种基于并联链表的数据结构,跳表是链表的一种,但它在链表的基础上增加了 “跳跃” 功能,正是这个功能,使得在查找元素时,跳表能够提供 O(log n)的时间复杂度;
  • 例如:一个有序的链表,如下图所示,从该有序链表中搜索元素 {13, 39},需要比较的次数分别为 {3, 5},总共比较的次数为 3 + 5 = 8 次。

  • 用跳表算法后可以采用类似二叉搜索树的方法,把一些节点提取出来作为索引,在这个结构里把 {3, 18,77} 提取出来作为一级索引,这样搜索的时候就可以减少比较次数了,得到如下图所示的结构:

  • 可以再从一级索引提取一些元素出来,作为二级索引,这样更能加快元素搜索。

  • 在搜索的时候就减少比较次数,提升查找的效率,这是一种通过 “空间来换取时间” 的算法,在 RT-Thread 中通过宏定义 RT_TIMER_SKIP_LIST_LEVEL 来配置跳表的层数,默认为 1,表示采用一级有序链表图的有序链表算法,每增加一,表示在原链表基础上增加一级索引。

定时器的管理方式

  • 在系统启动时需要初始化定时器管理系统。可以通过下面的函数接口完成:
  • /**
    * @ingroup SystemInit
    *
    * This function will initialize system timer
    */
    void rt_system_timer_init(void)
  • 如果需要使用 SOFT_TIMER,则系统初始化时,应该调用下面这个函数接口:

  • /**
    * @ingroup SystemInit
    *
    * This function will initialize system timer thread
    */
    void rt_system_timer_thread_init(void)
  • 定时器控制块中含有定时器相关的重要参数,在定时器各种状态间起到纽带的作用。对定时器的操作包含:创建 / 初始化定时器、启动定时器、运行定时器、删除 / 脱离定时器,所有定时器在定时超时后都会从定时器链表中被移除,而周期性定时器会在它再次启动时被加入定时器链表,这与定时器参数设置相关。在每次的操作系统时钟中断发生时,都会对已经超时的定时器状态参数做改变。

创建和删除定时器

  • /**
    * This function will create a timer
    *
    * @param name the name of timer
    * @param timeout the timeout function
    * @param parameter the parameter of timeout function
    * @param time the tick of timer
    * @param flag the flag of timer
    *
    * @return the created timer object
    */
    rt_timer_t rt_timer_create(const char *name,
    void (*timeout)(void *parameter),
    void *parameter,
    rt_tick_t time,
    rt_uint8_t flag)
  • 在 rtdef.h 中定义了函数参数flag支持的宏

  • #define RT_TIMER_FLAG_ONE_SHOT          0x0             /**< one shot timer */
    #define RT_TIMER_FLAG_PERIODIC 0x2 /**< periodic timer */
    #define RT_TIMER_FLAG_HARD_TIMER 0x0 /**< hard timer,the timer's callback function will be called in tick isr. */
    #define RT_TIMER_FLAG_SOFT_TIMER 0x4 /**< soft timer,the timer's callback function will be called in timer thread. */
  • 上面 2 组值可以以 “或” 逻辑的方式赋给 flag;

  • 当指定的 flag 为 RT_TIMER_FLAG_HARD_TIMER 时,如果定时器超时,定时器的回调函数将在时钟中断的服务例程上下文中被调用;
  • 当指定的 flag 为 RT_TIMER_FLAG_SOFT_TIMER 时,如果定时器超时,定时器的回调函数将在系统时钟 timer 线程的上下文中被调用。
  •  
  • 调用这个函数接口后,系统会把这个定时器从 rt_timer_list 链表中删除,然后释放相应的定时器控制块占有的内存;
  • /**
    * This function will delete a timer and release timer memory
    *
    * @param timer the timer to be deleted
    *
    * @return the operation status, RT_EOK on OK; RT_ERROR on error
    */
    rt_err_t rt_timer_delete(rt_timer_t timer)

初始化和脱离定时器

  • 当选择静态创建定时器时,可利用 rt_timer_init 接口来初始化该定时器使用该函数接口时会初始化相应的定时器控制块,初始化相应的定时器名称,定时器超时函数等;
  • /**
    * This function will initialize a timer, normally this function is used to
    * initialize a static timer object.
    *
    * @param timer the static timer object
    * @param name the name of timer
    * @param timeout the timeout function
    * @param parameter the parameter of timeout function
    * @param time the tick of timer
    * @param flag the flag of timer
    */
    void rt_timer_init(rt_timer_t timer,
    const char *name,
    void (*timeout)(void *parameter),
    void *parameter,
    rt_tick_t time,
    rt_uint8_t flag)
  • 当一个静态定时器不需要再使用时,可以使用下面的函数接口:

  • /**
    * This function will detach a timer from timer management.
    *
    * @param timer the static timer object
    *
    * @return the operation status, RT_EOK on OK; RT_ERROR on error
    */
    rt_err_t rt_timer_detach(rt_timer_t timer)

启动和停止定时器

  • 当定时器被创建或者初始化以后,并不会被立即启动,必须在调用启动定时器函数接口后,才开始工作;
  • 调用定时器启动函数接口后,定时器的状态将更改为激活状态(RT_TIMER_FLAG_ACTIVATED),并按照超时顺序插入到 rt_timer_list 队列链表中;
  • /**
    * This function will start the timer
    *
    * @param timer the timer to be started
    *
    * @return the operation status, RT_EOK on OK, -RT_ERROR on error
    */
    rt_err_t rt_timer_start(rt_timer_t timer)
  • 启动定时器以后,若想使它停止,可以使用函数

  • /**
    * This function will stop the timer
    *
    * @param timer the timer to be stopped
    *
    * @return the operation status, RT_EOK on OK, -RT_ERROR on error
    */
    rt_err_t rt_timer_stop(rt_timer_t timer)

控制定时器

  • RT-Thread 也额外提供了定时器控制函数接口,以获取或设置更多定时器的信息;
  • /**
    * This function will get or set some options of the timer
    *
    * @param timer the timer to be get or set
    * @param cmd the control command
    * @param arg the argument
    *
    * @return RT_EOK
    */
    rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
  • 在rtdef.h中定义了 函数参数cmd支持的宏

  • #define RT_TIMER_CTRL_SET_TIME          0x0             /**< set timer control command */
    #define RT_TIMER_CTRL_GET_TIME 0x1 /**< get timer control command */
    #define RT_TIMER_CTRL_SET_ONESHOT 0x2 /**< change timer to one shot */
    #define RT_TIMER_CTRL_SET_PERIODIC 0x3 /**< change timer to periodic */

定时器应用

  • 创建两个动态定时器,一个是单次定时,一个是周期性定时并让周期定时器运行一段时间后停止运行
  • #include <rtthread.h>
    
    /* 定时器的控制块 */
    static rt_timer_t timer1;
    static rt_timer_t timer2;
    static int cnt = ; /* 定时器 1 超时函数 */
    static void timeout1(void *parameter)
    {
    rt_kprintf("periodic timer is timeout %d\n", cnt); /* 运行第 10 次,停止周期定时器 */
    if (cnt++>= )
    {
    rt_timer_stop(timer1);
    rt_kprintf("periodic timer was stopped! \n");
    }
    } /* 定时器 2 超时函数 */
    static void timeout2(void *parameter)
    {
    rt_kprintf("one shot timer is timeout\n");
    } int timer_sample(void)
    {
    /* 创建定时器 1 周期定时器 */
    timer1 = rt_timer_create("timer1", timeout1,
    RT_NULL, ,
    RT_TIMER_FLAG_PERIODIC); /* 启动定时器 1 */
    if (timer1 != RT_NULL) rt_timer_start(timer1); /* 创建定时器 2 单次定时器 */
    timer2 = rt_timer_create("timer2", timeout2,
    RT_NULL, ,
    RT_TIMER_FLAG_ONE_SHOT); /* 启动定时器 2 */
    if (timer2 != RT_NULL) rt_timer_start(timer2);
    return ;
    } /* 导出到 msh 命令列表中 */
    MSH_CMD_EXPORT(timer_sample, timer sample);
  • 运行结果

  •  \ | /
    - RT - Thread Operating System
    / | \ 3.1. build Aug
    - Copyright by rt-thread team
    msh >timer_sample
    msh >periodic timer is timeout
    periodic timer is timeout
    one shot timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer was stopped!
  • 初始化 2 个静态定时器,一个是单次定时,一个是周期性的定时

  • #include <rtthread.h>
    
    /* 定时器的控制块 */
    static struct rt_timer timer1;
    static struct rt_timer timer2;
    static int cnt = ; /* 定时器 1 超时函数 */
    static void timeout1(void* parameter)
    {
    rt_kprintf("periodic timer is timeout\n");
    /* 运行 10 次 */
    if (cnt++>= )
    {
    rt_timer_stop(&timer1);
    }
    } /* 定时器 2 超时函数 */
    static void timeout2(void* parameter)
    {
    rt_kprintf("one shot timer is timeout\n");
    } int timer_static_sample(void)
    {
    /* 初始化定时器 */
    rt_timer_init(&timer1, "timer1", /* 定时器名字是 timer1 */
    timeout1, /* 超时时回调的处理函数 */
    RT_NULL, /* 超时函数的入口参数 */
    , /* 定时长度,以 OS Tick 为单位,即 10 个 OS Tick */
    RT_TIMER_FLAG_PERIODIC); /* 周期性定时器 */
    rt_timer_init(&timer2, "timer2", /* 定时器名字是 timer2 */
    timeout2, /* 超时时回调的处理函数 */
    RT_NULL, /* 超时函数的入口参数 */
    , /* 定时长度为 30 个 OS Tick */
    RT_TIMER_FLAG_ONE_SHOT); /* 单次定时器 */ /* 启动定时器 */
    rt_timer_start(&timer1);
    rt_timer_start(&timer2);
    return ;
    }
    /* 导出到 msh 命令列表中 */
    MSH_CMD_EXPORT(timer_static_sample, timer_static sample);
  • 仿真运行结果如下:

  • \ | /
    - RT - Thread Operating System
    / | \ 3.1. build Aug
    - Copyright by rt-thread team
    msh >timer_static_sample
    msh >periodic timer is timeout
    periodic timer is timeout
    one shot timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout
    periodic timer is timeout

高精度延时

  • RT-Thread 定时器的最小精度是由系统时钟节拍所决定的(1 OS Tick = 1/RT_TICK_PER_SECOND 秒,RT_TICK_PER_SECOND 值在 rtconfig.h 文件中定义),定时器设定的时间必须是 OS Tick 的整数倍。当需要实现更短时间长度的系统定时时,例如 OS Tick 是 10ms,而程序需要实现 1ms 的定时或延时,这种时候操作系统定时器将不能够满足要求,只能通过读取系统某个硬件定时器的计数器或直接使用硬件定时器的方式。
  • 在 Cortex-M 系列中,SysTick 已经被 RT-Thread 用于作为 OS Tick 使用,它被配置成 1/RT_TICK_PER_SECOND 秒后触发一次中断的方式,中断处理函数使用 Cortex-M3 默认的 SysTick_Handler 名字。在 Cortex-M3 的 CMSIS(Cortex Microcontroller Software Interface Standard)规范中规定了 SystemCoreClock 代表芯片的主频,所以基于 SysTick 以及 SystemCoreClock,我们能够使用 SysTick 获得一个精确的延时函数,如下例所示,Cortex-M3 上的基于 SysTick 的精确延时(需要系统在使能 SysTick 后使用):
  • 示例:
  • #include <board.h>
    void rt_hw_us_delay(rt_uint32_t us)
    {
    rt_uint32_t delta;
    /* 获得延时经过的 tick 数 */
    us = us * (SysTick->LOAD/(/RT_TICK_PER_SECOND));
    /* 获得当前时间 */
    delta = SysTick->VAL;
    /* 循环获得当前时间,直到达到指定的时间后退出循环 */
    while (delta - SysTick->VAL< us);
    }

参考

  • 《RT-Thread 编程指南》

RT-Thread--时间管理的更多相关文章

  1. CMSIS-RTOS 时间管理之时间延迟Time Delay

    时间管理 Time Management 此RTOS除了可以把你的应用代码作为线程运行,它还可以提供一些时间服务功能,使用这些功能你就可以访问RTOS的一些系统调用. 时间延迟Time Delay 在 ...

  2. PMP备考_第六章_项目时间管理

    项目时间管理 前言 项目时间管理是项目管理中最难的一个环节,与个人时间管理类似,团体的效率如果管理不当,是低于个人效率的,为了管理好时间,从预估,执行到反馈均需要严格的分析和处理.如果制定的计划是无法 ...

  3. Google内部邮件:如何进行高效的时间管理能量波动图

    Google内部邮件:如何进行高效的时间管理能量波动图 发布时间: 2015-12-16 22:16:50| 阅读数:462 views 最近,我给团队内部写了一封简单的邮件.主要内容是征询他们,如何 ...

  4. linux 时间管理——概念、注意点(一)【转】

    转自:http://www.cnblogs.com/openix/p/3324243.html 参考:1.http://bbs.eyeler.com/thread-69-1-1.html        ...

  5. μC/OS-Ⅲ系统的时间管理函数和定时器

    一.时间管理函数 μC/OS-Ⅲ系统提供一些列时间管理服务函数: 1.OSTimeDly():任务延时n个时钟节拍. 2.OSTimeDlyHMSM():任务延时指定的时间,采用“时:分:秒:毫秒”方 ...

  6. 时间管理的若干Tips

    时间管理的若干Tips 记下来 再好的记性也不如一支笔与一个本子. 买一支好点的笔于一个好点的本子,让自己有书写的欲望,将todo事项记下来. 小目标 太大太远的目标会使人气馁.通过将大目标分解再分解 ...

  7. 4D时间管理

    时间管理的4D原则   时间矩阵:每个人,每天要做的事情大致分为四类:一类:必须做的,不做会产生不良后果的二类:需要做的,为将来或者今后阶段做准备工作的三类:可以不做,但又不得不做的,例如通过简约形式 ...

  8. 【uTenux实验】时间管理(系统时间/周期性处理/警报处理)

    1.系统时间管理 系统时间管理函数用来对系统时间进行操作,是OS的一个基础性的东西.个人认为,设置系统时间和获取系统时间对OS来说基本是可有可无的. uTenux提供了三个系统时间相关API.分别用于 ...

  9. GTD时间管理---非行动性

    一:行动性和非行动有什么区别? 1:非行动性:指的是收集箱中的信息,是先存储后使用,这些信息平时都处于冬眠状态,只是要用到的时候能够找到它就好了.(偏向于处理生活) 2:行动性: 指的是收集箱中的信息 ...

  10. (笔记)Linux内核学习(八)之定时器和时间管理

    一 内核中的时间观念 内核在硬件的帮助下计算和管理时间.硬件为内核提供一个系统定时器用以计算流逝的时间.系 统定时器以某种频率自行触发,产生时钟中断,进入内核时钟中断处理程序中进行处理. 墙上时间和系 ...

随机推荐

  1. 【c# 学习笔记】c#中的语句

    1.条件语句: if语句: bool condition = true; if (condition) { } else if (condition) { } else { } switch语句: b ...

  2. Burpsuite—渗透测试神器

    Google浏览器插件---SwitchyOmega Firefox浏览器插件---SwitchyOmega hosts代理工具---SwitchHosts[右击使用管理员权限打开] 双击burp-l ...

  3. JS 报错(intermediate value)(...) is not a function

  4. [Android] 分析一个CalledFromWrongThreadException崩溃

    1 问题描述 问题本身比较清晰简单,但推敲的过程中发现了不少有意思的东西. 在C++ SDK回调JNI至Java Observer函数中,直接操作了UI界面textView.setText(msg), ...

  5. 设计模式的好书 -- ongoing

    1 设计模式--可复用面向对象软件的基础  Erich Gamma. Richard Helm  -- 已经下载了/baiduNetDisk   Design Patterns --- Element ...

  6. Java 最常见 200+ 面试题 + 全解析

    本文分为十九个模块,分别是: Java 基础.容器.多线程.反射.对象拷贝.Java Web .异常.网络.设计模式.Spring/Spring MVC.Spring Boot/Spring Clou ...

  7. C++基础--函数重载

    函数重载定义: 在相同的作用域中具有相同的函数名而函数形参列表(参数类型.参数个数.参数顺序)不同的两个函数,称之为函数重载.注意:函数返回值类型并不是重载的条件. 函数重载优点: 可以使用相同的函数 ...

  8. js — 字符串

    目录 1. 拼接字符串 2. 获取字符的方法 3. 字符串操作方法(切片) 4. 字符串位置方法 - 索引 5. trim()方法 6. 字符串大小写转换方法 字符串 typeof 用于校验当前变量的 ...

  9. double check

    http://www.cnblogs.com/limingluzhu/p/5156659.html http://blog.csdn.net/chenchaofuck1/article/details ...

  10. Nginx学习笔记(三):Nginx 请求处理

    Request Nginx 中的 ngx_http_request_t 是对一个 http 请求的封装: 一个 http 请求包含:请求行.请求头.请求体,响应行.响应头.响应体 Nginx 处理请求 ...