FreeRTOS操作系统(详细速通篇)——— 第十五章(完结)

       本专栏将对FreeRTOS进行快速讲解,带你了解并使用FreeRTOS的各部分内容。适用于快速了解FreeRTOS并进行开发、突击面试、对新手小白非常友好。期待您的后续关注和订阅!

目录

软件定时器和低功耗模式

1 软件定时器

1.1 什么是定时器?

1.2 软件定时器的优缺点

1.3 FreeRTOS软件定时器特点

1.4 软件定时器的状态及转换

1.4.1 定时器状态

1.4.2 定时器周期

1.5 结构体及API函数

1.5.1 结构体

1.5.1 相关API函数

1.6 软件定时器实验

2 低功耗模式

2.1 低功耗模式简介

​​​​​2.2 Tickless 模式详解

​​​​2.3 Tickless 模式相关配置项

2.4  Tickless 模式实验


软件定时器和低功耗模式

        本章节将软件定时器和低功耗模式放在一起进行讲解!

1 软件定时器

1.1 什么是定时器?

        定时器是一种可以在设定时间后执行某个操作的工具。想象一下,你在厨房里煮饭,需要在20分钟后关火。你可以设定一个厨房计时器(定时器),20分钟后它会提醒你去关火。

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

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

1.2 软件定时器的优缺点

优点 缺点
硬件定时器数量有限,而软件定时器理论上只需有足够内存,就可以创建多个。 软件定时器相对硬件定时器来说,精度没有那么高(因为它以系统时钟为基准,系统时钟中断优先级较低,容易被打断)。对于需要高精度的场合,不建议使用软件定时器。
使用简单、成本低。

1.3 FreeRTOS软件定时器特点

特点 描述
可裁剪 软件定时器是可裁剪可配置的功能,需将configUSE_TIMERS配置项配置成1。
单次和周期 支持单次定时器和周期定时器。
软件定时器服务任务 在调用vTaskStartScheduler()开启任务调度器的时候,会创建一个用于管理软件定时器的任务,这个任务就叫做软件定时器服务任务。
软件定时器服务任务的作用 负责软件定时器超时的逻辑判断,调用超时软件定时器的超时回调函数,处理软件定时器命令队列。

1.4 软件定时器的状态及转换

1.4.1 定时器状态

        其具备两种状态,休眠状态和运行状态。

状态 描述
休眠态 新创建的软件定时器处于休眠状态,也就是未运行的。
运行态 运行态的定时器,当指定时间到达之后,它的回调函数会被调用。
状态转换 软件定时器可以通过其句柄被引用,但因为没有运行,所以其定时超时回调函数不会被执行。要让软件定时器从休眠态转变为运行态,需要发送命令到命令队列
1.4.2 定时器周期

定时器按周期分为两种:

  • 单次定时器:定时超时后只会执行一次其软件定时器超时回调函数,不会自动重新开启定时,不过可以被手动重新开启。定时超时时间为2个单位时间,开启后,一直以2个时间单位间隔重复执行
  • 周期定时器:一旦启动后会在执行完回调函数后自动重新启动,从而周期地执行其软件定时器回调函数。定时超时时间为1个单位时间,开启后,在第一个超时后就不再执行

1.5 结构体及API函数

1.5.1 结构体

软件定时器结构体如下,了解一下:

typedef struct {
    const char *pcTimerName;               // 软件定时器名字
    ListItem_t xTimerListItem;             // 软件定时器列表项
    TickType_t xTimerPeriodInTicks;        // 软件定时器的周期
    void *pvTimerID;                       // 软件定时器的ID
    TimerCallbackFunction_t pxCallbackFunction; // 软件定时器的回调函数
    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxTimerNumber;         // 软件定时器的编号,调试用
    #endif
    uint8_t ucStatus;                      // 软件定时器的状态
} xTIMER;
1.5.1 相关API函数

        以下为软件相关API函数,中断重复部分函数不再进行再详细介绍。

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

(1)xTimerCreate()

函数作用:动态方式创建软件定时器

