FreeRTOS(任务通知&软件定时&低功耗&内存管理)

STM32FreeRTOS项目 FreeRTOS中的调度器是确保任务能够高效、有序执行的关键组件。通过提供抢占式调度和时间片轮转调度等策略,FreeRTOS能够满足不同实时系统的需求。同时,调度器的实现也涉及多个方面,需要仔细设计和实现以确保系统的稳定性和可靠性。FreeRTOS采用基于优先级的调度策略,支持可剥夺型内核。优先级高的任务一旦就绪就能剥夺优先级较低任务的CPU使用权,从而提高了系统的实时响应能力。对于相同优先级的任务,FreeRTOS支持轮换调度算法,确保任务能够轮流执FreeRTOS动态任务的创建与删除是通过调用和。 阅读详情

目录:

目录:

十四、 FreeRTOS任务通知

14.1 任务通知的简介(了解)

14.2 任务通知相关API函数介绍(熟悉)

14.3 任务通知模拟信号量实验

14.4 任务通知模拟消息邮箱实验

14.5 任务通知模拟事件标志组实验

十五、软件定时器

15.1 简介

15.2 软件定时器状态

15.3 单次定时器和周期定时器

15.4 相关API函数

 15.5 实验

十六、Tickless低功耗模式

16.1 简介

16.2 详解

16.3 Tickless模式相关配置项

十七、 FreeRTOS内存管理

17.1 简介

17.2 内存管理算法

17.2.1 heap_1 算法

17.2.2 heap_2 算法

17.2.3 heap_3 算法

17.2.4 heap_4 算法

17.2.5 heap_5 算法

17.3 相关API函数介绍

17.4 实验


十四、 FreeRTOS任务通知

14.1 任务通知的简介(了解)

        任务通知是 FreeRTOS 中一种用于任务间通信的机制,它允许一个任务向其他任务发送简单的通知或信号,以实现任务间的同步和协作。任务通知通常用于替代二值信号量或事件标志组,提供了更轻量级的任务间通信方式。
大多数任务间通信方法通过中间对象,如队列、信号量或事件组。发送任务写入通信对象,接收任务从通信对象读取。当使用直接任务通知时,顾名思义,发送任务直接向接收任务发送通知,而无需中间对象。

        每个 RTOS 任务都有一个任务通知组,每条通知均独立运行,都有“挂起”或“非挂起”的通知状态,以及一个 32 位通知值。常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 可设置任务通知组中的索引数量。在 FreeRTOS V10.4.0 版本前,任务只有单条任务通知(即只能一对一),没有任务通知组。
        向任务发送“任务通知” 会将目标任务通知设为“挂起”状态。 正如任务可以阻塞中间对象 (如等待信号量可用的信号量),任务也可以阻塞任务通知, 以等待通知状态变为“挂起”。向任务发送“任务通知”也可以更新目标通知的值(可选),可使用下列任一方法:

  1. 覆盖原值,无论接收任务是否已读取被覆盖的值。
  2. 覆盖原值(仅当接收任务已读取被覆盖的值时)。
  3. 在值中设置一个或多个位。
  4. 对值进行增量(添加 1)。

RTOS 任务通知功能默认为启用状态,将configUSE_TASK_NOTIFICATIONS 设为0可以禁用。

14.2 任务通知相关API函数介绍(熟悉)

任务通知相关函数如下:

函数

描述

xTaskNotify()

发送通知,带有通知值

xTaskNotifyAndQuery()

发送通知,带有通知值并且保留接收任务的原通知值

xTaskNotifyGive()

发送通知,不带通知值

xTaskNotifyFromISR()

在中断中发送任务通知

xTaskNotifyAndQueryFromISR()

vTaskNotifyGiveFromISR()

ulTaskNotifyTake()

获取任务通知,可选退出函数时对通知值清零或减1

xTaskNotifyWait()

获取任务通知,可获取通知值和清除通知值的指定位

注意:发送通知有相关ISR函数,接收通知没有ISR函数,不能在ISR中接收任务通知。

14.3 任务通知模拟信号量实验

将任务通知用作轻量级二进制信号量:

  1. start_task:用来创建其他2个任务。
  2. task1:用于按键扫描,当检测到按键KEY1被按下时,将发送任务通知。
  3. task2:用于接收任务通知,并打印相关提示信息。

 freertos_demo.c代码清单:

//1)任务配置
/* 启动任务函数 */
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_DEPTH 128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);

/* Task1 任务 配置 */
#define TASK1_PRIORITY 2
#define TASK1_STACK_DEPTH 128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);

/* Task2 任务 配置 */
#define TASK2_PRIORITY 3
#define TASK2_STACK_DEPTH 128
TaskHandle_t task2_handler;
void Task2(void *pvParameters);
//2)入口函数
/**
 * @description: FreeRTOS入口函数:创建任务函数并开始调度
 * @return {*}
 */
void FreeRTOS_Start(void)
{
    xTaskCreate((TaskFunction_t)Start_Task,
                (char *)"Start_Task",
                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)START_TASK_PRIORITY,
                (TaskHandle_t *)&start_task_handler);
    vTaskStartScheduler();
}
//3)初始任务函数
void Start_Task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   Task1,
                (char *                 )   "Task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIORITY,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   Task2,
                (char *                 )   "Task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIORITY,
                (TaskHandle_t *         )   &task2_handler );
    vTaskDelete(NULL);                  
    taskEXIT_CRITICAL();                /* 退出临界区 */
}
//4)task1任务函数
/**
 * @description: 发送任务通知值
 * @param {void *} pvParameters
 * @return {*}
 */
