任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。时钟节拍是特定的周期性中断,这个中断可以看做是系统心跳,中断之间的时间间隔取决于不同的应用,一般是 1ms–100ms,时钟节拍率越快,系统的额外开销就越大,从系统启动开始计数的时钟节拍数称为系统时间。
RT-Thread 中,时钟节拍的长度可以根据 RT_TICK_PER_SECOND 的定义来调整,等于 1/RT_TICK_PER_SECOND 秒。
时 钟 节 拍 由 配 置 为 中 断 触 发 模 式 的 硬 件 定 时 器 产 生, 当中 断 到 来 时, 将 调 用 一 次:void
rt_tick_increase(void),通知操作系统已经过去一个系统时钟;不同硬件定时器中断实现都不同。
全局变量 rt_tick 在每经过一个时钟节拍时,值就会加 1,rt_tick 的值表示了系统从启动开始总共经过的时钟节拍数,即系统时间。此外,每经过一个时钟节拍时,都会检查当前线程的时间片是否用完,以及是否有定时器超时。
由于全局变量 rt_tick 在每经过一个时钟节拍时,值就会加 1,通过调用 rt_tick_get 会返回当前rt_tick 的值,即可以获取到当前的时钟节拍值。此接口可用于记录系统的运行时间长短,或者测量某任务运行的时间。接口函数如下:
rt_tick_t rt_tick_get(void);
定时器,是指从指定的时刻开始,经过一定的指定时间后触发一个事件,定时器有硬件定时器和软件定时器之分:
1)硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
2)软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。(重点)
RT-Thread 操作系统提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时数值必须是 OS Tick 的整数倍。
模式有单次触发、周期触发,另外,根据超时函数执行时所处的上下文环境,RT-Thread 的定时器可以分为 HARD_TIMER 模式(默认)与 SOFT_TIMER 模式。
HARD_TIMER 模式的定时器超时函数在中断上下文环境中执行,可以在初始化 / 创建定时器时使用参数 RT_TIMER_FLAG_HARD_TIMER 来指定。RT-Thread 定时器默认的方式是 HARD_TIMER 模式,即定时器超时后,超时函数是在系统时钟中断的上下文环境中运行的。在中断上下文中的执行方式决定了定时器的超时函数不应该调用任何会让当前
上下文挂起的系统函数;也不能够执行非常长的时间,否则会导致其他中断的响应时间加长或抢占了其他
线程执行的时间。
SOFT_TIMER 模式可配置,通过宏定义 RT_USING_TIMER_SOFT 来决定是否启用该模式。该模式被启用后,系统会在初始化时创建一个 timer 线程,然后 SOFT_TIMER 模式的定时器超时函数在都会在 timer 线程的上下文环境中执行。可以在初始化 / 创建定时器时使用参数RT_TIMER_FLAG_SOFT_TIMER 来指定设置 SOFT_TIMER 模式。
1)初始化:在系统启动时需要初始化定时器管理系统。可以通过下面的函数接口完成:
void rt_system_timer_init(void); //默认是HARD_TIMER模式
void rt_system_timer_thread_init(void); //使用 SOFT_TIMER模式
2)动态创建与删除
rt_timer_t rt_timer_create(const char* name,
void (*timeout)(void* parameter),
void* parameter,
rt_tick_t time, //超时时间
rt_uint8_t flag); //参数,决定单次或周期,soft或hard
//删除
rt_err_t rt_timer_delete(rt_timer_t timer);
3)静态初始化和脱离
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);
//脱离
rt_err_t rt_timer_detach(rt_timer_t timer);
4)启动和停止,需要启动才工作。
rt_err_t rt_timer_start(rt_timer_t timer);
//停止
rt_err_t rt_timer_stop(rt_timer_t timer);
5)控制定时器
rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void* arg);
cmd 用于控制定时器的命令,当前支持四个命令,分别是设置定时时间,查看定时时间,设置单次触发,设置周期触发 。arg是与 cmd 相对应的控制命令参数比如,cmd 为设定超时时间时,就可以将超时时间参数通过arg 进行设定。
RT-Thread 定时器的最小精度是由系统时钟节拍所决定的(1 OS Tick = 1/RT_TICK_PER_SECOND秒),定时器设定的时间必须是 OS Tick 的整数倍。当需要实现更短时间长度的系统定时时,这种时候操作系统定时器将不能够满足要求,只能通过读取系统某个硬件定时器的计数器或直接使用硬件定时器的方式。
在 Cortex-M 系列中,SysTick 已经被 RT-Thread 用于作为 OS Tick 使用,它被配置成 1/RT_TICK_PER_SECOND 秒后触发一次中断的方式,中断处理函数使用 Cortex-M3 默认的 Sy-sTick_Handler 名字。在 Cortex-M3 的 CMSIS(Cortex Microcontroller Software Interface Standard)规范中规定了 SystemCoreClock 代表芯片的主频,所以基于 SysTick 以及 SystemCoreClock,我们能够使用SysTick 获得一个精确的延时函数,如下例所示,Cortex-M3 上的基于 SysTick 的精确延时(需要系统在使能 SysTick 后使用):
#include
void rt_hw_us_delay(rt_uint32_t us)
{
rt_uint32_t delta;
/* 获 得 延 时 经 过 的 tick 数 */
us = us * (SysTick->LOAD/(1000000/RT_TICK_PER_SECOND));
/* 获 得 当 前 时 间 */
delta = SysTick->VAL;
/* 循 环 获 得 当 前 时 间, 直 到 达 到 指 定 的 时 间 后 退 出 循环 */
while (delta - SysTick->VAL< us);
}
其中入口参数 us 指示出需要延时的微秒数目,这个函数只能支持低于 1 OS Tick 的延时,否则 SysTick
会出现溢出而不能够获得指定的延时时间。