函数原型:

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

参数解析:

参数 描述
pcTimerName 软件定时器名。
xTimerPeriodInTicks 定时超时时间,单位:系统时钟节拍。
uxAutoReload 定时器模式,pdTRUE:周期定时器,pdFALSE:单次定时器。
pvTimerID 软件定时器ID,用于多个软件定时器公用一个超时回调函数。
pxCallbackFunction 软件定时器超时回调函数。

返回值
返回 TimerHandle_t 类型的软件定时器句柄,NULL 表示创建失败。

函数举例:

// 创建一个周期性软件定时器,周期为1000个时钟节拍
TimerHandle_t xMyTimer = xTimerCreate("MyTimer", pdMS_TO_TICKS(1000), pdTRUE, 0, vTimerCallback);
if (xMyTimer == NULL) {
    // 创建失败处理
} else {
    // 创建成功
}

(2)xTimerCreateStatic()

函数作用:静态方式创建软件定时器

函数原型:

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

参数解析:

参数 描述
pcTimerName 软件定时器名。
xTimerPeriodInTicks 定时超时时间,单位:系统时钟节拍。
uxAutoReload 定时器模式,pdTRUE:周期定时器,pdFALSE:单次定时器。
pvTimerID 软件定时器ID,用于多个软件定时器公用一个超时回调函数。
pxCallbackFunction 软件定时器超时回调函数。
pxTimerBuffer 静态定时器结构体变量。

返回值
返回 TimerHandle_t 类型的软件定时器句柄,NULL 表示创建失败。

函数举例:

// 定义一个静态定时器缓冲区
StaticTimer_t xTimerBuffer;
TimerHandle_t xMyStaticTimer = xTimerCreateStatic("MyStaticTimer", pdMS_TO_TICKS(1000), pdTRUE, 0, vTimerCallback, &xTimerBuffer);
if (xMyStaticTimer == NULL) {
    // 创建失败处理
} else {
    // 创建成功
}

(3)xTimerStart()

函数作用:开启软件定时器定时

函数原型:

BaseType_t xTimerStart(
    TimerHandle_t xTimer,
    const TickType_t xTicksToWait
);

参数解析:

参数 描述
xTimer 待开启的软件定时器的句柄。
xTicksToWait 发送命令到软件定时器命令队列的最大等待时间。

返回值
返回 pdPASS 表示开启成功,pdFAIL 表示开启失败。

函数举例:

if (xTimerStart(xMyTimer, 0) == pdPASS) {
    // 开启成功
} else {
    // 开启失败处理
}

(4)xTimerStop()

函数作用:xTimerStop()

函数原型:

BaseType_t xTimerStop(
    TimerHandle_t xTimer,
    const TickType_t xTicksToWait
);

参数解析:

参数 描述
xTimer 待停止的软件定时器的句柄。
xTicksToWait 发送命令到软件定时器命令队列的最大等待时间。

返回值
返回 pdPASS 表示停止成功,pdFAIL 表示停止失败。

函数举例:

if (xTimerStop(xMyTimer, 0) == pdPASS) {
    // 停止成功
} else {
    // 停止失败处理
}

(5)xTimerReset()

函数作用:复位软件定时器定时

函数原型:

BaseType_t xTimerReset(
    TimerHandle_t xTimer,
    const TickType_t xTicksToWait
);

参数解析: 

参数 描述
xTimer 待复位的软件定时器的句柄。
xTicksToWait 发送命令到软件定时器命令队列的最大等待时间。

返回值
返回 pdPASS 表示复位成功,pdFAIL 表示复位失败。

函数举例:

if (xTimerReset(xMyTimer, 0) == pdPASS) {
    // 复位成功
} else {
    // 复位失败处理
}

(6)xTimerChangePeriod()

函数作用:更改软件定时器的定时超时时间         

函数原型:

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

参数解析:

参数 描述
xTimer 待更新的软件定时器的句柄。
xNewPeriod 新的定时超时时间,单位:系统时钟节拍。
xTicksToWait 发送命令到软件定时器命令队列的最大等待时间。

