0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

RT-Thread定时器工作机制以及定时器的管理方式

RTThread物联网操作系统 来源:lq 2019-02-15 10:36 次阅读

前言

本文记录RT-Thread时钟相关知识,包括时钟节拍、RT-Thread定时器工作机制以及定时器的管理方式,在定时器方面有硬件定时器和软件定时器,这里不讲硬件定时器,硬件定时器只需学习裸机时候的硬件定时器即可。后面进行实际的操作时采STM32L475VET6,RTT&正点原子联合出品潘多拉开发板进行实验。

一、时钟节拍

任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。时钟节拍是特定的周期性中断,这个中断可以看做是系统心跳,中断之间的时间间隔取决于不同的应用,一般是 1ms–100ms,时钟节拍率越快,系统的额外开销就越大,从系统启动开始计数的时钟节拍数称为系统时间。

1、定义时钟节拍大小

RT-Thread 中,时钟节拍的长度可以根据 RT_TICK_PER_SECOND 的定义来调整,RT_TICK_PER_SECOND 在rtconfig.h里面定义,时钟节拍的长度等于 1/RT_TICK_PER_SECOND 秒,如下是1个时钟节拍为1ms:

1#defineRT_TICK_PER_SECOND1000//定义时钟节拍,为1000时表示1000个tick每秒,一个tick为1ms

2、时钟节拍的实现方式

时钟节拍由配置为中断触发模式的硬件定时器产生。

在前面讲移植RT-Thread的时候,修改board.c中,有如下函数:

1voidSysTick_Handler(void) 2{ 3/*enterinterrupt*/ 4rt_interrupt_enter(); 5 6rt_tick_increase(); 7 8/*leaveinterrupt*/ 9rt_interrupt_leave();10}

当中断到来时,将调用一次rt_tick_increase();,而在中断函数中调用 rt_tick_increase() 对全局变量 rt_tick 进行自加,如下代码:

1/** 2*Thisfunctionwillnotifykernelthereisonetickpassed.Normally, 3*thisfunctionisinvokedbyclockISR. 4*/ 5voidrt_tick_increase(void) 6{ 7structrt_thread*thread; 8 9/*increasetheglobaltick*/10++rt_tick;1112/*checktimeslice*/13thread=rt_thread_self();1415--thread->remaining_tick;16if(thread->remaining_tick==0)17{18/*changetoinitializedtick*/19thread->remaining_tick=thread->init_tick;2021/*yield*/22rt_thread_yield();23}2425/*checktimer*/26rt_timer_check();27}

可以看到全局变量 rt_tick 在每经过一个时钟节拍时,值就会加 1,rt_tick 的值表示了系统从启动开始总共经过的时钟节拍数,即系统时间。此外,每经过一个时钟节拍时,都会检查当前线程的时间片是否用完,以及是否有定时器超时。

注意:上面的中断中的rt_timer_check()用于检查系统硬件定时器链表,如果有定时器超时,将调用相应的超时函数。且所有定时器在定时超时后都会从定时器链表中被移除,而周期性定时器会在它再次启动时被加入定时器链表。

3、获取时钟节拍

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

1/** 2*Thisfunctionwillreturncurrenttickfromoperatingsystemstartup 3* 4*@returncurrenttick 5*/ 6rt_tick_trt_tick_get(void) 7{ 8/*returntheglobaltick*/ 9returnrt_tick;10}11RTM_EXPORT(rt_tick_get);

二、软件定时器

RT-Thread 的软件定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止,否则将永远持续执行下去。软件定时器的优先级为RT_TIMER_THREAD_PRIO。

1、开启软件定时宏

如果要开启使用软件定时器,需再rtconfig.h中打开软件定时器的宏:

1#defineRT_USING_TIMER_SOFT//定义该宏可开启软件定时器,未定义则关闭

2、软件定时器工作机制

(1)在 RT-Thread 定时器模块中维护着两个重要的全局变量:

(A)当前系统经过的 tick 时间 rt_tick(当硬件定时器中断来临时,它将加 1);(B)定时器链表 rt_timer_list。系统新创建并激活的定时器都会按照以超时时间排序的方式插入到rt_timer_list 链表中。

