STM32+FreeRTOS基础配置

本篇文章基于STM32标准库、ARM_CM3架构和heap_4.c内存管理(FreeRTOS)编写,适用FreeRTOS入门后参考。

FreeRTOS的 核心文件

为了便于FreeRTOS的移植,可以直接创建一个FreeRTOS的文件夹用于存放需要移植的所有文件,而对于FreeRTOS来说,同样包含后缀为".c"的内核源码文件和".h"的公共头文件以及两个移植层文件。在FreeRTOS文件夹下创建名为src的源码文件夹存放".c"文件、创建名为inc的源码文件夹存放".h"文件和创建名为port的源码文件夹存放移植层文件。

src文件夹

tasks.c:任务管理核心:创建/删除任务、调度器启动、任务切换、延时、优先级管理。

queue.c:队列与信号量底层实现:消息队列、二值信号量、互斥量、计数信号量都基于此文件实现。

list.c:内核链表:FreeRTOS 内部所有任务调度、队列管理都用这个双链表实现。

timers.c:软件定时器:周期或一次性回调函数,依赖queue.c 和tasks.c 。

event_groups.c:事件标志组:等待多个事件同时发生,依赖queue.c。

croutine.c:协程(Co-routine):轻量级任务,适用于资源极小的设备。

inc文件夹

FreeRTOS.h:内核入口:必须最先包含。包含所有内核配置和 API 声明。

task.h:任务相关 API:xTaskCreate、vTaskSuspend、vTaskDelay等。

queue.h:队列与信号量API:xOueueCreate、

xQueueSend、xSemaphoreTake等。

semphr.h:信号量 API:二值信号量、互斥量、计数信号量(基于queue.h 实现)

timers.h:软件定时器 API:xTimerCreate、xTimerStart。

event_groups.h:事件标志组 API:xEventGroupCreate、xEventGroupSetBits。

list.h:内核链表 API:vListInitialise、vListInsert通常不直接调用。

portable.h:移植抽象层:定义栈类型、数据类型等。

projdefs.h:基础类型定义:TickType_t、BaseType_t等。

stack_macros.h/StackMacros.h:栈溢出检测宏configCHECK_FOR_STACK_OVERFLOW依赖。

atomic.h:原子操作:用于 SMP(多核)场景,单核可忽略。

message_buffer.h/stream_buffer.h:消息缓冲区:用于不定长数据流(如串口数据)。

mpu_prototypes.h /mpu_wrappers.h:MPU保护:用于带内存保护单元的芯片(如ARM Cortex-M3/M4带MPU)

deprecated_definitions.h:废弃宏定义:兼容旧版本。

croutine.h:协程 API:与croutine.c配套。

port文件夹

port.c:移植核心:任务切换汇编代码、中断管理、SysTick 配置、栈初始化。

heap_4.c:内存管理:内核动态分配内存(任务栈、队列、信号量等)。FreeRTOS提供5种heap实现,推荐 heap_4.c。

其他文件

文件stm32f10x_it.c

stm32f10x_it.c是STM32 标准库中的中断服务函数(ISR)文件,包含了所有外设中断和系统中断的弱定义空函数。stm32f10x_it.c中的SysTick_Handler、PendSV_Handler、SVC Handler中断在FreeRTOS都有对应的函数接管(xPortSysTickHandler()、vPortPendSVHandler()、vPortSVCHandler()),所以需要注释或删除掉SysTick_Handler、PendSV_Handler、SVC Handler。如果不修改,FreeRTOS 无法获得系统心跳,任务无法切换,系统将卡死在第一个任务中。

FreeRTOSConfig.h文件

FreeRTOSConfig.h头文件是 FreeRTOS 的核心配置文件,不是内核源码的一部分,FreeRTOS 源码本身不包含它。而是专门为项目提供的裁剪和配置头文件,这个配置文件由自己提供。它的作用是告诉 FreeRTOS 内核"项目的的系统有多大、有多少任务、优先级怎么划分、使用哪些功能、如何与硬件交互"。为了方便移植可以放置在inc文件夹里,并在 FreeRTOS.h 中通过 #include "FreeRTOSConfig.h" 引用。

FreeRTOSConfig.h 文件

cpp 复制代码
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H


/* =========================== 基础配置 =========================== */

/* 抢占式调度:1 = 启用(推荐),0 = 协作式 */
#define configUSE_PREEMPTION                    1

/* 空闲任务钩子:1 = 启用,0 = 禁用(不常用) */
#define configUSE_IDLE_HOOK                     0

/* 系统滴答钩子:1 = 启用,0 = 禁用(不常用) */
#define configUSE_TICK_HOOK                     0

/* CPU 主频(单位:Hz),必须与你的系统时钟配置一致 */
#define configCPU_CLOCK_HZ                      ( ( unsigned long ) 72000000 )

/* 系统心跳频率(单位:Hz),通常设为 1000(即 1ms 一个 tick) */
#define configTICK_RATE_HZ                      ( ( TickType_t )1000 )

/* 最大任务优先级数(0 ~ configMAX_PRIORITIES-1),建议 5~10 */
#define configMAX_PRIORITIES                    (5)

/* 最小任务栈大小(单位:字,即 4 字节),用于空闲任务 */
#define configMINIMAL_STACK_SIZE                ( ( unsigned short ) 128 )

/* 总堆大小(单位:字节),用于动态分配内存(任务栈、队列等) */
#define configTOTAL_HEAP_SIZE                   ( ( size_t ) ( 17 * 1024 ) )

/* 任务名最大长度(含 '\0') */
#define configMAX_TASK_NAME_LEN                 (16)


/* =========================== 功能开关 =========================== */

/* 互斥量:1 = 启用(推荐) */
#define configUSE_MUTEXES                       1

/* 递归互斥量:1 = 启用(需要时开启) */
#define configUSE_RECURSIVE_MUTEXES             0

/* 计数信号量:1 = 启用(需要时开启) */
#define configUSE_COUNTING_SEMAPHORES           1

/* 队列集:1 = 启用(需要时开启) */
#define configUSE_QUEUE_SETS                    0

/* 任务通知:1 = 启用(推荐,轻量级通信) */
#define configUSE_TASK_NOTIFICATIONS            1

/* 软件定时器:1 = 启用(需要时开启) */
#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY               (2)             // 定时器任务优先级
#define configTIMER_QUEUE_LENGTH                (10)            // 定时器命令队列长度
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE

/* 事件标志组:1 = 启用(需要时开启) */
#define configUSE_16_BIT_TICKS                  0               // 0=32位tick,1=16位tick