void Task1(void * pvParameters)
{
    uint8_t key = 0;
    
    while(1) 
    {
        key = Key_Detect();
        if(key == KEY1_PRESS)
        {
            printf("任务通知模拟二值信号量释放\r\n");
            xTaskNotifyGive(task2_handler);
        }
        vTaskDelay(10);
    }
}
//5)task2任务函数
/**
 * @description: 接收任务通知值
 * @param {void *} pvParameters
 * @return {*}
 */
void Task2(void * pvParameters)
{
    uint32_t rev = 0;
    while(1)
    {
        rev = ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
        if(rev != 0)
        {
            printf("接收任务通知成功,模拟获取二值信号量\r\n");
        }
    }
}

14.4 任务通知模拟消息邮箱实验

将任务通知用作轻量级邮箱:

  • start_task:用来创建其他2个任务。
  • task1:用于按键扫描,将按下的按键键值通过任务通知发送给指定任务。
  • task2:用于接收任务通知,并根据接收到的数据做相应动作。

freertos_demo.c代码清单:

//1)任务配置
/* 启动任务函数 */
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_DEPTH 128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);

/* Task1 任务 配置 */
#define TASK1_PRIORITY 2
#define TASK1_STACK_DEPTH 128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);

/* Task2 任务 配置 */
#define TASK2_PRIORITY 3
#define TASK2_STACK_DEPTH 128
TaskHandle_t task2_handler;
void Task2(void *pvParameters);
//2)入口函数
/**
 * @description: FreeRTOS入口函数:创建任务函数并开始调度
 * @return {*}
 */
void FreeRTOS_Start(void)
{
    xTaskCreate((TaskFunction_t)Start_Task,
                (char *)"Start_Task",
                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)START_TASK_PRIORITY,
                (TaskHandle_t *)&start_task_handler);
    vTaskStartScheduler();
}
//3)初始任务函数
void Start_Task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   Task1,
                (char *                 )   "Task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIORITY,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   Task2,
                (char *                 )   "Task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIORITY,
                (TaskHandle_t *         )   &task2_handler );
    vTaskDelete(NULL);                  
    taskEXIT_CRITICAL();                /* 退出临界区 */
}
//4)task1任务函数
/**
 * @description: 发送任务通知值
 * @param {void *} pvParameters
 * @return {*}
 */
void Task1(void *pvParameters)
{
    uint8_t key = 0;

    while (1)
    {
        key = Key_Detect();
        if ((key != 0) && (task2_handler != NULL))
        {
            printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n", key);
            xTaskNotify(task2_handler, key, eSetValueWithOverwrite);
        }
        vTaskDelay(10);
    }
}
//5)task2任务函数
/**
 * @description: 接收任务通知值
 * @param {void *} pvParameters
 * @return {*}
 */
void Task2(void *pvParameters)
{
    uint32_t noyify_val = 0;
    while (1)
    {
        xTaskNotifyWait(0, 0xFFFFFFFF, &noyify_val, portMAX_DELAY);
        switch (noyify_val)
        {
        case KEY1_PRESS:
        {
            printf("接收到的通知值为:%d\r\n", noyify_val);
            LED_Toggle(LED1_Pin);
            break;
        }
        case KEY2_PRESS:
        {
            printf("接收到的通知值为:%d\r\n", noyify_val);
            LED_Toggle(LED2_Pin);
            break;
        }
        default:
            break;
        }
    }
}

14.5 任务通知模拟事件标志组实验

将任务通知用作轻量级事件标志组:

  • start_task:用来创建其他2个任务。
  • task1:用于按键扫描,当检测到按键按下时,发送任务通知设置不同标志位。
  • task2:用于接收任务通知,并打印相关提示信息。

freertos_demo.c代码:

//1)任务配置
/* 启动任务函数 */
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_DEPTH 128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);

/* Task1 任务 配置 */
#define TASK1_PRIORITY 2
#define TASK1_STACK_DEPTH 128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);

/* Task2 任务 配置 */
#define TASK2_PRIORITY 3
#define TASK2_STACK_DEPTH 128
TaskHandle_t task2_handler;
void Task2(void *pvParameters);

#define EVENTBIT_0  (1 << 0)
#define EVENTBIT_1  (1 << 1)
//2)入口函数
/**
 * @description: FreeRTOS入口函数:创建任务函数并开始调度
 * @return {*}
 */
void FreeRTOS_Start(void)
{
    xTaskCreate((TaskFunction_t)Start_Task,
                (char *)"Start_Task",
                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)START_TASK_PRIORITY,
                (TaskHandle_t *)&start_task_handler);
    vTaskStartScheduler();
}
//3)初始任务函数
void Start_Task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   Task1,
                (char *                 )   "Task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIORITY,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   Task2,
                (char *                 )   "Task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIORITY,
                (TaskHandle_t *         )   &task2_handler );
    vTaskDelete(NULL);                  
    taskEXIT_CRITICAL();                /* 退出临界区 */
}
//4)task1任务函数
/**
 * @description: 发送任务通知值
 * @param {void *} pvParameters
 * @return {*}
 */
void Task1(void *pvParameters)
{
    uint8_t key = 0;
    while (1)
    {
        key = Key_Detect();
        if (key == KEY1_PRESS)
        {
            printf("将bit0位置1\r\n");
            xTaskNotify(task2_handler, EVENTBIT_0, eSetBits);
        }
        else if (key == KEY2_PRESS)
        {
            printf("将bit1位置1\r\n");
            xTaskNotify(task2_handler, EVENTBIT_1, eSetBits);
        }
        vTaskDelay(10);
    }
}
//5)task2任务函数
/**
 * @description: 接收任务通知值
 * @param {void *} pvParameters
 * @return {*}
 */
