软件定时器Software Timer(或者称为”定时器”)允许函数在未来的某个设定时间得到执行。函数的执行是通过定时器调用回调函数完成的。从定时器开始到回调函数执行,这段时间称为定时器的周期。简而言之,回调函数在定时器周期结束时间得到执行。

软件定时器必须先创建才能使用,也就是默认是无可用的定时器的。

实现软件定时器时对效率的考虑

软件定时器的实现是简单的,但是如何提高效率却是有难度的。freeRTOS在实现定时器时,不在中断中执行定时器的回调函数,不占用CPU时间,除非定时器定时时间确实到来。不增加任何处理开销到节拍中断中,在中断被禁止时,不走任何链表结构。

写定时器的回调函数的注意事项

定时器的回调函数在定时器服务任务中执行,因此回调函数不能尝试阻塞,比如调用vTaskDelay()vTaskDelayUntil()会到只阻塞,这是不允许的。同样对于队列、信号量的获取也会导致阻塞。

定时器的服务/守护任务和定时器命令队列

定时器功能时可选的,并不是freeRTOS内核的一部分,是由定时器服务任务提供的功能。

freeRTOS提供一系列的定时器相关的API函数。大多数的函数使用一个标准的freeRTOS队列来向定时器服务任务发送命令,这个队列称为”定时器器命令队列”。定时器命令队列是freeRTOS定时器的私有实现,无法直接访问。

下面的图表展示了这种方案:
左边的函数是用户程序代码的一部分,由用户任务调用。右边的函数是一个定时器服务任务。左边的函数通过调用xTimerReset()函数重置软件定时器,实际上该函数是发送重置指令到定时器命令队列中,定时器服务任务从队列中取出指令并执行。在这个过程中,用户代码无法直接访问定时器命令队列。

software timer

使用软件定时器

在freeRTOS中加入软件定时器是很简单的,只要完成:

  • 添加FreeRTOS/Source/timers.c源代码文件到你的工程中
  • 按照下面的表格配置软件定时器,注意,下面所列的选项在FreeRTOSConfig.h文件中,如果不存在,自己补全它。

























参数







描述





configUSE_TIMERS



设置为1将包含软件定时器的功能,软件定时器服务函数将在RTOS的调度器开始时自动创建



configTIMER_TASK_PRIORITY



设置软件定时器的优先级,与用户任务的优先级设置相同


优先级必须小心选择,否则可能无法满足你的应用程序需求。比如,如果优先级是整个系统最高的,发送到软件定时器的命令以及软件定时器超时都会立即得到处理。反过来,如果是一个比较低的优先级,命令及定时器超时所需要执行的操作会在所有高优先级的任务之后执行。值得一提的是,软件定时器的超时时间的计算与命令发送的时刻有关,而与命令的执行时刻无关。



configTIMER_QUEUE_LENGTH



这里设置的是命令队列的最大长度


可以从三个方面考虑这个值的大小



  • 在调度器开始之间会有多少次定时器相关的API效用


  • 在中断中定时器API调用频率


  • 比定时器优先级高的任务调用API的频率





configTIMER_TASK_STACK_DEPTH



S设置定时器服务任务的栈深度


定时器的回调函数在定时器的栈中执行,因此,这个栈深度,主要根据回调函数确定。

一次性定时器与自动重装定时器

定时器分为两种:一次性定时器(one-shot Timer)和自动重装定时器(auto-reload Timer)。一次性定时器只会执行回掉函数一次,可以手动重新开始。自动重装定时器会在每次执行回调函数之后自动重新启动自己,因此会周期性的调用回调函数。

下图展示了二者的不同,定时器1是一个周期100的一次性定时器,定时器2是一个周期为200的自动重装定时器:

The behaviour of one-shot timers and auto-reload timers

重设定时器

对于一个已经运行的定时器,重设它也是允许的。重设定时器会使得定时器从重设的时刻重新计算超时时间,而不是定时器最初启动的时间。下面的图展示了这个特点,定时器是一个5秒周期的一次性定时器。

这个例子里,假设使用一个按键控制LCD背光,挡按键按下,LCD背光打开,五秒后关闭,如果在这期间按键再次按下,则重置定时器。

Timer behaviour when a timer is reset