(2)如下图所示,系统当前 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链表中删除。

定时器链表插入示意图(来源RT-Thread编程指南)

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

图片占位 定时器链表插入示意图(来源RT-Thread编程指南)

注意:所有定时器在定时超时后都会从定时器链表中被移除,而周期性定时器会在它再次启动时被加入定时器链表。

3、动态创建软件定时器函数

调用动态创建软件定时器函数接口后,内核首先从动态内存堆中分配一个定时器控制块,然后对该控制块进行基本的初始化,函数如下:

1rt_timer_trt_timer_create(constchar*name,2void(*timeout)(void*parameter),3void*parameter,4rt_tick_ttime,5rt_uint8_tflag);

(1)入口参数

name:定时器的名称。name 定时器的名称void (timeout) (voidparameter):定时器超时函数指针(当定时器超时时,系统会调用这个函数,即定时器超时回调函数)。parameter:定时器超时函数的入口参数(当定时器超时时,调用超时回调函数会把这个参数做为入口参数传递给超时函数)。time:定时器的超时时间,单位是时钟节拍。flag:定时器创建时的参数,支持的值包括单次定时、周期定时、硬件定时器、软件定时器等(可以用 “或” 关系取多个值)。

(2)返回值:

RT_NULL:创建失败(通常会由于系统内存不够用而返回 RT_NULL)定时器的句柄:定时器创建成功时返回定时句柄。

(3)include/rtdef.h中定义了一些定时器相关的宏,通过如下4个宏或起来后赋给flag,来指定定时器类型:

1#defineRT_TIMER_FLAG_ONE_SHOT0x0/**< one shot timer */2#define RT_TIMER_FLAG_PERIODIC    0x2  /**< periodic timer */34#define RT_TIMER_FLAG_HARD_TIMER  0x0  /**< hard timer,the timer's callback function will be called in tick isr. */5#define RT_TIMER_FLAG_SOFT_TIMER  0x4  /**< soft timer,the timer's callback function will be called in timer thread. */

4、删除动态定时器函数

当不再需要动态定时器时,可以将其删除,执行如下函数之后系统会把这个定时器从rt_timer_list链表中删除,然后释放相应的定时器控制块占有的内存:rt_err_t rt_timer_delete(rt_timer_t timer);

(1)入口参数:

timer:定时器句柄,指向要删除的定时器。

(2)返回值:RT_EOK:删除成功(如果参数 timer 句柄是一个 RT_NULL,将会导致一个 ASSERT 断言)

5、静态创建软件定时器

静态创建软件定时器也就是《RT-Thread编程指南》里面所讲的定时器初始化,如下面函数,使用该函数接口时会初始化相应的定时器控制块,初始化相应的定时器名称,定时器超时函数:

1voidrt_timer_init(rt_timer_ttimer,2constchar*name,3void(*timeout)(void*parameter),4void*parameter,5rt_tick_ttime,6rt_uint8_tflag);

(1)入口参数:

timer:定时器句柄,指向要初始化的定时器控制块。name:定时器的名称void (timeout) (voidparameter):定时器超时函数指针(当定时器超时时,系统会调用这个函数,即定时器超时回调函数)。parameter:定时器超时函数的入口参数(当定时器超时时,调用超时回调函数会把这个参数做为入口参数传递给超时函数)。time:定时器的超时时间,单位是时钟节拍flag:定时器创建时的参数,支持的值包括单次定时、周期定时、硬件定时器、软件定时器(可以用 “或” 关系取多个值)。

6、删除静态定时器函数

当不再需要静态定时器时,可将它删除,也就是《RT-Thread编程》指南里面所说的脱离定时器。脱离定时器时,系统会把定时器对象从内核对象容器中脱离,但是定时器对象所占有的内存不会被释放:

1rt_err_trt_timer_detach(rt_timer_ttimer);

(1)入口参数:

timer:定时器句柄,指向要脱离的定时器控制块。

(2)返回值:

RT_EOK:脱离成功。

7、启动软件定时器

