软件定时器

        软件定时器可以设置定时周期,在周期到期时执行定时器的回调函数,以实现让函数在未来的设定时间执行的特性。

        软件定时器功能很容易实现,但很难高效地实现。如下code所示,可以很容易实现最简单的定时器,但如果系统里有很多定时器,要在每个tick中断里高效管理他们就很难。

  • 如,假设有n个timer,tick为1ms,那么系统每秒就要进入1000次中断,每次中断就要检查n个timer,这样中断时间就会特别长,因而很难高效管理;
/* 每个tick中断,都要执行, tick可能1ms 触发一次,timer可能几十甚至几百个 */
for each timer
    timer--
    if timer == 0
        call callback()

        FreeRTOS的软件定时器是通过tick ISR 通知定时器服务任务执行而实现的(如下所示),因而软件定时器回调函数在定时器服务任务的上下文中执行的。

  • 软件定时器回调函数类似硬件的ISR函数,因此,定时器回调函数也要快进快出,不能有阻塞(若阻塞,会导致定时器服务任务执行时间过长,影响下一次Tick 周期,以及其他timer的执行)
  • 定时器命令队列:基于FreeRTOS队列向定时器服务任务发送命令,用于此目的队列称为定时器命令队列,该队列专用于FreeRTOS定时器实现,无法直接访问。(各种timer api就是通过队列的方式发送一个消息给软件定时器任务,任务被唤醒从而执行接收到的命令)
/* tick ISR 通知定时器服务任务执行 */
Tick ISR
   |
   | 只做一件事
   v
通知 Timer Service Task


/* 定时器服务任务会遍历timer list,以执行对应timer 的callback */
Timer Service Task
     |
     v
处理 timer list
     |
     v
执行 callback

        软件定时器的精度是无法和硬件定时器相比的,因为在软件定时器的定时过程中是极有可能被其他中断所打断,所以,软件定时器更适用于对时间精度要求不高的任务。

  • 软件定时器通常是以系统节拍周期为计时单位,系统节拍配置为configTICK_RATE_HZ,系统节拍周期的值越小,精度越高,但系统开销也将越大,因为这代表在1s中系统进入时钟中断的次数也就越多;

        定时器有两种类型:

  • 一次性定时器:启动后只会执行一次回调函数,可以手动重新启动,但不会自动重新启动,该定时器执行完回调函数后,系统会自动删除该软件定时器,并回收资源;
  • 自动重载定时器:自动重载定时器一旦启动,将在每次执行回调函数后,自动重新启动,从而周期性地执行回调;

code相关接口

/* xTimerPeriod: 定时器周期,必须大于0 */
/* uxAutoReload: 设置为pdFALSE时,则此定时器为一次性定时器,否则将以xTimerPeriod周期重复过期*/
/* pvTimerID: 分配给正在创建的定时器的标识符,当同一回调函数分配给多个定时器时,
               该函数将于用定时器回调函数,以识别哪个定时器过期
             (pvTimerGetTimerID() & pvTimerGetTimerID)*/
/* pxCallbackFunction 原型:void vCallbackFunction( TimerHandle_t xTimer );*/
/* 创建成功后,返回新创建定时器的句柄,否则返回NULL */
 TimerHandle_t xTimerCreate
                 ( const char * const pcTimerName,
                   const TickType_t xTimerPeriod,
                   const UBaseType_t uxAutoReload,
                   void * const pvTimerID,
                   TimerCallbackFunction_t pxCallbackFunction );

/* 查询软件定时器是否处于活动或休眠状态,返回pdFALSE, 则定时器处于休眠状态 */
 BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer );

/* 若定时器已经启动且处于活跃状态,则xTimerStart 具有xTimerReset 相同功能 */
/* xBlockTime: 指定在调用 xTimerStart时队列已满的情况下,
            调用任务处于阻塞状态以等待启动命令成功发送到定时器命令队列的时间,
            发送成功则返回pdPASS */
 BaseType_t xTimerStart( TimerHandle_t xTimer,
                            TickType_t xBlockTime );

 BaseType_t xTimerStop( TimerHandle_t xTimer,
                        TickType_t xBlockTime );

/* timer delete */
 BaseType_t xTimerDelete( TimerHandle_t xTimer,
                          TickType_t xBlockTime );

/* 重置timer周期 */
 BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
                                TickType_t xNewPeriod,
                                TickType_t xBlockTime );

/* timer reset */
 BaseType_t xTimerReset( TimerHandle_t xTimer,
                         TickType_t xBlockTime );

/* 返回分配给软件定时器的ID */
 void *pvTimerGetTimerID( TimerHandle_t xTimer );

/* set timer id */
 void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID );


 TickType_t xTimerGetPeriod( TimerHandle_t xTimer );
 TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer );

/* 若timer为自动重载定时器,则返回pdTRUE, 否则返回pdFALSE */
BaseType_t  xTimerGetReloadMode( TimerHandle_t xTimer );
UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer );


/* 返回与定时器守护服务任务相关的任务句柄 */
TaskHandle_t xTimerGetTimerDaemonTaskHandle( void );

/* 返回创建定时器时分配给定时器的人类可读名称 */
const char * pcTimerGetName( TimerHandle_t xTimer )

/* 重置定时器模块的内部状态,在重新启动调度器之前,应用程序必须调用它 */
void vTimerResetState( void );

参考

软件定时器

FreeRTOS 软件定时器

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