void Task2(void *pvParameters)
{
    uint32_t notify_val = 0,event_bit = 0;
    while(1)
    {
        xTaskNotifyWait( 0, 0xFFFFFFFF, &notify_val, portMAX_DELAY );
        if(notify_val & EVENTBIT_0)
        {
            event_bit |= EVENTBIT_0;
        }
        if(notify_val & EVENTBIT_1)
        {
            event_bit |= EVENTBIT_1;
        }
        if(event_bit == (EVENTBIT_0|EVENTBIT_1))
        {
            printf("任务通知模拟事件标志组接收成功\r\n");
            event_bit = 0;
        }
    }
}

十五、软件定时器

15.1 简介

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

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

软件定时器

硬件定时器

FreeRTOS提供的功能来模拟定时器,依赖系统的任务调度器来进行计时和任务调度

由芯片或微控制器提供,独立于 CPU,可以在后台运行,不受任务调度器的影响

精度和分辨率可能受到任务调度的影响

具有更高的精度和分辨率

不需要额外的硬件资源,但可能会增加系统的负载

占用硬件资源,不会增加 CPU 的负载

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

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

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

15.2 软件定时器状态

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

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

15.3 单次定时器和周期定时器

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

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

15.4 相关API函数

函数

描述

xTimerCreate()

动态方式创建软件定时器

xTimerCreateStatic()

静态方式创建软件定时器

xTimerStart()

开启软件定时器定时

xTimerStartFromISR()

在中断中开启软件定时器定时

xTimerStop()

停止软件定时器定时

xTimerStopFromISR()

在中断中停止软件定时器定时

xTimerReset()

复位软件定时器定时

xTimerResetFromISR()

在中断中复位软件定时器定时

xTimerChangePeriod()

更改软件定时器的定时超时时间

xTimerChangePeriodFromISR()

在中断中更改定时超时时间

 15.5 实验

学习使用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:

//1)引入头文件
#include "timers.h"
//2)任务配置
/* 启动任务函数 */
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_DEPTH 128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);

/* Task1 任务 配置 */
#define TASK1_PRIORITY 2
#define TASK1_STACK_DEPTH 128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);

void timer1_callback( TimerHandle_t pxTimer );
void timer2_callback( TimerHandle_t pxTimer );
//3)入口函数
/**
 * @description: FreeRTOS入口函数:创建任务函数并开始调度
 * @return {*}
 */
void FreeRTOS_Start(void)
{
    xTaskCreate((TaskFunction_t)Start_Task,
                (char *)"Start_Task",
                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)START_TASK_PRIORITY,
                (TaskHandle_t *)&start_task_handler);
    vTaskStartScheduler();
}
//4)初始任务函数
TimerHandle_t timer1_handle = 0; /* 单次定时器 */
TimerHandle_t timer2_handle = 0; /* 周期定时器 */
void Start_Task(void *pvParameters)
{
    taskENTER_CRITICAL(); /* 进入临界区 */
    /* 创建单次定时器 */
    timer1_handle = xTimerCreate("timer1",
                                 500,
                                 pdFALSE,
                                 (void *)1,
                                 timer1_callback);

    /* 创建周期定时器 */
    timer2_handle = xTimerCreate("timer2",
                                 2000,
                                 pdTRUE,
                                 (void *)2,
                                 timer2_callback);

    xTaskCreate((TaskFunction_t)Task1,
                (char *)"Task1",
                (configSTACK_DEPTH_TYPE)TASK1_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)TASK1_PRIORITY,
                (TaskHandle_t *)&task1_handler);
    vTaskDelete(NULL);
    taskEXIT_CRITICAL(); /* 退出临界区 */
}
//5)task1任务函数
/**
 * @description: 根据按键控制软件定时器
 * @param {void *} pvParameters
 * @return {*}
 */
void Task1(void *pvParameters)
{
    uint8_t key = 0;
    while (1)
    {
        key = Key_Detect();
        if (key == KEY1_PRESS)
        {
            xTimerStart(timer1_handle, portMAX_DELAY);
            xTimerStart(timer2_handle, portMAX_DELAY);
        }
        else if (key == KEY2_PRESS)
        {
            xTimerStop(timer1_handle, portMAX_DELAY);
            xTimerStop(timer2_handle, portMAX_DELAY);
        }
        vTaskDelay(10);
    }
}
//6)超时回调函数
/**
 * @description: timer1的超时回调函数
 * @param {TimerHandle_t} pxTimer 定时器句柄
 * @return {*}
 */
void timer1_callback(TimerHandle_t pxTimer)
{
    static uint32_t timer = 0;
    printf("timer1的运行次数=%d\r\n", ++timer);
}

/**
 * @description: timer2的超时回调函数
 * @param {TimerHandle_t} pxTimer 定时器句柄
 * @return {*}
 */
void timer2_callback(TimerHandle_t pxTimer)
{
    static uint32_t timer = 0;
    printf("timer2的运行次数=%d\r\n", ++timer);
}

十六、Tickless低功耗模式

16.1 简介

FreeRTOS 的 Tickless 模式是一种特殊的运行模式,用于最小化系统的时钟中断频率,以降低功耗。在 Tickless 模式下,系统只在有需要时才会启动时钟中断,而在无任务要运行时则完全进入休眠状态,从而降低功耗。在滴答中断重启时,会对 RTOS 滴答计数值进行校正调整。

