软件定时器 - 图1

运作机制

创建定时器会分配一块内存空间。

FreeRTOS 会根据当前系统时间及用户设置的定时确定该定时器唤醒时间,并将该定时器控制块挂入软件定时器列表,
FreeRTOS 中采用两个定时器列表维护软件定时器

pxCurrentTimerList

系统新创建并激活的定时器都会以超时时间升序的方式插入到pxCurrentTimerList 列表中。

系统在定时器任务中扫描 pxCurrentTimerList 中的第一个定时器,看是否已超时,
若已经超时了则调用软件定时器回调函数。
否则将定时器任务挂起,因为定时时间是升序插入软件定时器列表的,列表中第一个定时器的定时时间都还没到的话,那后面的定时器定时时间自然没到。

pxOverflowTimerList

列表是在软件定时器溢出的时候使用,作用与 pxCurrentTimerList 一致。

那么系统如何处理软件定时器列表?系统在不断运行,而 xTimeNow(xTickCount)随着 SysTick 的触发一直在增长(每一次硬件定时器中断来临,xTimeNow 变量会加 1),
在软件定时器任务运行的时候会获取下一个要唤醒的定时器,比较当前系统时间 xTimeNow 是否大于或等于下一个定时器唤醒时间 xTicksToWait,
若大于则表示已经超时, 定时器任务将会调用对应定时器的回调函数,
否则将软件定时器任务挂起,直至下一个要唤醒的软件定时器时间到来或者接收到命令消息。以图 21-3 为例,讲解软件定时器调用回调函数的过程,

在创建定 Timer1 并且启动后,假如系统经过了 50 个 tick, xTimeNow 从 0 增长到 50,与 Timer1 的 xTicksToWait 值相等, 这时会触发与 Timer1 对应的回调函数,从而转到回调函数中执行用户代码,同时将 Timer1 从软件定时器列表删除,如果软件定时器是周期性的,那么系统会根据 Timer1 下一次唤醒时间重新将 Timer1 添加到软件定时器列表中,按照 xTicksToWait 的升序进行排列。同理,在 xTimeNow=40 的时候创建的 Timer3,在经过 130 个 tick 后(此时系统时间 xTimeNow 是 40,130 个 tick 就是系统时间
xTimeNow 为 170 的时候),与 Timer3 定时器对应的回调函数会被触发,接着将 Timer3 从软件定时器列表中删除,如果是周期性的定时器,还会按照 xTicksToWait 升序重新添加到软件定时器列表中。

使用软件定时器时候要注意以下几点:

  • 软件定时器的回调函数中应快进快出,绝对不允许使用任何可能引软件定时器起任务挂起或者阻塞的 API 接口,在回调函数中也绝对不允许出现死循环。

  • 软件定时器使用了系统的一个队列和一个任务资源,软件定时器任务的优先级默认为configTIMER_TASK_PRIORITY,为了更好响应,该优先级应设置为所有任务中最高的优先级。

  • 创建单次软件定时器,该定时器超时执行完回调函数后,系统会自动删除该软件定时器,并回收资源。

  • 定时器任务的堆栈大小默认为 configTIMER_TASK_STACK_DEPTH 个字节。


控制块

  1. typedef struct tmrTimerControl {
  2. const char *pcTimerName; //软件定时器的名字
  3. ListItem_t xTimerListItem;//软件定时器列表项 用于插入定时器列表
  4. TickType_t xTimerPeriodInTicks; //软件定时器的周期,单位为系统节拍周期 (即 tick)
  5. UBaseType_t uxAutoReload; //软件定时器是否自动重置,如果该值为 pdFalse,那么创建的软件
  6. //定时器工作模式是单次模式,否则为周期模式。
  7. void *pvTimerID; //软件定时器 ID,数字形式。
  8. TimerCallbackFunction_t pxCallbackFunction; //软件定时器的回调函数
  9. #if( configUSE_TRACE_FACILITY == 1 )
  10. UBaseType_t uxTimerNumber;
  11. #endif
  12. #if( ( configSUPPORT_STATIC_ALLOCATION == 1 )\
  13. && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
  14. uint8_t ucStaticallyAllocated; //标记定时器使用的内存,删除时判断是否需要释放内存
  15. #endif
  16. } xTIMER;
  17. typedef xTIMER Timer_t;

函数讲解

软件定时器 - 图2软件定时器创建函数 xTimerCreate()