/* =========================== 中断配置 =========================== */
/*
说明:STM32 使用 4 位中断优先级(即 0~15),数值越小优先级越高。
configMAX_SYSCALL_INTERRUPT_PRIORITY 决定了哪些中断可以调用带 FromISR 后缀的 API。
例如,设置为 5,则优先级 0~4 的中断可以调用 API,优先级 5~15 的中断则不能调用 FreeRTOS API。*/

/* 最低中断优先级(STM32 使用 4 位优先级,范围 0~15) */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         15

/* 最高可调用 API 的中断优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5

/* 内核中断优先级(用于 SysTick 和 PendSV) */
#define configKERNEL_INTERRUPT_PRIORITY                 255

/* 可调用 FreeRTOS API 的最高中断优先级 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY            191 /* equivalent to 0xb0, or priority 11. */


/* ==================== 内核中断优先级配置 ==================== */

/* 内核中断优先级(库级):STM32 使用 4 位优先级,范围 0~15 */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15


/* ==================== 中断向量映射(必须配置) ==================== */

/* PendSV 中断:FreeRTOS 的任务切换使用,必须重映射到 PendSV_Handler */
#define xPortPendSVHandler                      PendSV_Handler

/* SVC 中断:FreeRTOS 的系统服务调用,必须重映射到 SVC_Handler */
#define vPortSVCHandler                         SVC_Handler

/* SysTick 中断:FreeRTOS 的系统心跳,必须重映射到 SysTick_Handler */
#define xPortSysTickHandler                     SysTick_Handler


/* =========================== 调试与优化 =========================== */

/* 栈溢出检测:0=禁用,1=启用(简单版),2=启用(增强版)*/
#define configCHECK_FOR_STACK_OVERFLOW          0

/* 内存分配失败钩子:1 = 启用(可选) */
#define configUSE_MALLOC_FAILED_HOOK            0

/* 任务运行时间统计:1 = 启用(需额外配置定时器) */
#define configGENERATE_RUN_TIME_STATS           0

/* 统计格式化函数:1 = 启用(提供 vTaskList() 等) */
#define configUSE_STATS_FORMATTING_FUNCTIONS    0

/* 应用任务标签:1 = 启用(不常用) */
#define configUSE_APPLICATION_TASK_TAG          0

/* 动态内存分配失败钩子(可选) */
#define configUSE_MALLOC_FAILED_HOOK            0


/* =========================== 低功耗 =========================== */

/* Tickless 低功耗模式:1 = 启用 */
#define configUSE_TICKLESS_IDLE                 0

/* 进入低功耗模式的最小空闲时间(单位:tick) */
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP   2


/* ==================== API 裁剪(INCLUDE 系列) ==================== */

/* 任务优先级设置 API:1 = 启用(允许调用 vTaskPrioritySet()) */
#define INCLUDE_vTaskPrioritySet                1

/* 任务优先级获取 API:1 = 启用(允许调用 uxTaskPriorityGet()) */
#define INCLUDE_uxTaskPriorityGet               1

/* 任务删除 API:1 = 启用(允许调用 vTaskDelete()) */
#define INCLUDE_vTaskDelete                     1

/* 清理已删除任务资源 API(已废弃,保持 0) */
#define INCLUDE_vTaskCleanUpResources           0

/* 任务挂起/恢复 API:1 = 启用(允许调用 vTaskSuspend() 和 vTaskResume()) */
#define INCLUDE_vTaskSuspend                    1

/* 绝对延时 API:1 = 启用(允许调用 vTaskDelayUntil(),用于固定周期任务) */
#define INCLUDE_vTaskDelayUntil                 1

/* 相对延时 API:1 = 启用(允许调用 vTaskDelay(),用于普通延时) */
#define INCLUDE_vTaskDelay                      1

/* 获取调度器状态 API:1 = 启用(允许调用 xTaskGetSchedulerState()) */
#define INCLUDE_xTaskGetSchedulerState      1

/* 获取任务栈剩余空间:1 = 启用(允许调用 uxTaskGetStackHighWaterMark()) */
#define INCLUDE_uxTaskGetStackHighWaterMark     1

/* 获取任务优先级 API:1 = 启用(允许调用 uxTaskPriorityGet()) */
#define INCLUDE_uxTaskPriorityGet               1

/* 设置任务优先级 API:1 = 启用(允许调用 vTaskPrioritySet()) */
#define INCLUDE_vTaskPrioritySet                1

/* 获取调度器状态 API:1 = 启用(允许调用 xTaskGetSchedulerState()) */
#define INCLUDE_xTaskGetSchedulerState          1

/* 获取当前任务句柄 API:1 = 启用(允许调用 xTaskGetCurrentTaskHandle()) */
#define INCLUDE_xTaskGetCurrentTaskHandle       1


/* ==================== 追踪与调试配置 ==================== */

/* 追踪功能:1 = 启用(支持可视化追踪调试),0 = 禁用(节省资源) */
#define configUSE_TRACE_FACILITY                0


/* ==================== 空闲任务调度策略 ==================== */

/* 空闲任务让步:1 = 启用(同优先级任务轮流执行),0 = 禁用 */
#define configIDLE_SHOULD_YIELD                 1


#endif /* FREERTOS_CONFIG_H */

FreeRTOS的main.c结构

cpp 复制代码
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "Task.h"
#include "queue.h"
#include "bsp_led.h"   // 包含板级 LED 驱动头文件

//板级硬件初始化函数
void BSP_Init(void)
{
	LED_Init();
}

//定义任务句柄变量
TaskHandle_t myTaskHandler;

//任务栈大小
#define MYTASK_STACK_SIZE 128

//优先级设置,值越大优先级越高
#define MYTASK_PRIORITY	4


/**
 * @brief FreeRTOS 任务函数
 * @param arg 任务参数,此处未使用,传入了 NULL
 * @note 任务函数必须符合以下特征:
 *       1. 返回类型为 void
 *       2. 参数类型为 void*
 *       3. 通常是一个无限循环,不能正常返回(如果返回,必须调用 vTaskDelete 删除自身)
 */
void myTask(void *pvParameters)
{		
	while(1)
		{
			LED1_Toggle();
			vTaskDelay(pdMS_TO_TICKS(500));
		}
}
/**
 * @brief 主函数
 * @note FreeRTOS 程序的标准入口,执行顺序:
 *       1. 硬件初始化
 *       2. 创建任务
 *       3. 启动调度器(永不返回)
 */