Tickless模式的实现方式通常依赖于微控制器的硬件特性,尤其是低功耗定时器或实时时钟单元。以下是 Tickless 模式的一般工作原理:

  • 空闲任务检测:FreeRTOS 会通过空闲任务(Idle Task)来检测系统是否有任务需要执行。如果没有任务需要执行,系统可以进入休眠状态。
  • 时钟中断:当有任务需要执行时,系统会启动时钟中断,唤醒处理器。
  • 时钟中断处理:在时钟中断处理函数中,FreeRTOS 将检查任务的状态并决定是否继续执行。
  • 休眠状态:如果没有任务需要执行,系统可以进入休眠状态,关闭时钟中断。在休眠状态下,处理器可以进入更低功耗的模式。
  • 任务唤醒:当有任务需要执行时,系统会再次启动时钟中断,唤醒处理器,然后执行相应的任务。

在 Tickless 模式下,系统的时钟中断频率明显降低,从而降低了系统的平均功耗。Tickless 模式适用于那些对功耗要求较高、需要长时间运行在低功耗状态的嵌入式系统。比如:电池驱动设备、物联网(IoT)设备、低功耗传感器节点、无线通信模块等。

16.2 详解

STM32F103xC、STM32F103xD和STM32F103xE增强型产品支持三种低功耗模式,可以在要求低功耗、短启动时间和多种唤醒事件之间达到最佳的平衡。

1)睡眠模式(Sleep Mode)

只有CPU停止,所有外设处于工作状态并可在发生中断/事件时唤醒CPU。

2)停机模式(Stop Mode)

在保持SRAM和寄存器内容不丢失的情况下,停机模式可以达到最低的电能消耗。在停机模式下,停止所有内部1.8V部分的供电,PLL、HSI的RC振荡器和HSE晶体振荡器被关闭,调压器可以被置于普通模式或低功耗模式。可以通过任一配置成EXTI的信号把微控制器从停机模式中唤醒,EXTI信号可以是16个外部I/O 口之一、PVD的输出、RTC闹钟或USB的唤醒信号。

3)待机模式(Standby Mode)

在待机模式下可以达到最低的电能消耗。内部的电压调压器被关闭,因此所有内部1.8V部分的供电被切断;PLL、HSI的RC振荡器和HSE晶体振荡器也被关闭;进入待机模式后,SRAM和寄存器的内容将消失,但后备寄存器的内容仍然保留,待机电路仍工作。从待机模式退出的条件是:NRST上的外部复位信号、IWDG复位、WKUP引脚上的一个上升边 沿或RTC的闹钟到时。

注意:在进入停机或待机模式时,RTC、IWDG和对应的时钟不会被停止。

主要使用睡眠模式,任何中断或事件都可以唤醒睡眠模式。Tickless低功耗模式通过调用指令 __WFI 实现睡眠模式

FreeRTOS系统中的所有其它任务都不在运行时(处于阻塞或挂起),会运行空闲任务。所以想不影响系统运行又降低功耗,可以在空闲任务执行的期间,让MCU 进入相应的低功耗模式。

由于滴答定时器频繁中断则会影响低功耗,所以FreeRTOS的Tickless低功耗模式会自动把滴答定时器的中断周期修改为低功耗运行时间,退出低功耗后再补上系统时钟节拍数。

16.3 Tickless模式相关配置项

配置项

说明

configUSE_TICKLESS_IDLE

使能低功耗 Tickless 模式,默认0

configEXPECTED_IDLE_TIME_BEFORE_SLEEP

系统进入相应低功耗模式的最短时长,默认2

configPRE_SLEEP_PROCESSING(x)

在系统进入低功耗模式前执行的事务,比如关闭外设时钟

configPOST_SLEEP_PROCESSING(x)

系统退出低功耗模式后执行的事务,比如开启之前关闭的外设时钟

 16.4 实验

使用 FreeRTOS 中的Tickless低功耗模式:在11.3二值信号量实验案例中,加入低功耗模式,对比功耗结果,观察是否降低功耗。

FreeRTOSConfig.h代码清单:

#define configUSE_TICKLESS_IDLE                         1

#include "freertos_demo.h"

#define configPRE_SLEEP_PROCESSING( x )         PRE_SLEEP_PROCESSING()

#define configPOST_SLEEP_PROCESSING( x )        POST_SLEEP_PROCESSING()

 ​​​​​​​freertos_demo.c代码清单

//1)引入信号量头文件
#include "semphr.h"
//2)低功耗处理函数
/* 进入低功耗前所需要执行的操作 */
void PRE_SLEEP_PROCESSING(void)
{
    __HAL_RCC_GPIOA_CLK_DISABLE();
    __HAL_RCC_GPIOB_CLK_DISABLE();
    __HAL_RCC_GPIOC_CLK_DISABLE();
    __HAL_RCC_GPIOD_CLK_DISABLE();
    __HAL_RCC_GPIOE_CLK_DISABLE();
    __HAL_RCC_GPIOF_CLK_DISABLE();
    __HAL_RCC_GPIOG_CLK_DISABLE();
}
/* 退出低功耗后所需要执行的操作 */
void POST_SLEEP_PROCESSING(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();
}
//3)任务配置
/* 启动任务函数 */
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_DEPTH 128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);

/* Task1 任务 配置 */
#define TASK1_PRIORITY 2
#define TASK1_STACK_DEPTH 128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);

/* Task2 任务 配置 */
#define TASK2_PRIORITY 3
#define TASK2_STACK_DEPTH 128
TaskHandle_t task2_handler;
void Task2(void *pvParameters);
//4)入口函数
QueueHandle_t semphore_handle;
/**
 * @description: FreeRTOS入口函数:创建任务函数并开始调度
 * @return {*}
 */
