任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。
RT-Thread 中,时钟节拍的长度可以根据 RT_TICK_PER_SECOND 的定义来调整。 rtconfig.h配置文件中定义:
获取时钟节拍
#include
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include
int main(void)
{
int i=0;
rt_uint32_t tick;
for(i=0;i<10;i++)
{
tick=rt_tick_get();
rt_kprintf("tick:%u\n",tick);
rt_thread_mdelay(500);
}
}
定时器,是指从指定的时刻开始,经过一定的指定时间后触发一个事件,定时器有硬件定时器和软件定时器之分:
硬件定时器: 芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
软件定时器: 由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。
RT-Thread操作系统提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时数值必须是OS Tick的整数倍。
RT-Thread 的定时器提供两类定时器机制:
第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。
第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止,否则将永远持续执行下去
根据定时器超时函数执行时所处的上下文环境,RT-Thread的定时器可以分为HARD_TIMER模式和SOFT_TIMER模式。
HARD_TIMER模式:中断上下文
定时器超时函数的要求:执行时间应该尽量短,执行时不应导致当前上下文挂起、等待。例如在中断上下文中执行的超时函数它不应该试图去申请动态内存、释放动态内存等
SOFT_TIMER模式:线程上下文
该模式被启用后,系统会在初始化时创建一个 timer 线程,然后 SOFT_TIMER 模式的定时器超时函数在都会在timer线程的上下文环境中执行。
在 RT-Thread 定时器模块中维护着两个重要的全局变量:
1. 当前系统经过的 tick 时间 rt_tick(当硬件定时器中断来临时,它将加 1) ;
2. 定时器链表 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 随着硬件定时器的触发一直在增长(每一次硬件定时器中断来临,rt_tick 变量会加 1) ,50个tick以后,rt_tick从20增长到70,与Timer1的timeout值相等,这时会触发与Timer1定时器相关联的超时函数,同时将Timer1从rt_timer_list链表上删除。同理,100个tick和500个tick过去后,与Timer2 和 Timer3 定时器相关联的超时函数会被触发,接着将 Time2 和 Timer3 定时器从 rt_timer_list链表中删除。
如果系统当前定时器状态在 10 个 tick 以后(rt_tick=30)有一个任务新创建了一个 tick 值为 300 的Timer4定时器,由于Timer4定时器的timeout=rt_tick+300=330,因此它将被插入到Timer2和Timer3定时器中间,形成如下图所示链表结构:
/**
* 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
* #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. */
* @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)
/**
* 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)
#include
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include
rt_timer_t tm1=NULL;
void tm1_callback(void *parameter)
{
rt_kprintf("tm1_callback running...\n");
}
int main(void)
{
tm1=rt_timer_create("tm1_demo", tm1_callback, NULL, 30000,\
RT_TIMER_FLAG_PERIODIC|RT_TIMER_FLAG_SOFT_TIMER);
if(tm1==RT_NULL)
{
LOG_E("rt_timer1_create failed...\n");
return -ENOMEM;
}
LOG_D("rt_timer1_create successed...\n");
}
启动定时器
#include
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include
rt_timer_t tm1=NULL;
void tm1_callback(void *parameter)
{
rt_kprintf("tm1_callback running...\n");
}
int main(void)
{
//动态创建定时器
tm1=rt_timer_create("tm1_demo", tm1_callback, NULL, 3000,\
RT_TIMER_FLAG_PERIODIC|RT_TIMER_FLAG_SOFT_TIMER);
if(tm1==RT_NULL)
{
LOG_E("rt_timer1_create failed...\n");
return -ENOMEM;
}
LOG_D("rt_timer1_create successed...\n");
rt_timer_start(tm1);
}
/**
* This function will initialize a timer, normally this function is used to
* initialize a static timer object.
** @param timer the static timer object (typedef struct rt_timer *rt_timer_t;)
* @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)
#include
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include
struct rt_timer tm2;
void tm2_callback(void *parameter)
{
rt_kprintf("tm2_callback running...\n");
}
int main(void)
{
//静态创建定时器
rt_timer_init(&tm2, "tm2_demo", tm2_callback, NULL, 3000,\
RT_TIMER_FLAG_PERIODIC|RT_TIMER_FLAG_SOFT_TIMER);
rt_timer_start(&tm2);
}
/**
* 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
* #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 */
* @return RT_EOK
*/
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
rt_timer_control(&tm2, RT_TIMER_CTRL_SET_ONESHOT, NULL);
整体实现
#include
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include
rt_timer_t tm1;
struct rt_timer tm2;
void tm1_callback(void *parameter)
{
rt_kprintf("tm1_callback running...\n");
}
int flags=0;
void tm2_callback(void *parameter)
{
flags++;
if(flags==5)
{
rt_timer_control(&tm2, RT_TIMER_CTRL_SET_ONESHOT, NULL);
flags=0;
}
rt_tick_t timeout=1000;
rt_timer_control(&tm2, RT_TIMER_CTRL_SET_TIME, (void*)&timeout);
rt_kprintf("tm2_callback running...\n");
rt_kprintf("%d\n",rt_tick_get());
}
int main(void)
{
//动态创建定时器
tm1=rt_timer_create("tm1_demo", tm1_callback, NULL, 3000,\
RT_TIMER_FLAG_PERIODIC|RT_TIMER_FLAG_SOFT_TIMER);
if(tm1==RT_NULL)
{
LOG_E("rt_timer1_create failed...\n");
return -ENOMEM;
}
LOG_D("rt_timer1_create successed...\n");
// rt_timer_start(tm1);
//静态创建定时器
rt_timer_init(&tm2, "tm2_demo", tm2_callback, NULL, 3000,\
RT_TIMER_FLAG_PERIODIC|RT_TIMER_FLAG_SOFT_TIMER);
rt_timer_start(&tm2);
return 0;
}