FreeRTOS_软件定时器

犀利的毛毛虫 发布于 2025-03-12 386 次阅读


软件定时器的简介(了解)

FreeRTOS 中的软件定时器是一种轻量级的时间管理工具,用于在任务中创建和管理定时器。软件定时器是基于 FreeRTOS 内核提供的时间管理功能实现的,允许开发者创建、启动、停止、删除和管理定时器,从而实现在任务中对时间的灵活控制。

软件定时器与硬件定时器的主要区别如下:

软件定时器硬件定时器
FreeRTOS提供的功能来模拟定时器,依赖系统的任务调度器来进行计时和任务调度由芯片或微控制器提供,独立于 CPU,可以在后台运行,不受任务调度器的影响
精度和分辨率可能受到任务调度的影响具有更高的精度和分辨率
不需要额外的硬件资源,但可能会增加系统的负载占用硬件资源,不会增加 CPU 的负载

软件定时器能够让函数在未来的设定时间执行。由定时器执行的函数称为定时器的回调函数。从定时器启动到其回调函数执行之间的时间被称为定时器的周期。简而言之,当定时器的周期到期时,定时器的回调函数会被执行。

定时器回调函数在定时器服务任务的上下文中执行,在定时器回调函数中不能调用导致阻塞的API函数。

软件定时器服务任务是任务调度器中的一个特殊任务,专门用于管理和维护软件定时器的正常运行。如果configUSE_TIMERS 设置为1,在开启任务调度器的时候,会自动创建软件定时器服务的任务。它主要负责软件定时器超时的逻辑判断、调用超时软件定时器的超时回调函数、处理软件定时器命令队列。

软件定时器的状态(熟悉)

FreeRTOS 中的软件定时器有三种状态,分别是:

  • 未创建(Uncreated):软件定时器被创建之前的状态。在这个状态下,定时器的数据结构已经被定义,但尚未通过 xTimerCreate() 函数创建。
  • 已创建(Created):软件定时器已被成功创建,但尚未启动。在这个状态下,可以对定时器进行配置,如设置定时器的周期、回调函数等,但定时器并未开始计时。
  • 已运行(Running):软件定时器已经被启动,正在运行中。在这个状态下,定时器会按照预定的周期定时触发超时事件,执行注册的回调函数。

单次定时器和周期定时器(熟悉)

在 FreeRTOS 中,软件定时器主要有两种类型:一次性定时器和周期性定时器。

  • 一次性定时器(One-shot Timer): 这种定时器在触发一次超时后就会停止,不再执行。适用于只需在特定时间执行一次任务或动作的场景。
  • 周期性定时器(Periodic Timer): 这种定时器会在每个超时周期都触发一次,循环执行。适用于需要在固定的时间间隔内重复执行任务或动作的场景。

FreeRTOS软件定时器相关API函数(熟悉)

软件定时器相关函数如下:

函数描述
xTimerCreate()动态方式创建软件定时器
xTimerCreateStatic()静态方式创建软件定时器
xTimerStart()开启软件定时器定时
xTimerStartFromISR()在中断中开启软件定时器定时
xTimerStop()停止软件定时器定时
xTimerStopFromISR()在中断中停止软件定时器定时
xTimerReset()复位软件定时器定时
xTimerResetFromISR()在中断中复位软件定时器定时
xTimerChangePeriod()更改软件定时器的定时超时时间
xTimerChangePeriodFromISR()在中断中更改定时超时时间

FreeRTOS软件定时器实验(掌握)实验目标

学习使用FreeRTOS软件定时器的函数:

  • start_task:用来创建task1任务,并创建一次性定时器和周期性定时器。
  • task1:用于按键扫描,并对软件定时器进行开启、停止操作。

FreeRTOSConfig.h代码清单

/* 软件定时器相关定义 */

#define configUSE_TIMERS 1    /* 1: 使能软件定时器, 默认: 0。使能后需指定下面3个 */

#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) // 定义软件定时器任务的优先级 

#define configTIMER_QUEUE_LENGTH 5             /* 定义软件定时器命令队列的长度*/

#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小*/

freertos_demo.c代码清单

  • 引入头文件
#include "timers.h"
  • 任务配置

#include "freertos_demo.h"

/* freertos相关的头文件,必须的 */

#include "FreeRTOS.h"

#include "task.h"

#include "timers.h" // 软件定时器的头文件

/* 需要用到的其他头文件 */

#include "LED.h"

#include "Key.h"

/* 启动任务的配置 */

#define START_TASK_STACK 128

#define START_TASK_PRIORITY 1

TaskHandle_t start_task_handle;