void FreeRTOS_Start(void)
{
    semphore_handle = xSemaphoreCreateBinary();
    if (semphore_handle != NULL)
    {
        printf("二值信号量创建成功\r\n");
    }
    xTaskCreate((TaskFunction_t)Start_Task,
                (char *)"Start_Task",
                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)START_TASK_PRIORITY,
                (TaskHandle_t *)&start_task_handler);
    vTaskStartScheduler();
}
//5)初始任务函数
void Start_Task(void *pvParameters)
{
    taskENTER_CRITICAL(); /* 进入临界区 */
    xTaskCreate((TaskFunction_t)Task1,
                (char *)"Task1",
                (configSTACK_DEPTH_TYPE)TASK1_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)TASK1_PRIORITY,
                (TaskHandle_t *)&task1_handler);

    xTaskCreate((TaskFunction_t)Task2,
                (char *)"Task2",
                (configSTACK_DEPTH_TYPE)TASK2_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)TASK2_PRIORITY,
                (TaskHandle_t *)&task2_handler);
    vTaskDelete(NULL);
    taskEXIT_CRITICAL(); /* 退出临界区 */
}
//6)task1任务函数
/**
 * @description: 释放二值信号量
 * @param {void *} pvParameters
 * @return {*}
 */
void Task1(void *pvParameters)
{
    uint8_t key = 0;
    BaseType_t err;
    while (1)
    {
        key = Key_Detect();
        if (key == KEY1_PRESS)
        {
            if (semphore_handle != NULL)
            {
                err = xSemaphoreGive(semphore_handle);
                if (err == pdPASS)
                {
                    printf("信号量释放成功\r\n");
                }
                else
                    printf("信号量释放失败\r\n");
            }
        }
        vTaskDelay(10);
    }
}
//7)task2任务函数
/**
 * @description: 获取二值信号量
 * @param {void *} pvParameters
 * @return {*}
 */
void Task2(void *pvParameters)
{
    uint32_t i = 0;
    BaseType_t err;
    while (1)
    {
        /* 一直等待获取信号量 */
        err = xSemaphoreTake(semphore_handle, portMAX_DELAY); 
        if (err == pdTRUE)
        {
            printf("获取信号量成功\r\n");
        }
        else
        {
            printf("已超时%d\r\n", ++i);
        }
    }
}

十七、 FreeRTOS内存管理

17.1 简介

在使用 FreeRTOS 创建任务、队列、信号量等对象时,通常都有动态创建和静态创建的方式。动态方式提供了更灵活的内存管理,而静态方式则更注重内存的静态分配和控制。

如果是动态创建的,那么标准 C 库 malloc() 和 free() 函数有时可用于此目的,但是有以下缺点:

  • 它们在嵌入式系统上并不总是可用。
  • 它们占用了宝贵的代码空间。
  • 它们不是线程安全的。
  • 它们不是确定性的 (执行函数所需时间将因调用而异)。
  • ...

所以更多的时候需要的不是一个替代的内存分配实现。一个嵌入式/实时系统的 RAM 和定时要求可能与另一个非常不同,所以单一的 RAM 分配算法将永远只适用于一个应用程序子集。为了避免此问题,FreeRTOS 将内存分配 API 保留在其可移植层,提供了五种内存管理算法:

  • heap_1:最简单,不允许释放内存。
  • heap_2:允许释放内存,但不会合并相邻的空闲块。
  • heap_3:简单包装了标准 malloc() 和 free(),以保证线程安全。
  • heap_4:合并相邻的空闲块以避免碎片化。包含绝对地址放置选项。
  • heap_5:如同 heap_4,能够跨越多个不相邻内存区域的堆。

17.2 内存管理算法

17.2.1 heap_1 算法

heap_1 是最简单的实现方式。内存一经分配,它不允许内存再被释放。尽管如此,heap_1.c 还是适用于大量嵌入式应用程序。这是因为许多小型和深度嵌入的应用程序在系统启动时创建了所需的所有任务、队列、信号量等,并在程序的生命周期内使用所有这些对象(直到应用程序再次关闭或重新启动)。任何内容都不会被删除。

17.2.2 heap_2 算法

heap_2 使用最佳适应算法,并且与方案 1 不同,它允许释放先前分配的块,它不将相邻的空闲块组合成一个大块。

heap_2.c 适用于许多必须动态创建对象的小型实时系统 。

  • 如果动态地创建和删除任务,且分配给正在创建任务的堆栈大小总是相同的,那么 heap2.c 可以在大多数情况下使用。
  • 但是,如果分配给正在创建任务的堆栈的大小不是总相同,那么可用的空闲内存可能会被碎片化成许多小块,最终导致分配失败。

heap_2 使用最佳适应算法,该算法在空闲内存中选择与请求的内存大小最接近的块来分配内存。下面是一个简单的例子来说明最佳适应算法:

假设有一个空闲内存,其中包含以下块:

  • 大小为 20 字节的空闲块。
  • 大小为 15 字节的空闲块。
  • 大小为 25 字节的空闲块。

现在有一个任务请求分配 18 字节的内存。最佳适应算法将选择大小为 20 字节的块,因为它与请求的大小最接近。在选择这个块后,分配器可能会将该块分割为两部分,一部分大小为 18 字节,用于任务的内存,另一部分大小为 2 字节,留作未分配的块。

17.2.3 heap_3 算法

heap_3使用 C 库的 malloc 和 free 函数来进行内存分配和释放。它通过分配固定大小的块来管理内存,这些块的大小在配置 FreeRTOS 时进行定义,不会动态改变。

假设我们使用 Heap_3 管理内存,其中块的大小固定为 32 字节。初始时,整个内存被分割成大小为 32 字节的块:

  • 块 1(32 字节)。
  • 块 2(32 字节)。
  • 块 3(32 字节)。