返回值
返回 pdPASS 表示更改成功,pdFAIL 表示更改失败。

函数举例:

if (xTimerChangePeriod(xMyTimer, pdMS_TO_TICKS(500), 0) == pdPASS) {
    // 更改成功
} else {
    // 更改失败处理
}

1.6 软件定时器实验

实验设计

         设计两个任务:start_tasktask1start_task 负责创建 task1 任务,并创建两个定时器(单次和周期),task1 用于按键扫描,并对软件定时器进行开启、停止操作。

任务功能

  • start_task:创建 task1 任务,并创建两个软件定时器(单次定时器和周期定时器)。
  • task1:扫描按键,根据按键操作开启或停止软件定时器。

代码示例

#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include 

// 任务句柄
TaskHandle_t StartTask_Handle;
TaskHandle_t Task1_Handle;
TimerHandle_t Timer1_Handle;
TimerHandle_t Timer2_Handle;

// 定时器回调函数声明
void vTimerCallback1(TimerHandle_t xTimer);
void vTimerCallback2(TimerHandle_t xTimer);

// 任务函数声明
void StartTask(void *pvParameters);
void Task1(void *pvParameters);

int main(void)
{
    // 创建启动任务
    xTaskCreate((TaskFunction_t)StartTask, "StartTask", 128, NULL, 2, &StartTask_Handle);
    // 启动调度器
    vTaskStartScheduler();

    // 主函数不会再执行到这里
    while(1);
}

// 启动任务:创建任务1和两个定时器
void StartTask(void *pvParameters)
{
    // 创建任务1
    xTaskCreate((TaskFunction_t)Task1, "Task1", 128, NULL, 2, &Task1_Handle);

    // 创建周期定时器,定时时间为2000ms
    Timer1_Handle = xTimerCreate("Timer1", pdMS_TO_TICKS(2000), pdTRUE, (void *)0, vTimerCallback1);

    // 创建单次定时器,定时时间为1000ms
    Timer2_Handle = xTimerCreate("Timer2", pdMS_TO_TICKS(1000), pdFALSE, (void *)0, vTimerCallback2);

    // 启动任务调度器
    vTaskDelete(StartTask_Handle);
}

// 定时器回调函数1:周期定时器回调
void vTimerCallback1(TimerHandle_t xTimer)
{
    printf("Periodic Timer Callback\n");
}

// 定时器回调函数2:单次定时器回调
void vTimerCallback2(TimerHandle_t xTimer)
{
    printf("One-shot Timer Callback\n");
}

