软件定时器
- 1. 软件定时器控制块 xTIMER
- 2. 常用函数
- 2.1 软件定时器创建 xTimerCreate()
- 2.2 软件定时器启动 xTimerStart() / xTimerStartFromISR()
- 2.3 软件定时器停止 xTimerStop() / xTimerStopFromISR()
- 2.4 软件定时器任务删除 xTimerDelete()
1. 软件定时器控制块 xTIMER
pxCurrentTimerList系统新创建并激活的定时器都会以超时时间升序的方式插入到 pxCurrentTimerList 列表中。系统在定时器任务中扫描 pxCurrentTimerList 中的第一个定时器,看是否已超时,若已经超时了则调用软件定时器回调函数。否则将定时器任务挂起,因为定时时间是升序插入软件定时器列表的,列表中第一个定时器的定时时间都还没到的话,那后面的定时器定时时间自然没到。
pxOverflowTimerList列表是在软件定时器溢出的时候使用,作用与 pxCurrentTimerList一致。
(1)软件定时器名字,这个名字一般用于调试的,RTOS 使用定时器是通过其句柄,并不是使用其名字(2)软件定时器列表项,用于插入定时器列表(3)软件 定时 器的周 期,单 位为 系统节 拍周期 (即 tick) ,pdMS_TO_TICKS()可以把时间单位从 ms转换为系统节拍周期(4)软件定时器是否自动重置,如果该值为 pdFalse,那么创建的软件定时器工作模式是单次模式,否则为周期模式(5)软件定时器 ID,数字形式。该 ID 典型的用法是当一个回调函数分配给一个或者多个软件定时器时,在回调函数里面根据 ID 号来处理不同的软件定时器(6)软件定时器的回调函数,当定时时间到达的时候就会调用这个函数(7)标记定时器使用的内存,删除时判断是否需要释放内存
2. 常用函数
2.1 软件定时器创建 xTimerCreate()
动态创建定时任务:
(1)软件定时器名字,文本形式,纯粹是为了调试,FreeRTOS 使用定时器是通过其句柄,而不是使用其名字。(2)软件定时器的周期,单位为系统节拍周期(即 tick)。使用 pdMS_TO_TICKS()可以把时间单位从 ms 转换为系统节拍周期。如果软件定时器的周期为 100个 tick,那么只需要简单的设置 xTimerPeriod 的值为 100 即可。如果软件定时器的周期为 500ms,那么 xTimerPeriod 应设置为 pdMS_TO_TICKS(500)。宏 pdMS_TO_TICKS()只有当 configTICK_RATE_HZ 配置成小于或者等于 1000HZ 时才可以使用(3)如果 uxAutoReload 设置为 pdTRUE,那么软件定时器的工作模式就是周期模式 ,一 直会以用户指定的 xTimerPeriod 周期去执行回调函数 。如果 uxAutoReload 设置为 pdFALSE,那么软件定时器就在用户指定的 xTimerPeriod 周期下运行一次后就进入休眠态(4)软件定时器 ID,数字形式。该 ID 典型的用法是当一个回调函数分配给一个或者多个软件定时器时,在回调函数里面根据 ID 号来处理不同的软件定时器(5)软件定时器的回调函数,当定时时间到达的时候就会调用这个函数,该函数需要用户自己实现(6)为这个软件定时器申请一块内存,大小为软件定时器控制块大小,用于保存该定时器的基本信息(7)调用 prvInitialiseNewTimer()函数初始化一个新的软件定时器
- 断言,判断软件定时器的周期是否大于 0,否则的话其他任务是没办法执行的,因为系统会一直执行软件定时器回调函数
- 在 prvCheckForValidListAndQueue()函数中系统将初始化软件定时器列表与创建软件定时器消息队列,也叫“定时器命令队列”,因为在使用软件定时器的时候,用户是无法直接控制软件定时器的,必须通过“定时器命令队列”向软件定时器发送一个命令,软件定时器任务被唤醒就去执行对应的命令操作
- 初始化软件定时基本信息,如定时器名称、回调周期、定时器 ID与定时器回调函数等,这些信息保存在软件定时器控制块中,在操作软件定时器的时候,就需要用到这些信息
- 初始化定时器列表项
软件定时器回调函数 prvTimerTask()
(1)获取下一个要到期的软件定时器的时间,因为软件定时器是由定时器列表维护的,并且按照到期的时间进行升序排列,只需获取软件定时器列表中的第一个定时器到期时间就是下一个要到期的时间(2)处理定时器或者将任务阻塞到下一个到期的软件定时器时间,因为系统时间节拍随着系统的运行可能会溢出,那么就需要处理溢出的情况,如果没有溢出,那么就等待下一个定时器到期时间的到来。该函数每次调用都会记录节拍值, 下一次调用,通过比较相邻两次调用的值判断节拍计数器是否溢出过。当节拍计数器溢出,需要处理掉当前定时器列表上的定时器(因为这条定时器列表上的定时器都已经溢出了),然后切换定时器列表
- 挂起调度器。接下来的操作会对定时器列表进行操作,系统不希望别的任务来操作定时器列表,所以暂时让定时器任务独享CPU 使用权,在此期间不进行任务切换
- 获取当前系统时间节拍并判断系统节拍计数是否溢出,如果已经溢出了,那么就处理当前列表上的定时器,并切换定时器列表
2.1定义一个静态变量,记录上一次调用时系统时间节拍的值
2.2获取当前系统时间节拍值
2.3判断系统节拍计数器否溢出了,当前系统时间节拍比上一次调用时间节拍的值小,这种情况是溢出的情况。而如果发生了溢出,系统就要处理当前定时器列表上所有定时器并将当前时器列表的定时器切换到定时器溢出列表中,因为软件定时器由两个列表维护,并且标记一下定时器列表已经切换了,pxTimerListsWereSwitched 的值等于pdTRUE
2.4更新本次系统时间节拍的值
2.5返回当前系统时间节拍- 如果系统节拍计数器没有溢出
- 判断是否有定时器是否到期可以触发回调函数,如果定时器列表非空并且定时器的时间已比当前时间小,说明定时器到期了,系统可用恢复调度器,并且执行相应到期的定时器回调函数,对于需要自动重载的定时器,更新下一次溢出时间,然后插回定时器列表中,这些操作均在prvProcessExpiredTimer()函数中执行
- 定时器没有到期,然后看看当前定时器列表中没有定时器,如果没有,那么发生这种情况的可能是系统节拍计数器溢出了,定时器被添加到溢出列表中,所以判断一下定时器溢出列表上是否有定时器
- 定时器定时时间还没到,将当前的定时器任务阻塞,直到定时器到期才唤醒或者收到命令的时候唤醒。FreeRTOS 采用获取“定时器命令队列”的命令的方式阻塞当前任务,阻塞时间为下一个定时器到期时间节拍减去当前系统时间节拍,为什么呢?因为获取消息队列的时候,没有消息会将任务阻塞,时间由用户指定,这样子一来,既不会错过定时器的到期时间,也不会错过操作定时器的命令
- 恢复调度器,看看是否有任务需要切换,如果有则进行任务切换
(3)读取“定时器命令队列”,处理相应命令
2.2 软件定时器启动 xTimerStart() / xTimerStartFromISR()
(1)系统打算通过“定时器命令队列”发送命令给定时器任务,需要先判断一下“定时器命令队列”是否存在,只有存在队列才允许发送命令(2)要发送的命令基本信息,包括命令、命令的数值(比如可以表示当前系统时间、要修改的定时器周期等)以及要处理的软件定时器句柄等(3)根据用户指定的 xCommandID 参数,判断命令是在哪个上下文环境发出的,如果是在任务中发出的,则执行(4) 、(5) 代码,否则就执行(6)(4)如果系统调度器已经运行了,就根据用户指定超时时间向“定时器命令队列”发送命令(5)如果调度器还未运行,用户指定的超时时间是无效的,发送就行了,不需要阻塞,tmrNO_DELAY 的值为 0(6)命令是在中断中发出的,调用从中断向消息队列发送消息的函数 xQueueSendToBackFromISR()就行了