现在,有一个任务请求分配 20 字节的内存。Heap_3 算法将选择块 1,并将其分割成两部分:

  • 分配给任务的内存块(20 字节)。
  • 剩余未分配的块(12 字节)。

再假设另一个任务请求分配 40 字节的内存。由于没有足够大的块可供分配,heap_3 将返回分配失败的状态。

heap_3 的特点是块大小固定,这样可以简化内存管理。然而,也因为块大小不可变,可能导致内存碎片问题,即一些块可能无法完全被利用,从而浪费了一些内存。

17.2.4 heap_4 算法

heap_4使用第一适应算法,并且会将相邻的空闲内存块合并成大内存块,减少内存碎片。

第一适应算法会在可用内存块中选择第一个足够大的内存块进行分配。

假设有一个内存块链表,其中包含以下顺序的内存块:

  • 大小为 40 字节的块。
  • 大小为 30 字节的块。
  • 大小为 15 字节的块。
  • 大小为 20 字节的块。

如果一个任务需要申请 25 字节的内存,第一适应算法将选择大小为 40 字节的块,因为它是第一个足够大以容纳任务需求的内存块。(如果是heap_2的最佳适应算法,会选择30字节的块)

17.2.5 heap_5 算法

heap_5使用与 heap_4 相同的第一适应和内存合并算法,允许堆跨越多个不相邻(非连续)内存区域。适用于内存地址不连续的复杂场景。

17.3 相关API函数介绍

函数

描述

void * pvPortMalloc( size_t  xWantedSize );

申请内存

void  vPortFree( void * pv );

释放内存

size_t  xPortGetFreeHeapSize( void );

获取当前空闲内存的大小

17.4 实验

学习FreeRTOS 内存管理的函数,观察内存变化情况:

  • start_task:用来创建其他1个任务。
  • task1:用于按键扫描,当KEY1按下则申请内存,当KEY2按下则释放内存,并打印剩余内存信息。

freertos_demo.c代码清单:

//1)任务配置
/* 启动任务函数 */
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_DEPTH 128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);

/* Task1 任务 配置 */
#define TASK1_PRIORITY 2
#define TASK1_STACK_DEPTH 128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);
//2)入口函数
/**
 * @description: FreeRTOS入口函数:创建任务函数并开始调度
 * @return {*}
 */
void FreeRTOS_Start(void)
{
    xTaskCreate((TaskFunction_t)Start_Task,
                (char *)"Start_Task",
                (configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
                (void *)NULL,
                (UBaseType_t)START_TASK_PRIORITY,
                (TaskHandle_t *)&start_task_handler);
    vTaskStartScheduler();
}
//3)初始任务函数
void Start_Task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   Task1,
                (char *                 )   "Task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_DEPTH,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIORITY,
                (TaskHandle_t *         )   &task1_handler );
    vTaskDelete(NULL);                  
    taskEXIT_CRITICAL();                /* 退出临界区 */
}
//4)task1任务函数
/**
 * @description: 申请及释放内存,并显示空闲内存大小
 * @param {void *} pvParameters
 * @return {*}
 */
void Task1(void * pvParameters)
{
    uint8_t key = 0, t = 0;
    uint8_t * buf = NULL;
    while(1) 
    {
        key = Key_Detect();
        if(key == KEY1_PRESS)
        {
            /* 申请内存 */
            buf = pvPortMalloc(30);                 
            if(buf != NULL)
            {
                printf("申请内存成功\r\n");
            }
            else 
            {
                printf("申请内存失败\r\n");
            }
        }
        else if(key == KEY2_PRESS)
        {
            if(buf != NULL)
            {
                /* 释放内存 */
                vPortFree(buf);                     
                printf("释放内存\r\n");
            }                
        }
        if(t++ > 50)
        {
            t = 0;
            printf("剩余的空闲内存大小=%d\r\n",xPortGetFreeHeapSize());
        }
        vTaskDelay(10);
    }
}

FreeRTOS应用开发常用函数汇总 阅读详情

相关推荐

FreeRTOS源码解析——第一章 整体架构

本章主要介绍FreeRTOS的整体框架,给出一个简要的移植示例,文末有示例下载链接。

WangBo的博客 1万+

使用FREERTOS软件定时器组

    刚刚学会用FREERTOS,刚好称公司项目需要,就学习着用,有几个定时需求,本来想使用硬件中断定时器,再用软件做个分组定时,刚好看到FREERTOS有软件定时器组,就想用用试试看效果;    使用的是FREERTOSV10.0.1,MCU用的是STM32F205RET6,定时需要都是10S,2S,3S,最小单位为ms级;FREERTOS默认定时为1MS,即#define configTIC...

yedehui1985的博客 2122

在STMCubeIDE配置FreeRTOS的各种参数

上图: 今天试着在IDE上面配置FreeRTOS,因为之前都是直接在keil上面移植裁剪系统,第一次在IDE上图形化配置导致很多参数都看不懂,很多需要配置的重要参数也找不到位置,所有我把所有可以配置的参数都翻译了一遍。(注:只做翻译,不详细叙述作用) 翻译按照上图中1、2、3、4的顺序 第一部分:Configure paremeters USE_PREEMPTION参数说明:设置为1时使用抢占式RTOS调度器,设置为0时使用合作式RTOS调度器。 TICK_RATE...

luckyzz99的博客 3884

FreeRTOS学习笔记(四)——延时函数,列表,软件定时器,低功耗模式,内存管理

FreeRTOS学习笔记(四)——延时函数,列表,软件定时器,低功耗模式,内存管理

个人风格极强的知识汇总 1807

FreeRTOS实时操作系统(十四)软件定时器