前面讲了定时器的创建,但创建好的定时器并不会立即开始工作,需要在调用启动定时器函数接口后才开始工作,调用定时器启动函数接口后,定时器的状态将更改为激活状态(RT_TIMER_FLAG_ACTIVATED),并按照超时顺序插入到rt_timer_list队列链表中,启动函数如下:

1rt_err_trt_timer_start(rt_timer_ttimer);

(1)入口参数:

timer:定时器句柄,指向要启动的定时器控制块。

(2)返回值:

RT_EOK:启动成功。

8、停止软件定时器

启动定时器以后,若想使它停止,可调用停止函数,调用定时器停止函数接口后,定时器状态将更改为停止状态(RT_TIMER_FLAG_DEACTIVATED),并从rt_timer_list链表中脱离出来不参与定时器超时检查,函数如下:

1rt_err_trt_timer_stop(rt_timer_ttimer);

(1)入口参数:

timer:定时器句柄,指向要停止的定时器控制块。

(2)返回值:

RT_EOK:成功停止定时器。RT_ERROR:timer 已经处于停止状态。。

9、控制定时器

控制定时器函数接口可根据命令类型参数,来查看或改变定时器的设置,函数如下:

1rt_err_trt_timer_control(rt_timer_ttimer,intcmd,void*arg);

(1)入口参数:

timer:定时器句柄,指向要停止的定时器控制块。cmd:用于控制定时器的命令,当前支持四个命令,分别是设置定时时间,查看定时时间,设置单次触发,设置周期触发。arg:与 cmd 相对应的控制命令参数比如,cmd 为设定超时时间时,就可以将超时时间参数通过arg 进行设定。

(2)函数参数 cmd 支持的命令:

1#defineRT_TIMER_CTRL_SET_TIME0x0/*设置定时器超时时间*/2#defineRT_TIMER_CTRL_GET_TIME0x1/*获得定时器超时时间*/3#defineRT_TIMER_CTRL_SET_ONESHOT0x2/*设置定时器为单次定时器*/4#defineRT_TIMER_CTRL_SET_PERIODIC0x3/*设置定时器为周期型定时器*/

(3)返回值:

RT_EOK:成功

三、基于STM32的软件定时器应用示例

前面讲了很多RT-Thread定时器方面的东西,实际上,我如果用RT-Thread的定时相关接口的话,一般都是用软件定时器,硬件定时器通常我们都是自己像以前学习裸机那边去用。接下来我们来实际使用一些RT-Thread的软件定时器,使用RTT&正点原子联合出品的潘多拉开发板来实验:(1)动态创建一个软件定时器,周期执行,实现定时器超时时打印出当前获取滴答定时器的计数值以及回调函数执行次数。(2)动态创建一个线程,通过按下KEY0来启动软件定时器,按下KEY1来停止软件定时器。

1、实现代码

