RT-Thread版本:4.0.5
MCU型号:STM32F103RCT6(ARM Cortex-M3 内核)
RT-Thread 的定时器提供两类定时器机制:
当系统时钟节拍到达定时器设定的超时时间时会回调一个函数,此函数即为超时函数,根据超时函数执行的上下文环境不同,RT-Thread定时器分为硬件定时器与软件定时器,如下图所示:
可以通过下列宏配置定时器模式及其状态:
#define RT_TIMER_FLAG_DEACTIVATED 0x0 /* 非活动状态 */
#define RT_TIMER_FLAG_ACTIVATED 0x1 /* 活动状态 */
#define RT_TIMER_FLAG_ONE_SHOT 0x0 /* 单次触发定时器 */
#define RT_TIMER_FLAG_PERIODIC 0x2 /* 周期触发定时器 */
#define RT_TIMER_FLAG_HARD_TIMER 0x0 /* 硬件定时器,超时函数在tick中断服务例程中回调 */
#define RT_TIMER_FLAG_SOFT_TIMER 0x4 /* 软件定时器,超时函数在timer线程中回调 */
struct rt_object
{
char name[RT_NAME_MAX]; /* 内核对象名 */
rt_uint8_t type; /* 内核对象类型 */
rt_uint8_t flag; /* 内核对象标志位 */
rt_list_t list; /* 内核对象链表结点 */
};
struct rt_timer
{
struct rt_object parent; /* 继承父类属性与方法 */
rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL]; /* 定时器链表节点 */
void (*timeout_func)(void *parameter); /* 定时器超时调用的函数 */
void *parameter; /* 超时函数的参数 */
rt_tick_t init_tick; /* 定时器初始超时节拍数 */
rt_tick_t timeout_tick; /* 定时器实际超时时的节拍数 */
};
typedef struct rt_timer *rt_timer_t;
RT-Thread采用面向对象的思想设计的代码,所有内核对象都继承rt_object
结构体,定时器模式及其状态通过timer->parent.flag
成员修改:
创建的定时器会以超时时间排序(升序)的方式插入到定时器链表中(软件与硬件定时器采用不同的链表),因此该链表是一个有序链表
,因此RT-Thread采用跳变算法来加快搜索链表元素的速度,使得查找元素时链表的时间复杂度由O(n) -> O(logn)
。
【官方举例】
一个有序的链表,如下图所示,从该有序链表中搜索元素 {13, 39},需要比较的次数分别为 {3, 5},总共比较的次数为 3 + 5 = 8 次。
使用跳表算法后可以采用类似二叉搜索树的方法,把一些节点提取出来作为索引,得到如下图所示的结构:
在这个结构里把 {3, 18,77} 提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。还可以再从一级索引提取一些元素出来,作为二级索引,这样更能加快元素搜索:
可以看出,跳表算法是一种“空间换时间”的算法,跳表的最大层数为log2n - 1
层(n为链表长度),在 RT-Thread 中通过宏定义RT_TIMER_SKIP_LIST_LEVEL
来配置跳表的层数,默认为 1,表示采用一级有序链表图的有序链表算法,每增加一,表示在原链表基础上增加一级索引。
RT-Thread 定时器默认的方式就是HARD_TIMER
模式,线程对象都有一个内置定时器,其默认就是此模式。所谓的默认就是timer->parent.flag
成员为0时的标志状态。
硬件定时器超时函数工作在中断上下文环境,因此对超时函数的要求与中断服务例程的要求相同:
struct rt_list_node
{
struct rt_list_node *next; /**< point to next node. */
struct rt_list_node *prev; /**< point to prev node. */
};
typedef struct rt_list_node rt_list_t; /**< Type for lists. */
static rt_list_t _timer_list[RT_TIMER_SKIP_LIST_LEVEL];
后期添加定时器,按定时器超时时间顺序进行排序插入。如果两个定时器超时时间相同, 会按FIFO顺序插入,即先初始化的定时器位于链表前面。
void rt_system_timer_init(void)
{
int i;
for (i = 0; i < sizeof(_timer_list) / sizeof(_timer_list[0]); i++)
{
rt_list_init(_timer_list + i);
}
}
【功能】
初始化硬件定时器链表结点,将其前驱指针和后继指针均指向表头结点。
/**
* @brief 该函数用于扫描系统定时器列表,当有超时事件发生时
* 就调用对应的超时函数
*
* @note 该函数在操作系统定时器中断中被调用
*/
void rt_timer_check(void)
{
struct rt_timer *t;
rt_tick_t current_tick;
register rt_base_t level;
rt_list_t list;
rt_list_init(&list);
/* 获取系统时基计数器 rt_tick 的值 */
current_tick = rt_tick_get();
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 系统定时器列表不为空,则扫描定时器列表 */
while (!rt_list_isempty(&_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
/* 获取第一个节点定时器的地址 */
t = rt_list_entry(_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, // 节点地址
struct rt_timer, // 结点所在的父结构的数据类型
row[RT_TIMER_SKIP_LIST_LEVEL - 1]); // 节点在父结构的成员名
/* 最大超时时间不能大>= RT_TICK_MAX / 2 */
// t->timeout_tick当定时器重新启动时都会刷新
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t)); // 回调进入定时器钩子函数
/* 将定时器从定时器列表移除 */
_timer_remove(t);
if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
{
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
/* 将定时器添加到临时列表 */
rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
/* 调用超时函数 */
t->timeout_func(t->parameter);
/* 重新获取 tick */
current_tick = rt_tick_get();
RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t)); // 回调退出定时器钩子函数
/* 检测timer对象处于分离还是启动状态 */
if (rt_list_isempty(&list))
{
continue;
}
rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* 启动定时器 */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
}
else break;
}
/* 开中断 */
rt_hw_interrupt_enable(level);
}
rt_timer_check()
函数用于检查系统硬件定时器链表,如果有定时器超时,将调用相应的超时函数,且所有定时器在定时超时后都会从定时器链表中被移除,若是周期性定时器则会在它再次启动时重新加入到定时器链表。该函数在rt_tick_increase()
中被调用,具体查看[时钟节拍的获取与线程时间片轮转调度的实现]一文。
软件定时器模式可通过配置宏定义RT_USING_TIMER_SOFT
来决定是否启用该模式,该模式被启用后,系统会在初始化时创建一个 timer 线程,SOFT_TIMER
模式的定时器超时函数在都会在 timer 线程的上下文环境中执行,因此与线程一下需要注意以下几点:
static rt_list_t _soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
static rt_list_t _soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; // 软件定时器
static struct rt_thread _timer_thread; // 定义timer线程
ALIGN(RT_ALIGN_SIZE) // 四字节排列
static rt_uint8_t _timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE]; // 定义timer线程栈大小
/**
* @brief 这个函数将初始化系统定时器线程
*/
void rt_system_timer_thread_init(void)
{
#ifdef RT_USING_TIMER_SOFT // 是否使用软件定时器的宏开关
int i;
/* 初始化软件定时器链表 */
for (i = 0;
i < sizeof(_soft_timer_list) / sizeof(_soft_timer_list[0]);
i++)
{
rt_list_init(_soft_timer_list + i);
}
/* 创建timer线程 */
rt_thread_init(&_timer_thread,
"timer",
_timer_thread_entry,
RT_NULL,
&_timer_thread_stack[0],
sizeof(_timer_thread_stack),
RT_TIMER_THREAD_PRIO,
10);
/* 启动timer线程 */
rt_thread_startup(&_timer_thread);
#endif /* RT_USING_TIMER_SOFT */
}
软件定时器使用了系统的一个链表和一个线程资源,软件定时器线程的优先级通过宏RT_TIMER_THREAD_PRIO
设置,默认为4,定时器线程的堆栈大小 通过宏RT_TIMER_THREAD_STACK_SIZE
设置,默认为 512 个字节。
static void _timer_thread_entry(void *parameter)
{
rt_tick_t next_timeout;
while (1)
{
/* 获取软件定时器列表中下一个定时器的到达时间 */
next_timeout = _timer_list_next_timeout(_soft_timer_list);
if (next_timeout == RT_TICK_MAX)
{
/* 如果没有软件定时器,则挂起线程自身 */
rt_thread_suspend(rt_thread_self());
rt_schedule();
}
else
{
rt_tick_t current_tick;
/* 获取当前系统时间 */
current_tick = rt_tick_get();
if ((next_timeout - current_tick) < RT_TICK_MAX / 2)
{
/* 计算下一个定时器溢出时间与当前时间的间隔 */
next_timeout = next_timeout - current_tick;
rt_thread_delay(next_timeout);
}
}
/* 检查软件定时器列表 */
rt_soft_timer_check();
}
}
_timer_list_next_timeout
函数会返回RT_TICK_MAX
,然后会将定时器线程自身挂起。当其他执行单元调用rt_timer_start
函数启动软件定时器时,会将该定时器插入到软件定时器列表,如果发现定时器线程已挂起,则会将其唤醒。#define RT_SOFT_TIMER_IDLE 1
#define RT_SOFT_TIMER_BUSY 0
/* 软件定时器状态 */
static rt_uint8_t _soft_timer_status = RT_SOFT_TIMER_IDLE;
/**
* @brief 函数会检查软件定时器列表,如果出现超时事件,则会调用相应的超时函数
*/
void rt_soft_timer_check(void)
{
rt_tick_t current_tick;
struct rt_timer *t;
register rt_base_t level;
rt_list_t list;
rt_list_init(&list);
/* 关中断 */
level = rt_hw_interrupt_disable();
while (!rt_list_isempty(&_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
/* 获取第一个节点定时器的地址 */
t = rt_list_entry(_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer,
row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
current_tick = rt_tick_get();
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));
/* 获取第一个节点定时器的地址 */
_timer_remove(t);
if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
{
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
/* 将定时器添加到临时列表 */
rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
_soft_timer_status = RT_SOFT_TIMER_BUSY; // 将软件定时器标记为忙碌状态
/* 开中断 */
rt_hw_interrupt_enable(level);
/* 调用超时函数 */
t->timeout_func(t->parameter);
RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
/* 关中断 */
level = rt_hw_interrupt_disable();
_soft_timer_status = RT_SOFT_TIMER_IDLE; // 取消标记软件定时器为忙碌状态
/* 检测timer对象处于分离还是启动状态 */
if (rt_list_isempty(&list))
{
continue;
}
rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
}
else break;
}
/* 开中断 */
rt_hw_interrupt_enable(level);
}
软件定时器与硬件定时器扫描函数内部实现基本一致,注意:无论是软件还是硬件定时器,其超时时间必须小于RT_TICK_MAX / 2
,其中RT_TICK_MAX = 0xffff ffff
)
打开RT_USING_HEAP
宏才能创建与删除动态对象。
/**
* @brief 该函数用于初始化一个定时器,通常该函数用于初始化一个动态的定时器
*
* @param name 定时器的名字
* @param timeout 超时函数
* @param parameter 超时函数形参
* @param time 定时器的超时时间
* @param flag 定时器的标志
*
* @return 成功创建返回定时器结构体指针
*/
rt_timer_t rt_timer_create(const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
struct rt_timer *timer;
/* 动态申请内存*/
timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
if (timer == RT_NULL)
{
return RT_NULL;
}
/* 定时器初始化 */
_timer_init(timer, timeout, parameter, time, flag);
return timer;
}
rt_timer_init
函数将定时器具体的初始化封装在了一个内部函数_timer_init
中:
static void _timer_init(rt_timer_t timer,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
int i;
/* 设置标志 */
timer->parent.flag = flag;
/* 先设置为非激活态 */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
timer->timeout_func = timeout;
timer->parameter = parameter;
/* 初始化 定时器实际超时时的系统节拍数 */
timer->timeout_tick = 0;
/* 初始化 定时器需要超时的节拍数 */
timer->init_tick = time;
/* 初始化定时器链表的内置节点 */
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_init(&(timer->row[i]));
}
}
rt_err_t rt_timer_delete(rt_timer_t timer)
{
register rt_base_t level;
/* 检查定时器 */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE);
level = rt_hw_interrupt_disable();
/* 将定时器从链表中移除 */
_timer_remove(timer);
/* 将状态设为非激活态 */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_hw_interrupt_enable(level);
/* 从内核对象链表中移除并释放内存 */
rt_object_delete(&(timer->parent));
return RT_EOK;
}
_timer_remove
是内联函数:
rt_inline void _timer_remove(rt_timer_t timer)
{
int i;
/* 将定时器自身的节点从系统定时器列表 rt_timer_list 脱离 */
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_remove(&timer->row[i]);
}
}
调用rt_timer_delete
函数后,系统会把传入的定时器对象指针从 rt_timer_list 链表中删除,然后释放相应的定时器控制块占有的内存
/**
* @brief 该函数用于初始化一个定时器,通常该函数用于初始化一个静态的定时器
*
* @param timer 静态定时器对象
* @param name 定时器的名字
* @param timeout 超时函数
* @param parameter 超时函数形参
* @param time 定时器的超时时间
* @param flag 定时器的标志
*/
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_ASSERT(timer != RT_NULL);
/* 定时器对象初始化, 将定时器插入到系统对象容器列表 */
rt_object_init(&(timer->parent), RT_Object_Class_Timer, name);
/* 定时器初始化 */
_timer_init(timer, timeout, parameter, time, flag);
}
rt_err_t rt_timer_detach(rt_timer_t timer)
{
register rt_base_t level;
/* 检查定时器 */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(rt_object_is_systemobject(&timer->parent));
level = rt_hw_interrupt_disable();
_timer_remove(timer);
/* 将状态设为非激活态 */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_hw_interrupt_enable(level);
/* 从内核对象链表中移除,但不会释放内存 */
rt_object_detach(&(timer->parent));
return RT_EOK;
}
调用rt_timer_detach
函数会把定时器对象从内核对象容器中脱离,但是定时器对象所占有的内存不会被释放(静态对象也无法释放)
rt_err_t rt_timer_start(rt_timer_t timer)
{
unsigned int row_lvl;
rt_list_t *timer_list;
register rt_base_t level;
register rt_bool_t need_schedule;
rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL];
unsigned int tst_nr;
static unsigned int random_nr;
/* 检查定时器 */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
need_schedule = RT_FALSE;
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 将定时器从系统定时器列表移除 */
_timer_remove(timer);
/* 改变定时器的状态为非激活态 */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
/*
* 获取 timeout tick,
* 最大的 timeout tick 不能大于 RT_TICK_MAX/2
*/
RT_ASSERT(timer->init_tick < RT_TICK_MAX / 2);
timer->timeout_tick = rt_tick_get() + timer->init_tick;
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
/* 将定时器插入到软件定时器列表 */
timer_list = _soft_timer_list;
}
else
#endif /* RT_USING_TIMER_SOFT */
{
/* 将定时器插入到系统(硬件)定时器列表 */
timer_list = _timer_list;
}
/* 获取定时器列表第一条链表根节点地址 */
row_head[0] = &timer_list[0];
/* T_TIMER_SKIP_LIST_LEVEL 等于 1,这个循环只会执行一次 */
for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
for (; row_head[row_lvl] != timer_list[row_lvl].prev; // 遍历定时器链表(双向循环链表)
row_head[row_lvl] = row_head[row_lvl]->next)
{
struct rt_timer *t;
/* 获取定时器列表节点地址 */
rt_list_t *p = row_head[row_lvl]->next;
/* 根据节点地址获取父结构的指针 */
t = rt_list_entry(p, struct rt_timer, row[row_lvl]);
/* 如果两个定时器超时时间相同, 最好是最早插入的最先被调用
* 因此将新定时器插入到该定时器列表的尾部
*/
if ((t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
{
break;
}
}
/* T_TIMER_SKIP_LIST_LEVEL 等于 1, 判断条件永不为真, 即不使用跳表算法 */
if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
row_head[row_lvl + 1] = row_head[row_lvl] + 1;
}
/* random_nr 是一个静态变量,用于记录启动了多少个定时器 */
random_nr++;
tst_nr = random_nr;
/* 将定时器插入到定时器管理列表 */
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1], // 列表根节点地址
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); // 要被插入的节点的地址
/* RT_TIMER_SKIP_LIST_LEVEL 等于 1,该 for 循环永远不会执行 */
for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK))
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl]));
else
break;
/* Shift over the bits we have tested. Works well with 1 bit and 2
* bits. */
tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
}
/* 设置定时器标志位为激活态 */
timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
/* 检查timer线程是否准备好 */
if ((_soft_timer_status == RT_SOFT_TIMER_IDLE) &&
((_timer_thread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)) // 软件定时器链表为空时timer线程会挂起自身,让出CPU资源
{
/* 唤醒timer线程去调用软件定时器扫描函数 */
rt_thread_resume(&_timer_thread);
need_schedule = RT_TRUE;
}
}
#endif /* RT_USING_TIMER_SOFT */
/* 开中断 */
rt_hw_interrupt_enable(level);
if (need_schedule)
{
rt_schedule();
}
return RT_EOK;
}
调用rt_timer_start
函数,将定时器的状态将更改为激活状态RT_TIMER_FLAG_ACTIVATED
,并计算当前超时时间timer->timeout_tick = rt_tick_get() + timer->init_tick
;然后遍历定时器链表,根据timer->timeout_tick
超时时间大小,升序插入到定时器链表中。
同时在这里也可以看出只有使用了RT_USING_TIMER_SOFT
宏,才会判断定时器的flag是否为RT_TIMER_FLAG_SOFT_TIMER
软件定时器,如果是会执行两个操作:
_soft_timer_list
——软件定时器管理链表rt_err_t rt_timer_stop(rt_timer_t timer)
{
register rt_base_t level;
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
/* 只有 active 的定时器才能被停止,否则退出返回错误码 */
if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
return -RT_ERROR;
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
/* 关中断 */
level = rt_hw_interrupt_disable();
/* 将定时器从定时器列表删除 */
_timer_remove(timer);
/* 改变定时器的状态为非 active */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
/* 开中断 */
rt_hw_interrupt_enable(level);
return RT_EOK;
}
调用rt_timer_stop
函数,将定时器状态改为非激活态RT_TIMER_FLAG_DEACTIVATED
,并将其从定时器链表移除,不参与定时器超时检查。
/**
* @brief 该函数将获取或者设置定时器的一些选项
*
* @param timer 将要被设置或者获取的定时器
* @param cmd 控制命令
* @param arg 形参
*
* @return 控制状态
*/
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
{
register rt_base_t level;
/* timer check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
level = rt_hw_interrupt_disable();
switch (cmd)
{
case RT_TIMER_CTRL_GET_TIME:
*(rt_tick_t *)arg = timer->init_tick;
break;
case RT_TIMER_CTRL_SET_TIME:
timer->init_tick = *(rt_tick_t *)arg;
break;
case RT_TIMER_CTRL_SET_ONESHOT:
timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
break;
case RT_TIMER_CTRL_SET_PERIODIC:
timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
break;
case RT_TIMER_CTRL_GET_STATE:
if(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
{
/* 定时器处于运行或启动状态 */
*(rt_tick_t *)arg = RT_TIMER_FLAG_ACTIVATED;
}
else
{
/* 定时器处于停止状态 */
*(rt_tick_t *)arg = RT_TIMER_FLAG_DEACTIVATED;
}
break;
default:
break;
}
rt_hw_interrupt_enable(level);
return RT_EOK;
}
其中cmd有以下几种选择:
#define RT_TIMER_CTRL_SET_TIME 0x0 /* 设置定时器超时时间 */
#define RT_TIMER_CTRL_GET_TIME 0x1 /* 获得定时器超时时间 */
#define RT_TIMER_CTRL_SET_ONESHOT 0x2 /* 设置定时器为单次定时器 */
#define RT_TIMER_CTRL_SET_PERIODIC 0x3 /* 设置定时器为周期型定时器 */
#define RT_TIMER_CTRL_GET_STATE 0x4 /* 获取定时器状态:active or deactive */
注意:使用控制函数并且需要传入arg
时,定义的参数变量大小必须是4
字节,否则可能会直接卡死或者得到错误的数据。(个人猜测与字节对齐有关)
定时器钩子函数在扫描函数中调用。
void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer))
{
rt_timer_enter_hook = hook;
}
void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer))
{
rt_timer_exit_hook = hook;
}
static rt_timer_t timer1;
static struct rt_timer timer2;
static void timer1_callback(void *parameter)
{
rt_uint32_t active_status = 0; // 必须是四字节, 否则内存错误
rt_timer_control(timer1, RT_TIMER_CTRL_GET_STATE, &active_status);
rt_kprintf("[%u]tm1_flag: %u\n", rt_tick_get(), timer1->parent.flag);
rt_kprintf("[%u]tm1_active_status: %u\n", rt_tick_get(), active_status);
rt_kprintf("[%u]timer1_callback running...\n", rt_tick_get());
// rt_timer_delete(timer1); // 绝对不允许在ISR中申请与释放内存,否则断言报错卡在死循环
}
static void timer2_callback(void *parameter)
{
static rt_uint8_t count = 0;
if(++count == 10)
{
rt_timer_detach(&timer2); // 将状态设为非active,同时从定时器链表中脱离
// rt_timer_stop(&timer2); // 仅将状态改为非active
}
rt_kprintf("[%u]timer2_callback running...\n", rt_tick_get());
rt_thread_mdelay(10); // 挂起timer线程, 同时会把timer线程的定时器加入到软件定时器链表(这里仅做测试, 实际使用一般不允许调用延时函数)
}
static void tm_enter_hook(struct rt_timer *timer)
{
rt_kprintf("[%u]%s enter hook\n", rt_tick_get(), timer->parent.name);
}
static void tm_exit_hook(struct rt_timer *timer)
{
rt_kprintf("[%u]%s exit hook\n", rt_tick_get(), timer->parent.name);
}
static int tick_sample(void)
{
rt_uint32_t tm2_timeout = 1000; // 必须是四字节
// 设置定时器钩子
rt_timer_enter_sethook(tm_enter_hook);
rt_timer_exit_sethook(tm_exit_hook);
// 动态创建定时器
timer1 = rt_timer_create("tm1",
timer1_callback,
RT_NULL,
3000, // timeout 不能 >= RT_TICK_MAX / 2
RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_HARD_TIMER);
if(timer1 == NULL)
{
LOG_E("rt_timer1_create failed...\n");
return -RT_ENOMEM;
}
LOG_D("rt_timer1_create successed...\n");
rt_timer_start(timer1);
// 静态创建定时器
rt_timer_init(&timer2,
"tm2",
timer2_callback,
NULL,
5000,
RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
rt_timer_start(&timer2);
/* 重新设置tm2超时时间为1000 os_tick(第二次超时调用时生效, 因为只有当rt_soft_timer_check函数
* 调用rt_timer_start重新获取timer->init_tick时生效) */
rt_timer_control(&timer2, RT_TIMER_CTRL_SET_TIME, (rt_tick_t*)&tm2_timeout);
return RT_EOK;
}
INIT_APP_EXPORT(tick_sample);
END