3.软件定时器的超时回调函数是在软件定时器服务任务中被调用的,服务任务不是专为某个定时器服务的,它还要处理其他定时器。1.软件定时器的超时回调函数是由软件定时器服务任务调用的,软件定时器的超时回调函数本身不是任务,因此不能在该回调函数中使用可能会导致任务阻塞的 API 函数。单次定时器:单次定时器的一旦定时超时,只会执行一次其软件定时器超时回调函数,不会自动重新开启定时,不过可以被手动重新开启。软件定时器是指具有定时功能的软件,可以设置定时周期,在回调函数中进行信息处理,只要内存够可以创建非常多。

贾saisai的博客 1007

详解FreeRTOS:专栏总述

本专栏以FreeRTOS源码与嵌入式工程实践为主线,系统讲解多任务机制、任务管理与调度、队列和信号量、软件定时器、事件组、任务通知、内存管理、Tickless低功耗及SystemView性能分析,覆盖从移植入门到内核源码和调试优化的完整学习路径。

不脱发的程序猿 2657

13.FreeRTOS任务通知

涉及FreeRTOS任务通知知识。包含任务通知的简介、任务通知值和通知状态、任务通知相关API函数、任务通知模拟信号量实验以及任务通知模拟消息邮箱和事件标志组实验。

L的博客 1070

FreeRTOS实时操作系统(十三)任务通知

计数型信号量和二值信号量没啥区别,就是多次进行了任务通知,采用pdTRUE的形参在接收任务通知的时候,将通知值全部清空。任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值。用于获取通知值和清除通知值的指定位值,适用于模拟队列和事件标志组,使用该函数来获取任务通知。用于接收任务通知值,可以设置在退出此函数的时候将任务通知值清零或者减一。等待通知:接收方已经准备好了(调用了接收任务通知函数),等待发送方给个通知。

贾saisai的博客 924

FreeRTOS详解

FreeRTOS 1. 任务切换: 使用vTaskDelayUntil、vTaskDelay、xQueueSend函数可以引起任务切换从中断函数中退出后,执行高优先级任务: //如果 xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 portYIELD_FROM_ISR(xHigherPriorityTaskWoken); 栈溢出钩子函数方式一: 用来检测任务栈溢出 说明: 如果栈溢出了,在任务切换的时候会自动调用触发栈溢出钩子函数

路过的熊的博客 1万+

【电机学复习笔记】同步电机的特性及参数的求取

同步电机的主要特性包括:空载特性、短路特性、零功率因数负载特性、外特性和调整特性。 基于以上特性我们可以实现: 从空载特性和短路特性求同步电抗的不饱和值和短路比; 从零功率因数负载特性和空载特性确定保梯电抗(定子漏抗)和电枢反应电动势

Pantrick_的博客 2万+

海康线扫相机平场矫正教程

1、白纸准备确保视野中有一张平整且无折痕的白纸,使其完全铺满相机的整个视野。2、行高设置将行高参数设定为 2048。3、灰度值控制相机端图像的灰度值应维持在 120 - 160 这个区间内。同时,最亮像素点与最暗像素点的灰度值差值不得超过 4 倍。在调整灰度值时,尽量开启增益功能。4、 图像模糊处理为减少白纸对平场矫正的影响,需将相机端获取的图像进行模糊化处理。具体操作是对镜头进行虚焦,使整体图像变得模糊。

T 2743

嵌入式八股文总结(FreeRTOS篇)

最近在准备秋招,在此每天总结一些面试常问的问题,希望可以和大家一起进步。(持续更新中……)

小海螺的博客 1万+

目前最新NIST随机数测试软件下载、安装、及使用教程

