Skip to content

软件定时器

9.1 软件定时器特性

9.1.1 软硬件定时器

硬件定时器:

  • 芯片本身自带的定时器模块,硬件定时器的精度一般很高,每次在定时时间到达之后就会自动触发一个中断,用户在中断服务函数中处理信息。

软件定时器:

  • 是指具有定时功能的软件,可设置定时周期,当指定时间到达后要调用回调函数(也称超时函数),用户在回调函数中处理信息。

9.1.2 软件定时器特性

软件定时器可以设置为 单次定时器 和 周期定时器 :

  • 单次定时器,即当指定时间到达后只调用一次超时函数,之后软件定时器不再工作,但 可以被手动重新开启 。
  • 周期定时器,即会周期性的循环调用超时函数。

如下图所示:

image-20250117211336719

软件定时器有两种状态, 休眠态 和 运行态 :

  • 休眠态,即软件定时器可以通过其句柄被引用,但因为没有运行,所以其定时超时回调函数不会被执行。
  • 运行态,运行态的定时器,当指定时间到达之后,它的超时回调函数会被调用。

单次定时器状态转换图:

image-20250117211850033

周期定时器状态转换图:

image-20250117211904836

9.1.3 软件定时器的使用

  • 如果要使能软件定时器,需在FreeRTOSConfig.h文件中将configUSE_TIMERS 配置成 1 。
  • 软件定时器以Tick为基准,但软件定时器的超时回调函数是由 软件定时器服务任务 调用的。
  • 在开启任务调度器时,软件定时器服务任务prvTimerTask()与空闲任务一起被创建。
  • 软件定时器服务任务的作用:
    • 负责软件定时器超时的逻辑判断
    • 调用超时软件定时器的超时回调函数
    • 处理软件定时器命令队列
  • 软件定时器服务任务的优先级默认配置为 configTIMER_TASK_PRIORITY = 31 。
  • 用户通过命令队列与软件定时器服务任务交互,命令队列长度默认配置为 configTIMER_QUEUE_LENGTH = 5 。
  • 定时器的超时时间是基于调用 xTimerStart() 的时刻,xTimerStart()只是把开启定时器命令发给"定时器命令队列",至于服务任务能否马上执行,取决于它的优先级。

用户调用FreeRTOS的软件定时器相关的 API 函数,API 函数往软件定时器命令队列中写消息,实现用户与定时器服务任务的交互:

image-20250117213619523

9.1.4 回调函数

函数原型:

C
void ATimerCallback( TimerHandle_t xTimer );

定时器的回调函数是在守护任务中被调用的,守护任务不是专为某个定时器服务的,它还要处理其他定时器。

所以,定时器的回调函数不要影响其他人:

  • 回调函数要尽快实行,不能进入阻塞状态。
  • 不要调用会导致阻塞的API函数,比如 vTaskDelay() 。
  • 可以调用 xQueueReceive() 之类的函数,但是超时时间要设为0:即刻返回,不可阻塞。

9.2 软件定时器函数

9.2.1 创建定时器

C
TimerHandle_t xTimerCreate( 
        const char * const pcTimerName, 
        const TickType_t xTimerPeriodInTicks,
        const UBaseType_t uxAutoReload,
        void * const pvTimerID,
        TimerCallbackFunction_t pxCallbackFunction );
C
TimerHandle_t xTimerCreateStatic(   
        const char * const pcTimerName,         
        const TickType_t xTimerPeriodInTicks,
        const UBaseType_t uxAutoReload,
        void * const pvTimerID,
        TimerCallbackFunction_t pxCallbackFunction,
        StaticTimer_t *pxTimerBuffer );

9.2.2 删除定时器

C
BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );

9.2.3 启动/停止定时器

C
BaseType_t xTimerStart( 
        TimerHandle_t xTimer, 
        TickType_t xTicksToWait );

BaseType_t xTimerStartFromISR(   
        TimerHandle_t xTimer,
        BaseType_t *pxHigherPriorityTaskWoken );

BaseType_t xTimerStop( 
        TimerHandle_t xTimer, 
        TickType_t xTicksToWait );

BaseType_t xTimerStopFromISR(    
        TimerHandle_t xTimer,
        BaseType_t *pxHigherPriorityTaskWoken );

9.2.4 复位定时器

C
BaseType_t xTimerReset( 
        TimerHandle_t xTimer, 
        TickType_t xTicksToWait );

BaseType_t xTimerResetFromISR(  
        TimerHandle_t xTimer,
        BaseType_t *pxHigherPriorityTaskWoken );

9.2.5 修改周期

C
BaseType_t xTimerChangePeriod(   
        TimerHandle_t xTimer,
        TickType_t xNewPeriod,
        TickType_t xTicksToWait );

BaseType_t xTimerChangePeriodFromISR(
        TimerHandle_t xTimer,
        TickType_t xNewPeriod,
        BaseType_t *pxHigherPriorityTaskWoken );

9.2.6 软件定时器结构体

C
typedef struct tmrTimerControl
{
    const char              *pcTimerName;       /* 定时器名称 */ 
    ListItem_t              xTimerListItem;     /* 软件定时器列表项 */
    TickType_t              xTimerPeriodInTicks;/* 软件定时器的周期 */
    UBaseType_t             uxAutoReload;       /* 自动重启pdTRUE/pdFALSE */
    void                    *pvTimerID;         /* 软件定时器的ID */
    TimerCallbackFunction_t pxCallbackFunction; /* 软件定时器的回调函数 */
    #if( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t         uxTimerNumber;      /* 软件定时器的编号,调试用 */
    #endif

    #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
        uint8_t             ucStaticallyAllocated; /* 软件定时器的状态 */
    #endif
} xTIMER;

9.3 软件定时器示例

用软件定时器控制蜂鸣器发声,并在200ms后关闭。

蜂鸣器功能封装

C
static TimerHandle_t g_SoftTimerHandle;

/* 软件定时器超时函数 */
static void rtCallbackFunc(TimerHandle_t xTimer) {
    Buzzer_OFF();
}

/* 软件定时器控制蜂鸣器 */
void rtBuzzerSoftTimer_Init(void) {
    g_SoftTimerHandle = xTimerCreate(
            "BuzzerSound",
            200,
            pdFALSE,
            NULL,
            rtCallbackFunc
            );
}

/* 调用蜂鸣器,开启定时器 */
void rtBuzzerON(TickType_t period) {
    Buzzer_ON();
    xTimerChangePeriod(g_SoftTimerHandle, period, 0);
}

初始化与创建任务

C
void MX_FREERTOS_Init(void) {
    /* USER CODE BEGIN Init */
    rtBuzzerSoftTimer_Init();
    /* USER CODE END Init */
    ...
    /* USER CODE BEGIN RTOS_THREADS */
    /* add threads, ... */
    xTaskCreate(OLED_PrintTask, "Task1", 128, NULL, osPriorityNormal, NULL);
    /* USER CODE END RTOS_THREADS */
    ...
}

任务函数

C
void OLED_PrintTask(void *params) {
    int cnt = 1;
    while (1) {
        if (cnt % 10 == 0) {
            rtBuzzerON(200);
        }
        OLED_ShowNum(0, 0, cnt, 3, 16);
        cnt++;
        vTaskDelay(200);
    }
}