1#include"main.h" 2#include"board.h" 3#include"rtthread.h" 4#include"data_typedef.h" 5#include"delay.h" 6#include"led.h" 7#include"key.h" 8 9voidrt_sw_timer1(void); 10voidkey_start(void); 11 12staticrt_timer_ttimer1;/*timer1句柄*/ 13intg_sw_timer1_count=0; 14 15intmain(void) 16{ 17rt_sw_timer1(); 18key_start(); 19 20return0; 21} 22 23/************************************************************** 24函数名称:rt_sw_timer1_callback 25函数功能:软件定时timer1回调函数 26输入参数:parameter:回调函数的入口参数,当定时器超时, 27调用回调函数会把这个参数做为入口参数传递给回调函数。 28返回值:无 29备注:无 30**************************************************************/ 31voidrt_sw_timer1_callback(void*parameter) 32{ 33u32tick_num1; 34 35tick_num1=(u32)rt_tick_get();/*获取滴答定时器的计数值*/ 36g_sw_timer1_count++; 37 38rt_kprintf("tick_num1=%d\r\n",tick_num1); 39rt_kprintf("enterrt_sw_timer_callback,g_sw_timer1_count=%d\r\n",g_sw_timer1_count); 40} 41 42/************************************************************** 43函数名称:rt_sw_timer1 44函数功能:软件定时timer1动态创建函数 45输入参数:无 46返回值:无 47备注:无 48**************************************************************/ 49voidrt_sw_timer1(void) 50{ 51/*动态创建软件定时器,周期执行*/ 52timer1=rt_timer_create("timer1", 53rt_sw_timer1_callback, 54RT_NULL, 555000,/*周期为5000个时钟节拍*/ 56RT_TIMER_FLAG_SOFT_TIMER|RT_TIMER_FLAG_PERIODIC);/*软件定时器,周期执行*/ 57} 58 59/************************************************************** 60函数名称:key_thread_entry 61函数功能:key线程入口函数 62输入参数:parameter:线程入口函数参数 63返回值:无 64备注:无 65**************************************************************/ 66voidkey_thread_entry(void*parameter) 67{ 68u8key; 69 70while(1) 71{ 72key=key_scan(0); 73if(key==KEY0_PRES) 74{ 75rt_timer_start(timer1); 76rt_kprintf("RT-Threadswtimer1satrt\r\n"); 77} 78elseif(key==KEY1_PRES) 79{ 80rt_timer_stop(timer1); 81rt_kprintf("RT-Threadswtimer1stop\r\n"); 82} 83rt_thread_mdelay(1); 84} 85 86} 87 88/************************************************************** 89函数名称:key_start 90函数功能:创建并启动key线程 91输入参数:无 92返回值:无 93备注:无 94**************************************************************/ 95voidkey_start(void) 96{ 97rt_thread_tkey_thread=RT_NULL;; 98 99/*动态创建KEY线程*/100key_thread=rt_thread_create("key",101key_thread_entry,102RT_NULL,103512,/*线程栈大小,单位是字节*/104RT_THREAD_PRIORITY_MAX/2-5,/*优先级*/10550);/*50个时钟节拍*/106/*创建KEY线程成功,则启动线程*/107if(key_thread!=RT_NULL)108{109rt_thread_startup(key_thread);110}111}

2、观察FinSH

(1)开机之后,等待超过软件定时配置的周期过后,还是没有打印出回调函数要打印的信息,说明回调函数没有被执行,为什么呢?因为我们创建软件定时器之后还没启动软件定时器,我们输入list_timer回车,可以看到timer1是处于deactivated状态。

(2)按下KEY0按键,启动软件定时器,可观察到每个5s打印一次当前tick的数值和回调函数的执行次数,将前后两次tick的数值相减大概为5006ms,再次输入list_timer,可看到timer1为activated状态。

(3)按下KEY1按键,停止软件定时器,停止打印回调函数要打印的信息,输入list_timer,可以看到timer1为deactivated状态。

四、软件定时器设计注意事项

在设计日软件定时器时,超时回调函数的要求严格:执行时间应该尽量短,执行时不应导致当前上下文挂起、等待。例如在中断上下文中执行的超时函数它不应该试图去申请动态内存、释放动态内存等,也不允许调用rt_thread_delay()等导致上下文挂起的 API 接口。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 定时器
    +关注

    关注

    23

    文章

    3246

    浏览量

    114700
  • 函数
    +关注

    关注

    3

    文章

    4326

    浏览量

    62558
  • 时钟管理
    +关注

    关注

    0

    文章

    16

    浏览量

    8346

原文标题:社区新人的RT-Thread学习笔记2——时钟管理