最近由于科研需要,在搞一些随机数的测试的事情,众所周知 随机数测试我们可以使用NIST的软件进行多大15项的随机测试,我也在网上查找了很多大牛的博客,但是都是年代久远,很多的链接现在都是物是人非,都是不能直接使用了,今天顺便抽点时间更新一下这个NIST软件的下载及使用。我们首先讲一下该软件的下载:1.我们直接取NIST的官网(https://csrc.nist.gov/)下载即可 ;链接如下:下载...

Miracle.Zhao的博客 3万+

14.FreeRTOS软件定时器

涉及FreeRTOS软件定时器内容。具体包含软件定时器的简介、软件定时器的状态、单次定时器和周期定时器、软件定时器结构体成员、软件定时器相关API函数以及软件定时器实验。

L的博客 1321

发那科机器人Modbus通讯配置全攻略:从IP设置到信号调试

本文详细介绍了发那科机器人Modbus通讯的配置方法,从基础认知到IP设置、端口配置、信号调试及故障排查。重点讲解了UOP和DI/DO信号的配置技巧,帮助工程师快速实现机器人与PLC的稳定通讯,提升自动化系统效率。

weixin_27872611的博客 620

宇树机器人G1二次开发:导览、起始点、目的地与完整示例

通过宇树G1 SDK的导航接口,开发者可以高效地实现机器人的自主移动功能。连接 -> 设置目标点 -> 发送命令 -> 监控状态。本文提供的完整示例涵盖了从环境搭建到代码实现的全部步骤,并解释了起始点、目的地等关键概念。开发者可以在此基础上,结合多目标点巡逻、动态避障(需高级配置)等功能,构建更复杂的机器人应用。建议读者先在小范围、熟悉的平坦环境中测试,成功后再逐步扩展到更复杂的场景。

m0_52645558的博客 744

【ESP32_FreeRTOS篇】

FreeRTOS 是一款 “开源免费”的实时操作系统,遵循的是 GPLv2+的许可协议。这里说 到的开源,指的是你可以免费得获取到 FreeRTOS 的源代码,且当你的产品使用了 FreeRTOS 且没有修改 FreeRTOS 内核源码的时候,你的产品的全部代码都可以闭源,不用开源,但是当 你修改了 FreeRTOS 内核源码的时候,就必须将修改的这部分开源,反馈给社区,其它应用部 分不用开源。免费的意思是无论你是个人还是公司,都可以免费地使用,不需要掏一分钱。

K3169的博客 3681

人工智能的未来:从弱 AI 到通用人工智能(AGI)

从最初的简单自动化程序,到如今的深度学习大模型(如 ChatGPT、DeepSeek、Grok),AI 逐步从弱人工智能(Weak AI)向更智能、更自主的方向发展。AI 的发展将影响就业市场,取代重复性、低技能的工作,同时创造新的高技能岗位。尽管深度学习带来了巨大的进步,但它仍然依赖大量标注数据进行训练,对新环境的泛化能力有限,这也是当前 AI 迈向 AGI 的关键障碍之一。本文将探讨 AI 从弱 AI 进化到 AGI 的可能性,分析目前 AI 技术的局限性,并展望未来 AI 在各个行业的影响。

qq_20245171的博客 1万+

移植FreeRTOS

它不属于内核源码的文件,不过在示例中有。\3. 将FreeRTOS目录下Source之外的文件全部删除,并进入Source目录,保留include、portable与.c文件,其余文件删除。其中在****portable****目录包含了内存管理与多种开发工具下的多种硬件平台的接口,移植时也需要重点关注。对于移植FreeRTOS重点需要关注的是****Source****目录,其它功能根据实际需求来选择。添加完成后,编译将会发现编译错误,提示打不开****FreeRTOSConfig.h****文件。

qq_71306973的博客 1791

OmniParser V2 和 OmniTool 让 AI 来控制 Windows 设备

OmniParser V2 和 OmniTool 是由微软开发的一套工具,通过屏幕解析技术让 AI 能够理解和控制 Windows 设备。OmniParser V2 是一款数据解析工具,可以解读屏幕内容,并将内容传递给大模型OmniTool 则是一个运行 Windows Agent 的工具,提供 Windows 界面给 AI 操作。

简简单单Onlinezuozuo 2728

基于Simulink的发动机管理系统中的自适应增压控制系统设计

通过上述详细设计步骤,我们成功开发了一个基于Simulink的自适应增压控制系统。该系统不仅能够实时监测和预测发动机的运行状态,动态调整增压压力目标值,还具备实时监控、故障诊断和保护功能,确保系统的安全性和可靠性。此外,通过仿真实验和硬件在环测试,我们验证了系统的性能,并对其进行了优化,使其能够更好地满足实际应用需求。这个项目展示了如何利用MATLAB/Simulink的强大功能,结合控制理论和工程实践,开发出高效、可靠的自适应增压控制系统。

MHD0815的博客 219

FreeRTOS源码理解(十)——软件定时器和空闲任务

软件定时器和空闲任务

m0_59201432的博客 536

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

FreeRTOS总结

*函数重入:**函数可以安全的在多个任务调用,或既可以从中断也可以在任务使用,那么函数就是重入,也称作线程安全。中断结束时,会回到打断处继续执行,但如果中断期间,有了更高优先级任务就绪,就应该执行更高优先级任务,而不是返回原点继续执行。如果A,B任务优先级相同,A先获取了互斥量,等到时间片结束切换B,接着回A,释放互斥量,再等到时间片结束,B才能运行。通过任务通知,任务或ISR可以直接向接收任务发送事件。**互斥量:**特殊的二进制信号量,获取后必须归还,归还后别的任务才能获取,否则可能死锁。

qq_39669243的博客 1278

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

个人笔记FreeRTOS

FreeRTOS 一、简介 1、FreeRTOS是ROTS中一个开源的、轻量级的实时操作系统,适用于大多数的嵌入式开发板(如STM32板子等)。 2、操作系统允许多个任务同时运行,这个叫做多任务。实际上,一个处理器核心在某一时刻只能运行一个任务。操作系统中任务调度器的责任就是决定在某一时刻究竟运行哪个任务。 3、FreeRTOS 操作系统则是由用户给每个任务分配一个任务优先级,任务调度器就可以根据此优先级来决定下一刻应该运行哪个任务。 二、裸机系统和多任务系统区别 三、FreeRTOS特点 1、Fr

m0_47224145的博客 5251

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

FreeRTOS队列和补充

队列集是一项 FreeRTOS 功能, 可让 RTOS 任务在同时从多个队列和/或 信号量接收数据时进行阻塞(挂起)。队列和信号量被分成集合,之后任务并非对 单个队列或信号量进行阻塞,而是对集合进行阻塞。**请注意:**虽然有时需要阻塞(挂起)多个队列 (当 FreeRTOS 与遗留代码的第三方集成时),但不受此类限制的设计 通常能以更有效的方式实现相同的功能,方法是使用 替代设计模式(记录在此页面底部)。软件定时器(简称“定时器” )能够让函数在 未来的设定时间执行。

m0_68381723的博客 1278

FreeRTOS学习笔记(四)

本文对FreeRTOS 任务通知,软件定时器,低功耗模式,内存管理等知识点做出介绍,内容参考B站“尚硅谷”的课程和文档。通过本文和前面几篇文章,有关FreeRTOS的基本知识和相关API有了初步认识,下一步将通过项目巩固知识点,掌握FreeRTOS的具体使用。

2401_82807263的博客 602
上一篇: FreeRTOS(消息队列&信号量&队列集&事件标志组)
jzxnb521
博客等级 码龄7年 476粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值