int main(void)
 {
	BSP_Init();
	/* 创建任务
     * 参数1:任务函数入口
     * 参数2:任务名称(用于调试)
     * 参数3:任务栈深度,单位是字(word),STM32 中 1 word = 4 字节
     * 参数4:NULL 任务参数,传递给任务函数
     * 参数5:任务优先级,数值越大优先级越高
     * 参数6:任务句柄,用于后续操作任务
	 */
	xTaskCreate(myTask, "myTask", MYTASK_STACK_SIZE, NULL, MYTASK_PRIORITY, &myTaskHandler);
	/* 启动 FreeRTOS 调度器
     * 此函数会:
     *  初始化 SysTick 作为系统心跳
     *  创建空闲任务(和可选的定时器任务)
     *  从就绪列表中选出最高优先级的任务开始执行
     *  此函数永远不会返回!
	 */
	vTaskStartScheduler();
	while (1);
}

任务的创建、删除、挂起和恢复

cpp 复制代码
//头文件引用
#include "task.h"

//FreeRTOSConfig.h 文件配置
/* 任务挂起/恢复 API:1 = 启用(允许调用 vTaskSuspend() 和 vTaskResume()) */
#define INCLUDE_vTaskSuspend                    1
/* 任务删除 API:1 = 启用(允许调用 vTaskDelete()) */
#define INCLUDE_vTaskDelete                     1

内容

  • 创建任务:将项目实现的功能拆成多个独立任务,每个任务有独立的栈和优先级。
  • 删除任务:回收任务占用的内存。
  • 挂起任务:临时暂停任务执行,而不删除任务。
  • 恢复任务:使挂起的任务恢复正常运行。
cpp 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "bsp_led.h"   // 包含板级 LED 驱动头文件

//定义任务句柄变量
TaskHandle_t xTask1Handle;

//任务栈大小
#define TASK1_STACK_SIZE 128
#define TASK2_STACK_SIZE 128

//优先级设置
#define TASK1_PRIORITY	1
#define TASK2_PRIORITY	2

//板级硬件初始化函数
void BSP_Init(void)
{
	LED_Init();
}