void start_task(void *pvParameters);

/* 任务1的配置 */

#define TASK1_STACK 128

#define TASK1_PRIORITY 2

TaskHandle_t task1_handle;

void task1(void *pvParameters);

/* 软件定时器的超时回调函数 */

void timer1_callback(TimerHandle_t xTimer); // 单次

void timer2_callback(TimerHandle_t xTimer); // 周期

/* 软件定时器的句柄 */

TimerHandle_t timer1_handle;

TimerHandle_t timer2_handle;

/**

 * @description: 启动FreeRTOS

 * @return {*}

 */

void freertos_start(void)

{

    /* 创建一次性软件定时器 */

    timer1_handle = xTimerCreate(

        "ergou",         // 软件定时器的名称

        (TickType_t)500, // 超时时间 = 500个 RTOS的时钟节拍

        pdFALSE,         // 是否自动重载:pdTRUE --- 周期型; pdFALSE --- 一次性

        (void *)1,       // 定时器的唯一ID

        timer1_callback);

    if (timer1_handle != NULL)

    {

        printf("timer1一次性定时器创建成功...\r\n");

    }

    /* 创建周期性软件定时器 */

    timer2_handle = xTimerCreate(

        "gousheng",       // 软件定时器的名称

        (TickType_t)1000, // 超时时间 = 1000个 RTOS的时钟节拍

        pdTRUE,           // 是否自动重载:pdTRUE --- 周期型; pdFALSE --- 一次性

        (void *)2,        // 定时器的唯一ID

        timer2_callback);

    if (timer2_handle != NULL)

    {

        printf("timer2周期性定时器创建成功...\r\n");

    }

    /* 1.创建一个启动任务 */

    xTaskCreate((TaskFunction_t)start_task,               // 任务函数的地址

                (char *)"start_task",                     // 任务名字符串

                (configSTACK_DEPTH_TYPE)START_TASK_STACK, // 任务栈大小,默认最小128,单位4字节

                (void *)NULL,                             // 传递给任务的参数

                (UBaseType_t)START_TASK_PRIORITY,         // 任务的优先级

                (TaskHandle_t *)&start_task_handle);      // 任务句柄的地址

    /* 2.启动调度器:会自动创建空闲任务 */

    vTaskStartScheduler();

}

/**

 * @description: 启动任务:用来创建其他Task

 * @param {void} *pvParameters

 * @return {*}

 */

void start_task(void *pvParameters)

{

    /* 进入临界区:保护临界区里的代码不会被打断 */

    taskENTER_CRITICAL();

    /* 创建3个任务 */

    xTaskCreate((TaskFunction_t)task1,

                (char *)"task1",

                (configSTACK_DEPTH_TYPE)TASK1_STACK,

                (void *)NULL,

                (UBaseType_t)TASK1_PRIORITY,

                (TaskHandle_t *)&task1_handle);

    /* 启动任务只需要执行一次即可,用完就删除自己 */

    vTaskDelete(NULL);

    /* 退出临界区 */

    taskEXIT_CRITICAL();

}

/**

 * @description: 任务一:用于按键扫描,并对软件定时器进行开启、停止操作

 * @param {void} *pvParameters

 * @return {*}

 */

void task1(void *pvParameters)

{

    uint8_t key = 0;

    BaseType_t res = 0;

    while (1)

    {

        key = Key_Detect();

        if (key == KEY1_PRESS)

        {

            /* 开启软件定时器 */

            res = xTimerStart(timer1_handle, portMAX_DELAY);

            if (res != pdFAIL)

            {

                printf("timer1单次定时器启动成功\r\n");

            }

            res = xTimerStart(timer2_handle, portMAX_DELAY);

            if (res != pdFAIL)

            {

                printf("timer2周期定时器启动成功\r\n");

            }

        }

        else if (key == KEY2_PRESS)

        {

            /* 停止软件定时器 */

            res = xTimerStop(timer1_handle, portMAX_DELAY);

            if (res != pdFAIL)

            {

                printf("timer1单次定时器停止成功\r\n");

            }

            res = xTimerStop(timer2_handle, portMAX_DELAY);

            if (res != pdFAIL)

            {

                printf("timer2周期定时器停止成功\r\n");

            }

        }

        vTaskDelay(500);

    }

}

void timer1_callback(TimerHandle_t xTimer)

{

    static uint16_t timer1_count = 0;

    printf("timer1超时回调=%d次..\r\n",++timer1_count);

}

void timer2_callback(TimerHandle_t xTimer)

{

    static uint16_t timer2_count = 0;

    printf("timer2超时回调=%d次..\r\n",++timer2_count);

}