文章出处:【微信号:RTThread,微信公众号:RTThread物联网操作系统】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    rt-thread 驱动篇(八)hwtimer 重载算法优化

    区别于 rt-thread 内核实现的两种定时器,这种定时器依赖芯片内置的定时器外设,依靠稳定高速的晶振实现精确定时,可以实现
    的头像 发表于 06-23 10:10 2990次阅读
    <b class='flag-5'>rt-thread</b> 驱动篇(八)hwtimer 重载算法优化

    RT-Thread记录(四、RTT时钟节拍和软件定时器

    RT-Thread第4课,听听 RT-Thread 的心跳,再学习一下基于心跳的软件定时器使用。
    的头像 发表于 06-20 11:50 7724次阅读
    <b class='flag-5'>RT-Thread</b>记录(四、RTT时钟节拍和软件<b class='flag-5'>定时器</b>)

    rt-thread优化系列(三)软定时器定时漂移问题分析

    所谓软定时器,是由一个线程运行维护的定时器列表。由线程调用定时器回调函数。
    的头像 发表于 06-23 09:35 3488次阅读

    【每日一练】RT-Thread Nano-ADC(时钟管理之软件定时器)-3软件定时器内核代码分析(第十七节学习视频)

    )【每日一练】RT-Thread Nano-ADC(时钟管理之软件定时器)-1软件定时器创建(第十五节学习视频)【每日一练】RT-Thread
    发表于 06-02 10:32

    【每日一练】课程实践-基于RT-Thread内核定时器的电压采集

    是基于对何老师课程的理解,做一个使用内核的定时器来周期获取电压的采样值,以这个实践来巩固这段时间学习到的RT-Thread的知识,借这篇文章分享课程感悟,帮助更多想学习RT-Thread的小伙伴。二
    发表于 06-24 11:21

    RT-Thread提供了怎样的定时器操作函数以及如何使用它们呢

    rt_tick 的值表示了系统从启动到现在共经过的时钟节拍个数。定时器工作机制RT-Thread 提供的
    发表于 04-02 11:41

    请问下rt-thread是否有更高定时器对象的实现

    查看rt-thread的源码和相关文档,已经了解到了rt-thread定时器的实现原理,如文档介绍,该定时器的精度并不很高,请问下,r-thread
    发表于 05-09 11:40

    如何利用RT-Thread的接口去访问硬件定时器设备呢

    间达到500ms后,框架会调用用户预先设置好的回调函数。访问硬件定时器设备应用程序通过 RT-Thread 提供的 I/O 设备管理接口来访问硬件定时器设备,相关接口如下所示:查找
    发表于 07-22 10:31

    RT-Thread系统定时器管理相关资料介绍

    1、RT-Thread系统定时器管理  定时器,是指从指定的时刻开始,经过一个指定时间,然后触发一个事件,类似定个时间提醒第二天能够按时起床
    发表于 09-14 16:21

    RT-Thread软件定时器怎么获取定时器的状态?

    rt-thread 软件定时器怎么获取定时器的状态 比如获取当前定时器是运行状态,还是停止状态 ,自己加flag的方法除外哈
    发表于 01-31 15:19

    STM32定时器-基本定时器

    目录定时器分类基本定时器功能框图讲解基本定时器功能时钟源计数时钟计数自动重装载寄存
    发表于 11-23 18:21 31次下载
    STM32<b class='flag-5'>定时器</b>-基本<b class='flag-5'>定时器</b>

    详细剖析Linux和RTOS(RT-Thread)的时钟和定时器的使用

    Linux发烧友1.RTOS篇1.1RT-Thread简介1.2时钟管理1.2.1时钟节拍1.3获取系统节拍1.4定时器分类1.5定时器源码分析1.6
    发表于 01-17 09:31 4次下载
    详细剖析Linux和RTOS(<b class='flag-5'>RT-Thread</b>)的时钟和<b class='flag-5'>定时器</b>的使用

    RT-Thread学习笔记 -- 时钟管理

    RT-Thread学习笔记 – 时钟管理软件硬件定时器区别1.定时器分为硬件定时器和软件定时器
    发表于 01-17 09:53 1次下载
    <b class='flag-5'>RT-Thread</b>学习笔记 -- 时钟<b class='flag-5'>管理</b>

    RT-Thread驱动篇:hwtimer重载值算法

    区别于 rt-thread 内核实现的两种定时器,这种定时器依赖芯片内置的定时器外设,依靠稳定高速的晶振实现精确定时,可以实现
    的头像 发表于 04-01 10:06 1764次阅读

    定时器工作方式介绍

    定时器是计算机和嵌入式系统中常见的一种硬件模块,用于实现定时和计数功能。定时器工作方式通常由一组寄存来控制,这些寄存
    的头像 发表于 07-12 10:29 884次阅读