//任务一
void vTask1(void *pvParameters)
{
    while(1)
    {
        LED1_Toggle();
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

//任务二
void vTask2(void *pvParameters)
{
    while(1)
    {
        // 挂起任务一2秒后恢复
        vTaskSuspend(xTask1Handle);
        vTaskDelay(pdMS_TO_TICKS(2000));
        vTaskResume(xTask1Handle);
		//删除任务本身
        vTaskDelete(NULL);
    }
}

int main(void)
{
	BSP_Init();
	
    xTaskCreate(vTask1, "Task1", TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, &xTask1Handle);
    xTaskCreate(vTask2, "Task2", TASK2_STACK_SIZE, NULL, TASK2_PRIORITY, NULL);
    vTaskStartScheduler();
    while(1);
}

调度器的挂起和恢复

cpp 复制代码
//头文件引用
#include "task.h"
cpp 复制代码
void vTask(void *pvParameters)
{
    while(1)
    {
        vTaskSuspendAll();  // 禁止调度
        // 批量修改共享数据
        for(int i = 0; i < 100; i++) {
            shared_array[i] = i;
        }
        xTaskResumeAll();   // 恢复调度
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

相/绝对延时

cpp 复制代码
//头文件引用
#include "task.h"
cpp 复制代码
//FreeRTOSConfig.h 文件配置
/* 绝对延时 API:1 = 启用(允许调用 vTaskDelayUntil(),用于固定周期任务) */
#define INCLUDE_vTaskDelayUntil                 1

/* 相对延时 API:1 = 启用(允许调用 vTaskDelay(),用于普通延时) */
#define INCLUDE_vTaskDelay                      1

示例:

cpp 复制代码
// 相对延时(软延时)
vTaskDelay(pdMS_TO_TICKS(500));

// 绝对延时(固定周期)
TickType_t xLastWakeTime = xTaskGetTickCount();
while(1) {
// 周期任务代码
// ......
    vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));
    // 任务代码(周期固定为 1000ms)
}

临界区

保护共享资源(如全局变量)不被多任务或中断同时访问。

任务级临界区关中断;中断级临界区只关调度器。

  • 任务中使用 taskENTER_CRITICAL() / taskEXIT_CRITICAL() 包裹共享数据操作。
  • 中断中使用 taskENTER_CRITICAL_FROM_ISR() / taskEXIT_CRITICAL_FROM_ISR()。
  • 中断级临界区是指在中断服务函数(ISR)中,为了保护被中断和任务共享的全局变量或资源而设置的临界区。

在中断处理中,禁止更高优先级的中断嵌套,防止当前正在操作的数据被另一个中断破坏。

cpp 复制代码
//头文件引用
#include "task.h"
cpp 复制代码
// 任务级临界区
void vTask(void *pvParameters)
{
    while(1)
    {
        taskENTER_CRITICAL();
        // 操作共享全局变量
        shared_counter++;
        taskEXIT_CRITICAL();
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

// 中断级临界区(在中断服务函数中)
#define RX_BUF_SIZE   128
volatile uint8_t rx_buffer[RX_BUF_SIZE];
volatile uint16_t rx_head = 0;
volatile uint16_t rx_tail = 0;

void USART1_IRQHandler(void)
{
    UBaseType_t uxSavedStatus;
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        uint8_t data = USART_ReceiveData(USART1);

        // 进入临界区,保护缓冲区操作
        uxSavedStatus = taskENTER_CRITICAL_FROM_ISR();
        uint16_t next_head = (rx_head + 1) % RX_BUF_SIZE;
        if (next_head != rx_tail)   // 缓冲区未满
        {
            rx_buffer[rx_head] = data;
            rx_head = next_head;
        }
        taskEXIT_CRITICAL_FROM_ISR(uxSavedStatus);
    }
}

信号量

  • 二值信号量:任务同步(如中断通知任务)。
  • 互斥信号量:保护共享资源(支持优先级继承,防止优先级反转)。
  • 计数信号量:管理多个资源。
cpp 复制代码
//头文件应引用
#include "semphr.h"

//FreeRTOSConfig.h 文件配置
/* 互斥量:1 = 启用(推荐) */
#define configUSE_MUTEXES                       1

/* 递归互斥量:1 = 启用(需要时开启) */
#define configUSE_RECURSIVE_MUTEXES             0

/* 计数信号量:1 = 启用(需要时开启) */
#define configUSE_COUNTING_SEMAPHORES           1

二值信号量只有 0 和 1 两个值,初始值通常为 0支持任务与中断之间的同步。不是用来保护资源的,不具备优先级继承特性。
*

cpp 复制代码
// 创建(main中)
SemaphoreHandle_t xBinarySem = xSemaphoreCreateBinary();

// 中断中释放
void USART1_IRQHandler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(xBinarySem, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

// 任务中等待
void vTask(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(xBinarySem, portMAX_DELAY);
        // 收到信号后处理
    }
}

互斥信号量只有 0 和 1,支持优先级继承(防止优先级反转)必须由同一个任务执行Take和Give。用于保护共享资源,比如串口、I2C、共享内存等。不能在中断中使用,同一个任务必须负责释放。
*

cpp 复制代码
// 创建(main中)
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();

void vTask1(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(xMutex, portMAX_DELAY);
        USART_SendString("Task1 using USART\r\n");
        xSemaphoreGive(xMutex);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void vTask2(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(xMutex, portMAX_DELAY);
        USART_SendString("Task2 using USART\r\n");
        xSemaphoreGive(xMutex);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

计数信号量值可以大于 1,最大值在创建时设定,初始值可自定义。

cpp 复制代码
// 定义最大可存储事件数
#define MAX_EVENT_COUNT     10

SemaphoreHandle_t xEventCounter;
TaskHandle_t xEventProcessTaskHandle;

// 初始化
void EventCounter_Init(void)
{
    // 创建计数信号量:最大 10 个事件,初始 0 个(还没有事件)
    xEventCounter = xSemaphoreCreateCounting(MAX_EVENT_COUNT, 0);
}

// 中断:按键按下
void EXTI0_IRQHandler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    if (EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // 释放信号量,记录一次按键事件
        xSemaphoreGiveFromISR(xEventCounter, &xHigherPriorityTaskWoken);
        
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
    
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

// 任务:处理按键事件
void vTask_ProcessEvents(void *pvParameters)
{
    uint32_t total_events = 0;
    
    while(1)
    {
        // 等待按键事件(阻塞,直到有按键按下)
        if (xSemaphoreTake(xEventCounter, portMAX_DELAY) == pdTRUE)
        {
            total_events++;
            
            USARTPrintf(USART1, "Key pressed! Total: %d\r\n", total_events);
            
            // 如果达到 10 次,执行特殊操作
            if (total_events >= 10)
            {
                USARTPrintf(USART1, "10 times! Action triggered!\r\n");
                total_events = 0;  // 重置计数
            }
        }
    }
}

// 启动
int main(void)
{
    EventCounter_Init();
    
    xTaskCreate(vTask_ProcessEvents, "EventProcess", 128, NULL, 2, &xEventProcessTaskHandle);
    
    vTaskStartScheduler();
    while(1);

队列/队列集

cpp 复制代码
//头文件引用
#include "queue.h"

//FreeRTOSConfig.h 文件配置
/* 队列集:1 = 启用(需要时开启) */
#define configUSE_QUEUE_SETS                    0

队列

  • 示例:生产者发送一递增数据,消费者接收并打印这个数据

    cpp 复制代码
    //生产者发送一递增数据,消费者接收并打印这个数据
    #include "FreeRTOS.h"
    #include "task.h"
    #include "queue.h"
    #include "bsp_usart.h"
    
    // 定义队列句柄
    QueueHandle_t xDataQueue;
    
    // 生产者任务:发送数据
    void vTask_Sender(void *pvParameters)
    {
        int32_t data = 0;
        
        while(1)
        {
            data++;
            
            // 将数据发送到队列(不等待,如果队列满则丢弃)
            if (xQueueSend(xDataQueue, &data, 0) == pdPASS)
            {
                USARTPrintf(USART1, "Sent: %d\r\n", data);
            }
            else
            {
                USARTPrintf(USART1, "Queue full, data lost\r\n");
            }
            
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
    
    // 消费者任务:接收数据
    void vTask_Receiver(void *pvParameters)
    {
        int32_t received_data;
        
        while(1)
        {
            // 阻塞等待队列中的数据(永久等待)
            if (xQueueReceive(xDataQueue, &received_data, portMAX_DELAY) == pdPASS)
            {
                USARTPrintf(USART1, "Received: %d\r\n", received_data);
            }
        }
    }
    
    int main(void)
    {
        USART1_Init();
        
        // 创建队列:长度为 5,每个元素占 4 字节(int32_t)
        // 参数1:队列长度(最多能存放 5 个元素)
        // 参数2:每个元素的大小(字节)
        xDataQueue = xQueueCreate(5, sizeof(int32_t));
        
        // 创建任务
        xTaskCreate(vTask_Sender, "Sender", 128, NULL, 1, NULL);
        xTaskCreate(vTask_Receiver, "Receiver", 128, NULL, 2, NULL);
        
        vTaskStartScheduler();
        while(1);
    }

    示例:创建两个队列,生产者同时发送到两个队列用于控制和显示。

    cpp 复制代码
    /* 传感器数据结构体 */
    typedef struct
    {
        uint8_t temperature;
        uint8_t humidity;
        float light;
    	uint16_t mq2_value;
        uint32_t timestamp;    // 可选:时间戳
    } SensorData_t;
    
    
    /* 定义创建两个队列 */
    QueueHandle_t xControlQueue = xQueueCreate(5, sizeof(SensorData_t));  // 给控制任务
    QueueHandle_t xDisplayQueue = xQueueCreate(5, sizeof(SensorData_t));;   // 给显示任务
    
    
    /* 生产者:采集任务 */
    void vSensor_Task(void *pvParam)
    {
        SensorData_t data;
        const TickType_t xFrequency = pdMS_TO_TICKS(1000);
        TickType_t xLastWakeTime = xTaskGetTickCount();
    
        while(1)
        {
            vTaskDelayUntil(&xLastWakeTime, xFrequency);
    
            data.temperature = g_temperature;
            data.humidity    = g_humidity;
            data.light       = g_light;
    		data.mq2_value	 = g_mq2_value;
            data.timestamp   = xTaskGetTickCount();
    
            /* 关键:将同一份数据发送到两个队列 */
            xQueueOverwrite(xControlQueue, &data);
            xQueueOverwrite(xDisplayQueue, &data);
        }
    }
    
    /* 消费者1:控制任务 */
    void vControl_Task(void *pvParam)
    {
        SensorData_t data;
        
        while(1)
        {
            if(xQueueReceive(xControlQueue, &data, portMAX_DELAY) == pdTRUE)
            {
                /* 数据到达,执行控制逻辑 */
                if(data.temperature > 34)
                {
                    LED_Control(LED1_GPIO_PORT, LED1_GPIO_PIN1, 1);
                }else{
    				LED_Control(LED1_GPIO_PORT, LED1_GPIO_PIN1, 0);
    			}
    
    			if(data.humidity < 50)
    			{
    				LED_Control(LED1_GPIO_PORT, LED1_GPIO_PIN2, 1);
    			}else{
    				LED_Control(LED1_GPIO_PORT, LED1_GPIO_PIN2, 0);
    			}
    
                if(data.light < 100)
                {
                    // 补光 / 开灯
    				LED_Control(LED1_GPIO_PORT, LED1_GPIO_PIN3, 1);
                }else{
    				LED_Control(LED1_GPIO_PORT, LED1_GPIO_PIN3, 0);
    			}
            }
        }
    }
    
    /* 消费者2:显示任务 */
    void vDisplay_Task(void *pvParam)
    {
    	SensorData_t data;
    	while (1)
    	{
    		if(xQueueReceive(xDisplayQueue, &data, portMAX_DELAY) == pdTRUE)
    		{
    			/* 更新 OLED 显示 */
    			taskENTER_CRITICAL();
    			OLED_ShowFormat(1,1,"WD:%-6d",data.temperature);
    			OLED_ShowFormat(2,1,"SD:%-6d",data.humidity);
    			OLED_ShowFormat(3,1,"GZ:%-8.2f",data.light);
    			OLED_ShowFormat(4,1,"MQ:%-6d",data.mq2_value);
    			taskEXIT_CRITICAL();
    		}
    	}
    }

    队列集

    同时等待多个队列或信号量,有事件触发时按优先级处理。

cpp 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "bsp_usart.h"

// 定义队列句柄
QueueHandle_t xUART_Queue;
QueueHandle_t xKey_Queue;
SemaphoreHandle_t xTimer_Semaphore;
QueueSetHandle_t xQueueSet;

// 生产者任务:模拟串口数据
void vTask_UART_Sender(void *pvParameters)
{
    uint8_t data = 'A';
    
    while(1)
    {
		// 将数据发送到 UART 队列(不等待,队列满则丢弃)
        // 参数1:目标队列句柄
        // 参数2:要发送的数据的地址(队列会拷贝数据)
        // 参数3:超时时间 0(不等待,立即返回)
        xQueueSend(xUART_Queue, &data, 0);
        data++;
        if (data > 'Z') data = 'A';
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

// 生产者任务:模拟按键事件
void vTask_Key_Sender(void *pvParameters)
{
    uint8_t key = 0x01;
    
    while(1)
    {
        key = (key == 0x01) ? 0x02 : 0x01;
        xQueueSend(xKey_Queue, &key, 0);
        vTaskDelay(pdMS_TO_TICKS(3000));
    }
}

// 生产者任务:模拟定时器信号量
void vTask_Timer_Sender(void *pvParameters)
{
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(5000));
		// 释放二值信号量(通知监控任务)
        // 如果有任务在等待这个信号量,它会被唤醒
        xSemaphoreGive(xTimer_Semaphore);
    }
}

// 主监控任务:使用队列集同时等待多个源
void vTask_Monitor(void *pvParameters)
{
	// 用于存储从队列集中返回的成员句柄(是哪个队列/信号量触发了)
    QueueSetMemberHandle_t xMember;
    uint8_t data;
    
    while(1)
    {
        // 阻塞等待队列集中的任意一个成员有数据/事件
        // 返回值:哪个队列/信号量触发了
        xMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);
        
        if (xMember == xUART_Queue)
        {
            // 从 UART 队列取数据
            if (xQueueReceive(xUART_Queue, &data, 0) == pdPASS)
            {
                USARTPrintf(USART1, "UART: %c\r\n", data);
            }
        }
        else if (xMember == xKey_Queue)
        {
            // 从按键队列取数据
            if (xQueueReceive(xKey_Queue, &data, 0) == pdPASS)
            {
                USARTPrintf(USART1, "Key: 0x%02X\r\n", data);
            }
        }
        else if (xMember == xTimer_Semaphore)
        {
            // 从信号量取数据(信号量本质也是队列)
            if (xSemaphoreTake(xTimer_Semaphore, 0) == pdTRUE)
            {
                USARTPrintf(USART1, "Timer event!\r\n");
            }
        }
    }
}

int main(void)
{
    USART1_Init();
    
    // 创建队列 长度为 5,每个元素 1 字节
    xUART_Queue = xQueueCreate(5, sizeof(uint8_t));
    xKey_Queue = xQueueCreate(5, sizeof(uint8_t));
	// 创建二值信号量:初始状态为不可用(0)
    xTimer_Semaphore = xSemaphoreCreateBinary();
    
    //创建队列集:最大可容纳 3 个成员
    // 参数:队列集可以容纳的最大成员数(队列+信号量的总数)
    xQueueSet = xQueueCreateSet(3);
    
    //将队列添加到队列集
    xQueueAddToSet(xUART_Queue, xQueueSet);
    xQueueAddToSet(xKey_Queue, xQueueSet);
    xQueueAddToSet(xTimer_Semaphore, xQueueSet);
    
    // 创建任务
    xTaskCreate(vTask_UART_Sender, "UART_Send", 128, NULL, 1, NULL);
    xTaskCreate(vTask_Key_Sender, "Key_Send", 128, NULL, 1, NULL);
    xTaskCreate(vTask_Timer_Sender, "Timer_Send", 128, NULL, 1, NULL);
    xTaskCreate(vTask_Monitor, "Monitor", 256, NULL, 2, NULL);
    
    vTaskStartScheduler();
    while(1);
}

软件定时器

cpp 复制代码
//头文件引用
#include "timer.h"

//FreeRTOSConfig.h 文件配置
/* 软件定时器:1 = 启用(需要时开启) */
#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY               (2)             // 定时器任务优先级
#define configTIMER_QUEUE_LENGTH                (10)            // 定时器命令队列长度
#define configTIMER_TASK_STACK_DEPTH 			configMINIMAL_STACK_SIZE

示例
*

cpp 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"          // 软件定时器 API 在此头文件中
#include "bsp_usart.h"       // 串口调试输出
#include "bsp_led.h"         // LED 控制

// 定义软件定时器句柄
TimerHandle_t xTimer = NULL;

//软件定时器回调函数
void vTimerCallback(TimerHandle_t xTimer)
{
    LED_Toggle();
}

int main(void)
{
    // 硬件初始化(在创建定时器之前)
    USART1_Init();
    LED_Init(); 
    
    // 创建软件定时器
    xTimer = xTimerCreate(
        "Timer",                     // 定时器名称
        pdMS_TO_TICKS(1000),         // 周期:1000ms
        pdTRUE,                      // 自动重载
        0,                           // 定时器 ID
        vTimerCallback               // 回调函数
    );

    // 检查定时器是否创建成功
    if (xTimer == NULL)
    {
        // 创建失败处理
        USARTPrintf(USART1, "ERROR: Timer creation failed!\r\n");
        
        
        // 停止执行
        while(1);
    }
    else
    {
        USARTPrintf(USART1, "Timer created successfully.\r\n");
    }

    // 启动定时器
    if (xTimerStart(xTimer, 0) == pdPASS)
    {
        USARTPrintf(USART1, "Timer started successfully.\r\n");
    }
    else
    {
        USARTPrintf(USART1, "ERROR: Timer start failed!\r\n");
        while(1);
    }
	
    // 启动调度器
    vTaskStartScheduler();

    while(1);
}

事件标志组

cpp 复制代码
//头文件引用
#include "event_groups.h"

//FreeRTOSConfig.h 文件配置
/* 事件标志组:1 = 启用(需要时开启) */
#define configUSE_16_BIT_TICKS                  0               // 0=32位tick,1=16位tick

示例:满足四个按键按下后led闪烁一次

cpp 复制代码
#define EVENT_BIT_KEY1    (1 << 0)   /* 按键1事件位:0x01 */
#define EVENT_BIT_KEY2    (1 << 1)   /* 按键2事件位:0x02 */
#define EVENT_BIT_KEY3    (1 << 2)   /* 按键3事件位:0x04 */
#define EVENT_BIT_KEY4    (1 << 3)   /* 按键4事件位:0x08 */
#define EVENT_ALL_KEYS    (EVENT_BIT_KEY1 | EVENT_BIT_KEY2 | EVENT_BIT_KEY3 | EVENT_BIT_KEY4)  /* 0x0F */

// 创建事件标志组
// EventGroupHandle_t event_group = xEventGroupCreate();

//等待4个条件全部满足  => LED1闪烁一下
void myTask1(void *pvParameters){
	while(1)
	{
		/* ============================================================         
		* 等待4个按键事件全部发生                 
		* 参数1: event_group   	- 事件标志组句柄         
		* 参数2: uxBitsToWaitFor 	- 等待条件拼接成的int值(二进制 1111 ==> 十六进制 0x0f)        
		* 参数3: xClearOnExit   	- 退出时是否清除 (pdFALSE = 不清除(1 -> 0))         
		* 参数4: xWaitForAllBits  	- 是否等待全部位 (pdTRUE = 全部(1111))         
		* 参数5: xTicksToWait  	- 永久等待
		============================================================ */
        xEventGroupWaitBits(event_group,EVENT_ALL_KEYS, pdFALSE, pdTRUE, portMAX_DELAY);
        LED_Toggle(GPIOA, GPIO_Pin_0);
        vTaskDelay(pdMS_TO_TICKS(500));
        LED_Toggle(GPIOA, GPIO_Pin_0);
        vTaskDelay(pdMS_TO_TICKS(500));

        // 可以手动清除标志位
        xEventGroupClearBits(event_group, EVENT_ALL_KEYS);
    }
}

void myTask2(void *pvParameters){
		uint8_t key1_last = 0, key1_current = 0;
		uint8_t key2_last = 0, key2_current = 0;
		uint8_t key3_last = 0, key3_current = 0;
		uint8_t key4_last = 0, key4_current = 0;
    while(1)
    {
        // 读取当前状态
        key1_current = (KEY_ReadState(KEY1) == RESET) ? 1 : 0;
        key2_current = (KEY_ReadState(KEY2) == RESET) ? 1 : 0;
        key3_current = (KEY_ReadState(KEY3) == RESET) ? 1 : 0;
        key4_current = (KEY_ReadState(KEY4) == RESET) ? 1 : 0;
        
        // 扫描按键  => 按下不同的按钮 将事件标志组中的对应位设置为1
        // 处理按键1
        if(key1_current == 1 && key1_last == 0)  // 下降沿
        {
           // 在事件标志组的第0位设置为1
            xEventGroupSetBits(event_group, EVENT_BIT_KEY1);
        }
        vTaskDelay(pdMS_TO_TICKS(10));
        // 处理按键2
        if(key2_current == 1 && key2_last == 0)  // 下降沿
        {
            // 在事件标志组的第1位设置为1
            xEventGroupSetBits(event_group, EVENT_BIT_KEY2);
        }
        vTaskDelay(pdMS_TO_TICKS(10));
        // 处理按键3
        if(key3_current == 1 && key3_last == 0)  // 下降沿
        {
            // 在事件标志组的第2位设置为1
            xEventGroupSetBits(event_group, EVENT_BIT_KEY3);
        }
        // 处理按键4
        if(key4_current == 1 && key4_last == 0)  // 下降沿
        {
			// 在事件标志组的第3位设置为1
            xEventGroupSetBits(event_group, EVENT_BIT_KEY4);
        }
        // 更新上一次状态
        key1_last = key1_current;
        key2_last = key2_current;
        key3_last = key3_current;
        key4_last = key4_current;
        
        // 周期性扫描,间隔 20ms(消抖 + 扫描周期)
        vTaskDelay(pdMS_TO_TICKS(20));
		}
}

直接任务通知

cpp 复制代码
//头文件引用
#include "task.h"

//FreeRTOSConfig.h 文件配置
/* 任务通知:1 = 启用(推荐,轻量级通信) */
#define configUSE_TASK_NOTIFICATIONS            1

示例:直接任务通知代替二值信号量

cpp 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "bsp_usart.h"
#include "bsp_led.h"

// 定义任务句柄(接收通知的任务)
TaskHandle_t xTaskWithBinaryHandle = NULL;


void EXTI0_IRQHandler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    if (EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // 向任务发送通知(中断专用版本)
        xTaskNotifyFromISR(xTaskWithBinaryHandle,     // 目标任务句柄
                           1,                         // 通知值(固定为 1)
                           eSetValueWithOverwrite,    // 覆盖旧值 → 二值行为
                           &xHigherPriorityTaskWoken);
        
        // 清除中断标志
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
    
    // 如果有更高优先级任务被唤醒,请求切换
	// pdFALSE 不切换,继续执行被中断的任务  pdTRUE立即切换,唤醒的任务优先执行
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

//  接收任务:等待通知(二值信号量模式)
void vTask_Binary_Notification(void *pvParameters)
{
    uint32_t ulNotifiedValue;
    
    while(1)
    {
        // ========== 代替 xSemaphoreTake() ==========
        // 等待通知(永久等待)
        // 参数1:pdTRUE 表示清零通知值  pdFALSE表示不清零通知值
        // 参数2:永久等待
        // 返回值:收到的通知次数(这里固定为 1)
        ulNotifiedValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        
        if (ulNotifiedValue > 0)
        {
            // 事件发生,执行一次动作
            LED_Toggle();
            USARTPrintf(USART1, "Binary Event Received!\r\n");
        }
    }
}

int main(void)
{
    USART1_Init();
    LED_Init();
    
	   // 配置外部中断(此处省略了GPIO配置)
	
    // 创建接收任务(先创建获取句柄)
    xTaskCreate(vTask_Binary_Notification, "BinaryTask", 128, NULL, 2, &xTaskWithBinaryHandle);
    
	vTaskStartScheduler();
    while(1);
}

示例:直接任务通知代替计数型信号量

cpp 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "bsp_usart.h"
#include "bsp_led.h"

// 定义任务句柄(接收通知的任务)
TaskHandle_t xTaskWithCountingHandle = NULL;

/**
 * @brief EXTI0 中断服务函数(按键中断)
 * @note  每次按键按下,通知值 +1(计数型信号量行为)
 */
void EXTI0_IRQHandler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    if (EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // ========== 计数型信号量行为 ==========
        // 使用 xTaskNotifyGiveFromISR 让通知值 +1
        // 相当于计数信号量的 Give 操作
        // 每次按键按下,通知值累加:0→1→2→3→...
        xTaskNotifyGiveFromISR(xTaskWithCountingHandle, &xHigherPriorityTaskWoken);
        
        // 清除中断标志
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
    
    // 如果有更高优先级任务被唤醒,请求切换
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

/**
 * @brief 接收任务:等待通知(计数信号量模式)
 * @note  每收到3次通知,翻转一次LED
 */
void vTask_Counting_Notification(void *pvParameters)
{
    uint32_t ulNotifiedValue;
    uint32_t total_count = 0;  // 累计按键次数
    
    while(1)
    {
        // ========== 代替 xSemaphoreTake() ==========
        // 等待通知(永久等待)
        // 参数1:pdTRUE 表示清零通知值
        // 参数2:永久等待
        // 返回值:本次被唤醒时,通知值的大小(累积的通知次数)
        ulNotifiedValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        
        // ulNotifiedValue 表示本次积压了多少次按键
        // 例如:如果连续按了3次才被调度,则 ulNotifiedValue = 3
        USARTPrintf(USART1, "Received %d notifications\r\n", ulNotifiedValue);
        
        // 累加总按键次数
        total_count += ulNotifiedValue;
        
        // 如果累计按键次数 >= 3,翻转LED并重置计数
        if (total_count >= 3)
        {
            LED_Toggle();
            USARTPrintf(USART1, "LED Toggled! (Total: %d)\r\n", total_count);
            total_count = 0;  // 重置计数
        }
    }
}

int main(void)
{
    USART1_Init();
    LED_Init();
    
    // 配置外部中断(此处省略了GPIO配置)
    
    // 创建接收任务(先创建获取句柄)
    xTaskCreate(vTask_Counting_Notification, "CountingTask", 128, NULL, 2, &xTaskWithCountingHandle);
    
    vTaskStartScheduler();
    while(1);
}

示例:直接任务通知代替事件标志组

cpp 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include "key.h"

// ============================================================
// 1. 定义事件位(4个按键对应4个位)
// ============================================================
#define EVENT_BIT_KEY1    (1 << 0)   // 0x01
#define EVENT_BIT_KEY2    (1 << 1)   // 0x02
#define EVENT_BIT_KEY3    (1 << 2)   // 0x04
#define EVENT_BIT_KEY4    (1 << 3)   // 0x08
#define ALL_EVENTS        (EVENT_BIT_KEY1 | EVENT_BIT_KEY2 | EVENT_BIT_KEY3 | EVENT_BIT_KEY4)  // 0x0F

// ============================================================
// 2. 定义任务句柄(接收通知的任务)
// ============================================================
TaskHandle_t xTaskWithNotifyHandle = NULL;

// ============================================================
// 3. LED闪烁任务(等待4个按键全部按下)
// ============================================================
void vTask_WaitForAllKeys(void *pvParameters)
{
    uint32_t ulNotifiedValue;
    
    while(1)
    {
        // ============================================================
        // 代替:xEventGroupWaitBits(event_group, 0x0f, pdFALSE, pdTRUE, portMAX_DELAY)
        // ============================================================
        // 参数1:ulBitsToClearOnEntry  - 进入等待前清零的位(0 表示不清零)
        // 参数2:ulBitsToClearOnExit   - 退出等待后清零的位(0xFFFFFFFF 表示全部清零)
        // 参数3:pulNotificationValue  - 存储当前通知值
        // 参数4:xTicksToWait          - 等待时间(永久等待)
        //
        // 返回值:pdTRUE 表示收到通知,pdFALSE 表示超时
        if (xTaskNotifyWait(0,                    // 进入时不清零
                            0xFFFFFFFF,           // 退出时全部清零(相当于清除事件标志)
                            &ulNotifiedValue,     // 获取当前通知值
                            portMAX_DELAY) == pdTRUE)
        {
            // 检查是否所有4个按键都按下了
            if ((ulNotifiedValue & ALL_EVENTS) == ALL_EVENTS)
            {
                // LED闪烁一次(亮500ms,灭500ms)
                LED_Toggle(GPIOA, GPIO_Pin_0);
                vTaskDelay(pdMS_TO_TICKS(500));
                LED_Toggle(GPIOA, GPIO_Pin_0);
                vTaskDelay(pdMS_TO_TICKS(500));
                
                USARTPrintf(USART1, "All 4 keys pressed! LED blinked!\r\n");
            }
            else
            {
                // 只按下了部分按键,打印当前状态
                USARTPrintf(USART1, "Current bits: 0x%02X (need 0x0F)\r\n", ulNotifiedValue);
            }
        }
    }
}

// ============================================================
// 4. 按键扫描任务(代替 xEventGroupSetBits)
// ============================================================
void vTask_KeyScan(void *pvParameters)
{
    uint8_t key1_last = 0, key1_current = 0;
    uint8_t key2_last = 0, key2_current = 0;
    uint8_t key3_last = 0, key3_current = 0;
    uint8_t key4_last = 0, key4_current = 0;
    
    while(1)
    {
        // 读取当前状态
        key1_current = (KEY_ReadState(KEY1) == RESET) ? 1 : 0;
        key2_current = (KEY_ReadState(KEY2) == RESET) ? 1 : 0;
        key3_current = (KEY_ReadState(KEY3) == RESET) ? 1 : 0;
        key4_current = (KEY_ReadState(KEY4) == RESET) ? 1 : 0;
        
        // ============================================================
        // 代替:xEventGroupSetBits(event_group, 1 << n)
        // 使用 xTaskNotify 的 eSetBits 模式,按位或设置通知值
        // ============================================================
        
        // 处理按键1(下降沿)
        if(key1_current == 1 && key1_last == 0)
        {
            // 向通知值按位或 EVENT_BIT_KEY1
            xTaskNotify(xTaskWithNotifyHandle, EVENT_BIT_KEY1, eSetBits);
            USARTPrintf(USART1, "Key1 pressed -> BIT0 set\r\n");
        }
        
        // 处理按键2(下降沿)
        if(key2_current == 1 && key2_last == 0)
        {
            xTaskNotify(xTaskWithNotifyHandle, EVENT_BIT_KEY2, eSetBits);
            USARTPrintf(USART1, "Key2 pressed -> BIT1 set\r\n");
        }
        
        // 处理按键3(下降沿)
        if(key3_current == 1 && key3_last == 0)
        {
            xTaskNotify(xTaskWithNotifyHandle, EVENT_BIT_KEY3, eSetBits);
            USARTPrintf(USART1, "Key3 pressed -> BIT2 set\r\n");
        }
        
        // 处理按键4(下降沿)
        if(key4_current == 1 && key4_last == 0)
        {
            xTaskNotify(xTaskWithNotifyHandle, EVENT_BIT_KEY4, eSetBits);
            USARTPrintf(USART1, "Key4 pressed -> BIT3 set\r\n");
        }
        
        // 更新上一次状态
        key1_last = key1_current;
        key2_last = key2_current;
        key3_last = key3_current;
        key4_last = key4_current;
        
        // 周期性扫描,间隔 20ms(消抖 + 扫描周期)
        vTaskDelay(pdMS_TO_TICKS(20));
    }
}

// ============================================================
// 5. 主函数
// ============================================================
int main(void)
{
    USART1_Init();
    LED_Init();
    KEY_Init();
    
    // 创建任务(先创建等待任务,获取句柄)
    xTaskCreate(vTask_WaitForAllKeys, "WaitKeys", 128, NULL, 2, &xTaskWithNotifyHandle);
    
    // 创建按键扫描任务(优先级稍低)
    xTaskCreate(vTask_KeyScan, "KeyScan", 256, NULL, 1, NULL);
    
    vTaskStartScheduler();
    while(1);
}

低功耗模式 FreeRTOSConfig.h 配置

cpp 复制代码
// FreeRTOSConfig.h 配置

#define configUSE_TICKLESS_IDLE                  1

#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP   2

获取任务数

cpp 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "bsp_usart.h"

void vTask_GetTaskCount(void *pvParameters)
{
    UBaseType_t task_count;
    
    while(1)
    {
        // 获取当前系统中的任务总数(包括空闲任务和定时器任务)
        task_count = uxTaskGetNumberOfTasks();
        
        USARTPrintf(USART1, "Current Task Count: %d\r\n", task_count);
        
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

获取任务剩余空间

cpp 复制代码
// FreeRTOSConfig.h 配置
/* 获取任务栈剩余空间:1 = 启用(允许调用 uxTaskGetStackHighWaterMark()) */
#define INCLUDE_uxTaskGetStackHighWaterMark     1
cpp 复制代码
#include "FreeRTOS.h"
#include "task.h"
#include "bsp_usart.h"

TaskHandle_t xMyTaskHandle = NULL;

void vMyTask(void *pvParameters)
{
    while(1)
    {
        // ... 任务代码 ...
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void vTask_GetStackInfo(void *pvParameters)
{
    UBaseType_t stack_remaining;
    
    while(1)
    {
        // 获取指定任务的栈剩余空间(历史最小值)
        stack_remaining = uxTaskGetStackHighWaterMark(xMyTaskHandle);
        
        USARTPrintf(USART1, "Task Stack Remaining: %d words (%d bytes)\r\n", 
                    stack_remaining, stack_remaining * 4);
        
        // 获取当前任务自身的栈剩余空间
        stack_remaining = uxTaskGetStackHighWaterMark(NULL);
        USARTPrintf(USART1, "Current Task Stack Remaining: %d words\r\n", stack_remaining);
        
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

int main(void)
{
    USART1_Init();
    
    // 创建任务(保存句柄)
    xTaskCreate(vMyTask, "MyTask", 256, NULL, 1, &xMyTaskHandle);
    xTaskCreate(vTask_GetStackInfo, "StackInfo", 128, NULL, 2, NULL);
    
    vTaskStartScheduler();
    while(1);
}

注:以上内容仅个人理解,不具备唯一性和绝对正确性,仅供参考。

相关推荐
恒锐丰-罗生2 小时前
率能半导体 SS6840 单通道 H 桥电机驱动芯片深度解析
驱动开发·嵌入式硬件·硬件工程
智购科技自动贩卖机3 小时前
自动售货机硬件主控方案选型实战:单片机、树莓派、ESP32怎么选?
人工智能·单片机·嵌入式硬件·物联网·网络协议·yolo·架构
qq_401700413 小时前
RTOS的几种常见调度策略
freertos
圆奋奋4 小时前
FreeRTOS学习(十)- 最核心的内容:优先级抢占式调度 + 上下文切换
学习·freertos
WWJA王文举5 小时前
# STM32使用DMA后数据异常?可能不是DMA配置问题:从缓存、变量类型、时序到外设状态全面排查
stm32·嵌入式硬件·缓存
电化学仪器白超5 小时前
MV-CS200-10UC相机参数配置
python·单片机·嵌入式硬件·数码相机·自动化·ltspice
我和头发拼了5 小时前
STM32--9USART收发HEX数据包
stm32·单片机·嵌入式硬件
wuyk5555 小时前
7.AVL 树:第一个自平衡二叉搜索树
开发语言·stm32·单片机·算法
国科安芯5 小时前
星载网络化控制的总线脊梁:四路CANFD在分布式载荷管理中的架构优势
分布式·单片机·嵌入式硬件·架构·系统架构·canfd·低轨卫星星座