// 任务1:按键扫描,并对软件定时器进行开启、停止操作
void Task1(void *pvParameters)
{
    while(1)
    {
        // 模拟按键操作,实际中需要根据硬件按键状态进行判断
        int keyPressed = 1; // 模拟按键按下
        if(keyPressed)
        {
            // 开启周期定时器
            if(xTimerStart(Timer1_Handle, 0) == pdPASS)
            {
                printf("Periodic Timer Started\n");
            }

            // 开启单次定时器
            if(xTimerStart(Timer2_Handle, 0) == pdPASS)
            {
                printf("One-shot Timer Started\n");
            }
        }

        // 模拟按键释放,停止定时器
        int keyReleased = 1; // 模拟按键释放
        if(keyReleased)
        {
            // 停止周期定时器
            if(xTimerStop(Timer1_Handle, 0) == pdPASS)
            {
                printf("Periodic Timer Stopped\n");
            }

            // 停止单次定时器
            if(xTimerStop(Timer2_Handle, 0) == pdPASS)
            {
                printf("One-shot Timer Stopped\n");
            }
        }

        // 模拟任务延时
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

        

2 低功耗模式

2.1 低功耗模式简介

       在许多应用场合,对功耗的要求非常严格,例如可穿戴设备和物联网设备。一般 MCU 都有相应低功耗模式,而FreeRTOS 也提供了一个名为 Tickless 的低功耗模式,以便在运行 FreeRTOS 操作系统的应用中方便地实现低功耗。

低功耗模式主要有以下三种:

模式 描述
睡眠模式 最低功耗模式,MCU 停止所有活动,等待中断或事件唤醒。
停止模式 在睡眠模式的基础上关闭更多的时钟源。
待机模式 关闭所有时钟源,最小功耗,需外部事件唤醒。
​​​​​2.2 Tickless 模式详解

        Tickless 模式的设计思想是在系统进入空闲任务时,MCU 进入低功耗模式;当其他任务准备运行时,唤醒 MCU 退出低功耗模式。

  • 空闲任务:是在系统中所有其他任务都阻塞或挂起时运行的任务。
  • 进入低功耗模式:通过调用指令 WFIWFE 实现睡眠模式。

Tickless 模式的关键在于:

  1. 准确唤醒:进入低功耗后,需准确唤醒下一个需要运行的任务。
  2. 处理中断:任何中断均可唤醒 MCU,但频繁的滴答定时器中断会影响低功耗效果。退出低功耗后需补上系统时钟节拍数。
​​​​2.3 Tickless 模式相关配置项
配置项 描述
configUSE_TICKLESS_IDLE 此宏用于使能低功耗 Tickless 模式。
configEXPECTED_IDLE_TIME_BEFORE_SLEEP 此宏用于定义系统进入相应低功耗模式的最短时长。
configPRE_SLEEP_PROCESSING(x) 此宏用于定义需要在系统进入低功耗模式前执行的事务,如:进入低功耗前关闭外设时钟,以达到降低功耗的目的。
configPOST_SLEEP_PROCESSING(x) 此宏用于定义需要在系统退出低功耗模式后执行的事务,如:退出低功耗后开启之前关闭的外设时钟,以使系统能够正常运行。

示例配置:

#define configUSE_TICKLESS_IDLE               1
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2

void configPRE_SLEEP_PROCESSING(TickType_t xExpectedIdleTime) {
    // 关闭外设时钟,降低功耗
}

void configPOST_SLEEP_PROCESSING(TickType_t xExpectedIdleTime) {
    // 开启外设时钟,恢复系统运行
}
2.4  Tickless 模式实验

实验目的:学习使用 FreeRTOS 中的低功耗 Tickless 模式,并观察该模式是否对功耗有明显降低。

实验设计:将在原先二值信号量的课堂源码中,加入低功耗模式,最后对比这两个实验的功耗结果,观察 Tickless 模式对于降低功耗是否有用。

代码示例:

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

// 定义二值信号量句柄
SemaphoreHandle_t xBinarySemaphore;

void vTask1(void *pvParameters);
void vTask2(void *pvParameters);

int main(void) {
    // 创建二值信号量
    xBinarySemaphore = xSemaphoreCreateBinary();
    if (xBinarySemaphore != NULL) {
        // 创建任务1
        xTaskCreate(vTask1, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
        // 创建任务2
        xTaskCreate(vTask2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
        
        // 启动调度器
        vTaskStartScheduler();
    }

    // 主函数不会再执行到这里
    for(;;);
}

void vTask1(void *pvParameters) {
    for(;;) {
        // 等待信号量
        if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
            // 信号量被取走后的处理
        }
    }
}

void vTask2(void *pvParameters) {
    for(;;) {
        // 释放信号量
        xSemaphoreGive(xBinarySemaphore);
        // 模拟任务处理
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// 进入低功耗前处理
void configPRE_SLEEP_PROCESSING(TickType_t xExpectedIdleTime) {
    // 关闭外设时钟,降低功耗
}

// 退出低功耗后处理
void configPOST_SLEEP_PROCESSING(TickType_t xExpectedIdleTime) {
    // 开启外设时钟,恢复系统运行
}

       本专栏将对FreeRTOS进行快速讲解,带你了解并使用FreeRTOS的各部分内容。期待诸君的关注和订阅!

你可能感兴趣的:(快速通关FreeRTOS,stm32,FreeRTOS,单片机,嵌入式,嵌入式软件,c语言,操作系统)