Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
Kernel
RT-Thread--内核学习timer(三)
发布于 2018-10-01 10:34:49 浏览:3692
订阅该版
上一篇看完了rt_thread类的成员以及相关的函数,可曾记得,在rt_thread类的成员中有一个rt_timer的实例,这篇就来看看与rt_timer的成员以及相关函数。 rt_timer实际就是RTT中实现的一个软定时器。MCU底层做的多的人一般对硬件定时器都很熟悉,软件定时器反而不太熟悉。这里稍微介绍下软件定时器。 回忆一下硬件定时器,设置一个通用的硬件定时器,步骤如下: * 配置定时器时钟。 * 初始化定时寄存器,前两步就是为了确定超时时间。 * 写定时器中断函数,确定时间到了之后执行的代码。 再之后就是开启或者关闭定时器了。 软件定时器其实也一样,只是这些全部由程序来执行,软件定时器的时钟节拍由软件周期性的产生,在RTOS中一般由心跳提供。通过软定时器初始化函数来设置超时时间以及超时回调函数(对应硬件定时器的中断函数)。 在RTT中,软件定时器又被分为纯软定时器以及普通定时器。纯软定时器中,定时器是由一个专门的线程来执行超时回调,该线程默认优先级为0(既最高)。普通定时器的时钟节拍由心跳中断来提供,超时回调也在硬件ISR中执行,心跳中断一般是一个硬件定时器的中断。 开启 ```#define RT_USING_TIMER_SOF``` 这个宏可以开启纯软定时器功能。 为了知道RTT如何实现这两种模式的软件定时器,先来看看rt_timer类 ```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;``` rt_timer的成员比较少, 1.parent,是object类的一个实例,说明rt_timer也继承自rt_object。 2.row[RT_TIMER_SKIP_LIST_LEVEL],一个双向链表结构,用来把rt_timer加入各种链表中。RT_TIMER_SKIP_LIST_LEVEL被定义为1,暂时不清楚RT_TIMER_SKIP_LIST_LEVEL这个变量的作用。可能是为日后可以将timer加入更多的链表。 3.timeout_func,超时回调函数指针,指向定时器超时后执行的回调函数。 4.parameter,回调函数的参数指针,指向回调函数执行时输入的参数。 5.init_tick,超时周期。超时时间=超时周期*每个周期的时间 6.timeout_tick,超时剩余周期。每个周期,该变量会被减1,直到到达0,就会触发timeout回调函数。 只有6个成员,看来成员是真的少啊。接下来,还是一样,继续看看与rt_timer相关的函数。可以看见,rt_timer相关函数有很多都与rt_thread相同,如init,creat,delete,detach, control等函数。 ```void rt_system_tick_init(void); rt_tick_t rt_tick_get(void); void rt_tick_set(rt_tick_t tick); void rt_tick_increase(void); int rt_tick_from_millisecond(rt_int32_t ms); void rt_system_timer_init(void); void rt_system_timer_thread_init(void); rt_tick_t rt_timer_next_timeout_tick(void); void rt_timer_check(void); 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); rt_timer_t rt_timer_create(const char *name, void (*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag); rt_err_t rt_timer_delete(rt_timer_t timer); rt_err_t rt_timer_start(rt_timer_t timer); rt_err_t rt_timer_stop(rt_timer_t timer); rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg);``` 一、rt_timer_init()和rt_timer_create() 还是一个规律,rt_timer_init是针对静态的rt_timer进行初始化,rt_timer_create是动态创建rt_timer,并进行初始化。 ```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; /* allocate a object */ timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name); if (timer == RT_NULL) { return RT_NULL; } _rt_timer_init(timer, timeout, parameter, time, flag); return timer; }``` 两个函数最后都调用了_rt_timer_init()这个实际的初始化函数 这个函数做了以下一些操作: 1.设置flag,去掉RT_TIMER_FLAG_ACTIVATED的标志,rt_timer使用了基类object中的flag成员作为自己的标志位。主要有以下几种标志: ```#define RT_TIMER_FLAG_DEACTIVATED 0x0 /**< timer is deactive */ #define RT_TIMER_FLAG_ACTIVATED 0x1 /**< timer is active */ #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. */``` 可以看到有3个标志都是0x0,这点有点怪。 2.配置time_out回调函数,以及parameter参数 3.设置超时周期init_tick,并把timeout_tick置为0 4.初始化双向链表row ```static void _rt_timer_init(rt_timer_t timer, void (*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag) { int i; /* set flag */ timer->parent.flag = flag; /* set deactivated */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; timer->timeout_func = timeout; timer->parameter = parameter; timer->timeout_tick = 0; timer->init_tick = time; /* initialize timer list */ for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) { rt_list_init(&(timer->row*)); } }``` 二、rt_timer_detach()和rt_timer_delete() 同样的,这两个函数都是对rt_timer进行删除,rt_timer_detach()针对的是静态的。 主要执行以下操作: 1.参数检查 2.执行_rt_timer_remove()将rt_timer移除相应的timer队列。(ISR或软timer线程将不再执行它) 3.删除rt_timer(释放分配的内存,移出对象容器) ```rt_err_t rt_timer_delete(rt_timer_t timer) { register rt_base_t level; /* timer check */ 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); /* disable interrupt */ level = rt_hw_interrupt_disable(); _rt_timer_remove(timer); /* enable interrupt */ rt_hw_interrupt_enable(level); rt_object_delete((rt_object_t)timer); return RT_EOK; }``` 三、rt_timer_start()和rt_timer_stop() 从名字就可以看出这两个函数一个是启动定时器一个是停止定时器。先来看启动的代码,代码挺长的主要完成以下一些步骤: 1.参数检查 2.重置rt_timer(将timer移出当前队列,RT_TIMER_FLAG_ACTIVATED标志移除。 3.调用rt_object_take_hook钩子函数 4.设置timeout_tick。 5.将rt_timer加入到对应的定时器列表中(纯软定时器加入纯软队列,普通定时器加入普通队列) 这里详细解析下下面这段代码。 rt_list_entry是一个神奇的宏定义,这个宏定义我也看了很久,写的很复杂。。关于宏定义提一下,RTT中具有很多很神奇的宏定义,写的十分精彩,大大减小了代码的复杂度,可以说是很优雅了。 ```#define rt_container_of(ptr, type, member) \ ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))``` 总的来说rt_list_entry会通过成员地址返回包含该成员的实例的地址。一般使用方式是,ptr为类中rt_list_t成员的地址,type为类名,member为成员名 这段代码搜寻timer_list这个列表中的所有timer,当 ```else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)``` 时停止,后面在这个位置,把当前timer加入列表。这条语句看似是一个相减小与 RT_TICK_MAX / 2的条件,其实真实的含义是t->timeout_tick - timer->timeout_tick>0,这里是利用的32位整型的表示方式,RT_TICK_MAX 为0xffffffff,在MCU中对于有符号的整型,最高为都是符号位,当两个数相减的时候实际是把减数按位取反+1再相加,所以其实只有一种情况t->timeout_tick - timer->timeout_tick>RT_TICK_MAX /2会成立,那就是为负数的时候。 综上,这个for循环,其实是在rt_timer列表中搜寻到了刚好超时时间大于启动的rt_timer的那个索引,作为之后rt_timer插入列表的位置。所以整个rt_timer的列表(软timer一个列表,普通timer一个列表),都是按照timeout_tick 这个参数的值进行排序的,是一个有序列表。 ``` row_head[0] = &timer_list[0]; 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; /* fix up the entry pointer */ t = rt_list_entry(p, struct rt_timer, row[row_lvl]); /* If we have two timers that timeout at the same time, it's * preferred that the timer inserted early get called early. * So insert the new timer to the end the the some-timeout timer * list. */ if ((t->timeout_tick - timer->timeout_tick) == 0) { continue; } else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2) { break; } } if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1) row_head[row_lvl + 1] = row_head[row_lvl] + 1; }``` 至于之后函数,功能其实是把一个完整的rt_timer列表拆分成几份,这样做的作用我也还没有看出来。。。 6.若启动的定时器为纯软的定时器,那么检测定时器线程是否启动,若未启动则重新启动线程。 ```rt_err_t rt_timer_start(rt_timer_t timer) { unsigned int row_lvl; rt_list_t *timer_list; register rt_base_t level; rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL]; unsigned int tst_nr; static unsigned int random_nr; /* timer check */ RT_ASSERT(timer != RT_NULL); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); /* stop timer firstly */ level = rt_hw_interrupt_disable(); /* remove timer from list */ _rt_timer_remove(timer); /* change status of timer */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; rt_hw_interrupt_enable(level); RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent))); /* * get timeout tick, * the max timeout tick shall not great than RT_TICK_MAX/2 */ RT_ASSERT(timer->init_tick < RT_TICK_MAX / 2); timer->timeout_tick = rt_tick_get() + timer->init_tick; /* disable interrupt */ level = rt_hw_interrupt_disable(); #ifdef RT_USING_TIMER_SOFT if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) { /* insert timer to soft timer list */ timer_list = rt_soft_timer_list; } else #endif { /* insert timer to system timer list */ timer_list = rt_timer_list; } row_head[0] = &timer_list[0]; 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; /* fix up the entry pointer */ t = rt_list_entry(p, struct rt_timer, row[row_lvl]); /* If we have two timers that timeout at the same time, it's * preferred that the timer inserted early get called early. * So insert the new timer to the end the the some-timeout timer * list. */ if ((t->timeout_tick - timer->timeout_tick) == 0) { continue; } else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2) { break; } } if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1) row_head[row_lvl + 1] = row_head[row_lvl] + 1; } /* Interestingly, this super simple timer insert counter works very very * well on distributing the list height uniformly. By means of "very very * well", I mean it beats the randomness of timer->timeout_tick very easily * (actually, the timeout_tick is not random and easy to be attacked). */ 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])); 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; /* enable interrupt */ rt_hw_interrupt_enable(level); #ifdef RT_USING_TIMER_SOFT if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) { /* check whether timer thread is ready */ if ((timer_thread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY) { /* resume timer thread to check soft timer */ rt_thread_resume(&timer_thread); rt_schedule(); } } #endif return RT_EOK; }``` stop函数就相对来说简单了很多,主要就是: 1.参数检查 2.调用rt_object_put_hook钩子函数 3.将rt_timer移出列表。 ```rt_err_t rt_timer_stop(rt_timer_t timer) { register rt_base_t level; /* timer check */ RT_ASSERT(timer != RT_NULL); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)) return -RT_ERROR; RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent))); /* disable interrupt */ level = rt_hw_interrupt_disable(); _rt_timer_remove(timer); /* enable interrupt */ rt_hw_interrupt_enable(level); /* change stat */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; return RT_EOK; }``` 四、rt_timer_control()函数 改函数可以通过参数中的cmd来实现不同的功能,cmd总共有4中取值: ```#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 */``` 分别对应: * 设置定时器时间 * 获取定时器时间 * 设置定时器为单触发模式 * 设置定时器为周期性触发模式 当rt_timer被标记为周期性定时器后,当time_out回调触发后会执行start重新启动该定时器。 五、其他函数。 剩下的函数还有 ```(1)void rt_system_tick_init(void); (2)rt_tick_t rt_tick_get(void); (3)void rt_tick_set(rt_tick_t tick); (4)void rt_tick_increase(void); (5)int rt_tick_from_millisecond(rt_int32_t ms); (6)void rt_system_timer_init(void); (7)void rt_system_timer_thread_init(void); (8)rt_tick_t rt_timer_next_timeout_tick(void); (9)void rt_timer_check(void);``` (1)是个空函数。 (2)返回系统tick数,这个数在系统启动后就随着心跳不断的增加。 (3)设置系统tick数 (4) ```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 == 0) { /* change to initialized tick */ thread->remaining_tick = thread->init_tick; /* yield */ rt_thread_yield(); } /* check timer */ rt_timer_check(); }``` 函数会对rt_tick加1,并检查线程时间片时间是否过期,若过期,则调用rt_thread_yield()。这个函数在上一篇分析过。 之后会执行(9)rt_timer_check()函数 (5)是个工具函数,将ms数换算成tick数 (6)初始化普通定时器列表 (7)若开启了纯软定时器,启动定时器线程,纯软定时器的time_out回调将在这个线程中执行,该线程默认优先级为0 (8)返回下一个最近会到期的普通定时器的time_out值。 (9) ```void rt_timer_check(void) { struct rt_timer *t; rt_tick_t current_tick; register rt_base_t level; RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter
")); current_tick = rt_tick_get(); /* disable interrupt */ level = rt_hw_interrupt_disable(); while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) { t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); /* * It supposes that the new tick shall less than the half duration of * tick max. */ if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2) { RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t)); /* remove timer from timer list firstly */ _rt_timer_remove(t); /* call timeout function */ t->timeout_func(t->parameter); /* re-get tick */ current_tick = rt_tick_get(); RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d
", current_tick)); 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 { /* stop timer */ t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; } } else break; } /* enable interrupt */ rt_hw_interrupt_enable(level); RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave
")); }``` 该函数在ISR中执行,检测是否有普通定时器到期,对于到期的定时器先执行time_out回调,并判断是否是周期定时器,若是周期定时器,重新启动这个定时器。 那么对于rt_timer的解读就这么多,篇幅感觉越来越长呀。。
查看更多
1
个回答
默认排序
按发布时间排序
whj467467222
认证专家
2018-10-02
开源,分享,交流,共同进步
楼主写的真好,期待楼主继续更新
撰写答案
登录
注册新账号
关注者
0
被浏览
3.7k
关于作者
yushigengyu
这家伙很懒,什么也没写!
提问
15
回答
25
被采纳
0
关注TA
发私信
相关问题
1
请教cpu使用率分析
2
选择FreeRTOS, 还是RT-Thread。
3
thread heap stack overflow ?
4
rtt消息队列delay问题
5
释放被删除线程的内存地方在哪里啊
6
请教:各线程结束后,释放其中的内存的连续性问题
7
STM32F103中断关于信号量、邮箱问题
8
RTT中的线程栈大小如何控制
9
关于线程由执行态变为挂起态的代码实现,,,
10
rt_malloc(rt_size_t size)内存分配函数最小分配尺寸问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
在用clangd开发RTT吗,快来试试如何简单获得清晰干净的工作区
2
GD32F450 片内 flash驱动适配
3
STM32H7R7运行CherryUSB
4
RT-Smart首次线下培训,锁定2024 RT-Thread开发者大会!
5
使用RC522软件包驱动FM1722
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部