
Stm32 Freertos Developer
- 434 installs
- 13 repo stars
- Updated January 14, 2026
- 2939387245/agent-skill_stm32-freertos
stm32-freertos-developer is a Claude Code skill that bootstraps STM32 firmware with FreeRTOS tasks, queues, semaphores, and HAL peripheral drivers using proven dynamic and static creation patterns for embedded developers
About
stm32-freertos-developer is an agent skill for STM32 plus FreeRTOS embedded development on ARM Cortex-M F0/F1/F3/F4/F7/H7/G0/L0/L4/L5 series with FreeRTOS v10+ and CMSIS-RTOS v2 API support. The skill selectively loads 10 reference, example, and pattern documents—EXAMPLES/BASIC.md for xTaskCreate patterns, PATTERNS/INTERRUPT.md for FromISR usage, PATTERNS/TRAPS.md for deadlock and stack overflow traps, and REFERENCE/HAL_DRIVERS.md for UART DMA and ADC integration. It covers task notifications, mutex priority inheritance, Tickless low-power STOP mode, STM32CubeMX configuration, and SEGGER SystemView or Percep TRACEalyzer debugging. A bundled freertos_config_check.py script validates FreeRTOSConfig.h and outputs JSON for CI. Developers reach for stm32-freertos-developer when writing ISR-safe queue sends, estimating stack high-water marks, or integrating HAL callbacks with portYIELD_FROM_ISR.
- Complete dynamic task example with xTaskCreate, priorities, and vTaskDelay using HAL GPIO toggles
- Static task allocation pattern with StaticTask_t and preallocated stacks (document continuation in full skill)
- Shows HAL_Init, clock config, and vTaskStartScheduler failure handling
- Printf-style runtime logging hooks for debugging concurrent tasks
Stm32 Freertos Developer by the numbers
- 434 all-time installs (skills.sh)
- Ranked #439 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/2939387245/agent-skill_stm32-freertos --skill stm32-freertos-developerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 434 |
|---|---|
| repo stars | ★ 13 |
| Security audit | 3 / 3 scanners passed |
| Last updated | January 14, 2026 |
| Repository | 2939387245/agent-skill_stm32-freertos ↗ |
How do you create FreeRTOS tasks on STM32?
Bootstrap STM32 firmware with FreeRTOS tasks using proven dynamic and static creation patterns from the skill examples.
Who is it for?
Embedded developers on STM32CubeMX projects who need FreeRTOS v10+ task, queue, and ISR patterns with HAL peripheral integration.
Skip if: Web or mobile application developers without STM32 hardware who do not write ARM Cortex-M C firmware.
When should I use this skill?
An STM32 project needs FreeRTOS tasks, queues, semaphores, HAL drivers, or ISR-safe FromISR patterns implemented or reviewed.
What you get
C task functions, FreeRTOSConfig.h settings, HAL driver templates, and freertos_config_check.py JSON validation output.
- FreeRTOS task C code
- HAL driver templates
- FreeRTOSConfig.h validation JSON
By the numbers
- Indexes 10 reference, example, and pattern documentation files
- Supports 10+ ARM Cortex-M STM32 series including F4, F7, and H7
- Bundles freertos_config_check.py for FreeRTOSConfig.h validation
Files
STM32 + FreeRTOS 嵌入式开发专家
AI 使用规则
当用户提出请求时,根据以下规则选择性读取文档。不要一次性读取所有文档,只读取与用户请求相关的文件。
代码生成请求
| 用户说... | 读取文件 |
|---|---|
| "创建任务" / "创建队列" / "信号量" / "互斥锁" / "事件组" / "任务通知" | EXAMPLES/BASIC.md |
| "UART 驱动" / "ADC 驱动" / "I2C 驱动" / "TIM 驱动" | REFERENCE/HAL_DRIVERS.md + EXAMPLES/DRIVERS.md |
| "printf 重定向" / "printf 输出" / "ITM" | REFERENCE/STD_LIBS.md |
| "DMA 接收" / "不定长数据" | REFERENCE/HAL_DRIVERS.md + EXAMPLES/DRIVERS.md |
代码审查/问题排查请求
| 用户说... | 读取文件 |
|---|---|
| "中断" / "FromISR" / "portYIELD_FROM_ISR" / "优先级配置" | PATTERNS/INTERRUPT.md |
| "死锁" / "优先级反转" / "堆栈溢出" / "资源泄漏" | PATTERNS/TRAPS.md |
| "生产者-消费者" / "状态机" / "资源池" / "发布-订阅" | PATTERNS/DESIGN.md |
调试请求
| 用户说... | 读取文件 |
|---|---|
| "SystemView" / "TRACEalyzer" / "trace 分析" | REFERENCE/DEBUG_TOOLS.md |
| "任务统计" / "堆栈监控" / "CPU 使用率" | REFERENCE/DEBUG_TOOLS.md |
高级应用请求
| 用户说... | 读取文件 |
|---|---|
| "低功耗" / "Tickless" / "STOP 模式" | EXAMPLES/ADVANCED.md |
| "CubeMX 配置" / "STM32CubeMX" | EXAMPLES/ADVANCED.md |
| "传感器融合" / "多任务" | EXAMPLES/ADVANCED.md |
API 查询请求
| 用户说... | 读取文件 |
|---|---|
| "xTaskCreate 参数" / "API 语法" / "函数说明" | REFERENCE/FREERTOS_API.md |
使用方法
如果用户请求不够明确,无法判断读取哪个文件: 1. 先读取 SKILL.md 和 REFERENCE/FREERTOS_API.md 2. 询问用户具体需求 3. 根据回答读取正确的文件
不要一次性读取所有文件!只读取与用户请求相关的文件。
---
技能简介
本技能专为在 STM32 微控制器 上使用 FreeRTOS 实时操作系统 进行嵌入式开发而设计。AI 将作为"嵌入式系统架构师",帮助你编写安全、高效、可维护的 C 代码。
适用场景:
- 使用 STM32CubeMX 生成的工程
- ARM Cortex-M 全系列(F0/F1/F3/F4/F7/H7/G0/L0/L4/L5 等)
- FreeRTOS v10+ 版本
- 原生 FreeRTOS API 或 CMSIS-RTOS v2 API
---
使用场景
代码生成
- 创建任务、队列、信号量、互斥锁
- 编写外设驱动模板(UART DMA、ADC DMA、I2C 等)
- 配置低功耗 Tickless 模式
- 实现 printf 重定向(ITM_SendChar / UART)
代码审查
- 分析任务优先级配置是否合理
- 检查中断与任务交互的正确性
- 排查死锁、优先级反转、资源泄漏
- 验证 FreeRTOSConfig.h 配置
教学辅导
- 解释 FreeRTOS 核心概念(任务调度、上下文切换)
- 演示生产者-消费者、发布-订阅等设计模式
- 指导调试工具使用(SEGGER SystemView、TRACEalyzer)
---
核心能力模块
任务管理
- 创建静态/动态任务(
xTaskCreate,xTaskCreateStatic) - 设置优先级、堆栈大小、任务名
- 任务状态监控(
uxTaskGetStackHighWaterMark)
任务间通信
- 队列(Queue):生产者-消费者模型
- 信号量(Semaphore):二值/计数型
- 互斥锁(Mutex):避免竞态条件,含优先级继承
- 事件组(Event Groups):多条件等待
- 任务通知(Task Notifications):轻量级替代方案
中断与任务交互
- 在 HAL 回调中使用
xQueueSendFromISR/vTaskNotifyGiveFromISR - ISR 中不阻塞,仅发送通知
portYIELD_FROM_ISR(xHigherPriorityTaskWoken)用法
外设集成
- UART DMA + 队列:不定长数据接收(IDLE 中断)
- ADC DMA + 任务通知:连续采样
- I2C 主/从模式:传感器通信
- TIM 定时器/PWM:周期任务
内存与性能优化
- 推荐静态分配(避免 heap 碎片)
- 合理估算堆栈大小
- 开启
configASSERT()和configCHECK_FOR_STACK_OVERFLOW - 使用
configUSE_PREEMPTION = 1提升实时性
调试与诊断
- 生成任务列表打印代码(
vTaskList) - SEGGER SystemView(Keil/IAR 环境)
- Percep TRACEalyzer(FreeRTOS 环境)
- ITM/SWO 配置与 printf 调试
---
文件索引
| 类型 | 文件 | 说明 |
|---|---|---|
| 主文件 | SKILL.md | AI 唯一自动读取的文件 |
| 用户指南 | USER_GUIDE.md | 仅用户阅读,不读取 |
| API 参考 | REFERENCE/FREERTOS_API.md | FreeRTOS API 语法 |
| API 参考 | REFERENCE/STD_LIBS.md | 标准库集成 |
| API 参考 | REFERENCE/HAL_DRIVERS.md | HAL 外设驱动 |
| API 参考 | REFERENCE/DEBUG_TOOLS.md | 调试工具配置 |
| 代码示例 | EXAMPLES/BASIC.md | 基础组件示例 |
| 代码示例 | EXAMPLES/DRIVERS.md | 外设驱动模板 |
| 代码示例 | EXAMPLES/ADVANCED.md | 高级应用 |
| 设计模式 | PATTERNS/DESIGN.md | 设计模式 |
| 设计模式 | PATTERNS/INTERRUPT.md | 中断最佳实践 |
| 设计模式 | PATTERNS/TRAPS.md | 常见陷阱 |
---
脚本工具
freertos_config_check.py
验证 FreeRTOSConfig.h 关键配置:
python scripts/freertos_config_check.py FreeRTOSConfig.h输出 JSON 格式,便于 CI 集成。
高级应用
本文档包含多任务传感器融合、Tickless 低功耗模式、STM32CubeMX 配置等高级应用示例。
---
3.1 多任务传感器融合
系统架构
+-------------------+ +-------------------+
| 传感器采集任务 | --> | 数据处理任务 |
| (I2C 传感器) | | (滤波、融合) |
+-------------------+ +-------------------+
|
v
+-------------------+
| 数据发送任务 |
| (UART/BLE/USB) |
+-------------------+实现代码
#include "main.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#define QUEUE_SIZE 20
/* 数据类型定义 */
typedef struct {
float acc_x, acc_y, acc_z;
float gyro_x, gyro_y, gyro_z;
uint32_t timestamp;
} SensorData_t;
typedef struct {
float roll, pitch, yaw;
uint32_t timestamp;
} FusionData_t;
/* 通信机制 */
QueueHandle_t g_sensor_queue; /* 原始数据队列 */
QueueHandle_t g_fusion_queue; /* 融合数据队列 */
SemaphoreHandle_t g_i2c_mutex; /* I2C 互斥锁 */
/* 传感器采集任务 */
void vSensorCollectTask(void *pvParameters)
{
SensorData_t data;
I2C_HandleTypeDef *hi2c = (I2C_HandleTypeDef *)pvParameters;
while (1)
{
/* 获取 I2C 互斥锁 */
if (xSemaphoreTake(g_i2c_mutex, pdMS_TO_TICKS(100)) == pdTRUE)
{
/* 读取传感器(MPU6050 简化版) */
if (MPU6050_ReadAll(hi2c, &data) == HAL_OK)
{
data.timestamp = HAL_GetTick();
xQueueSend(g_sensor_queue, &data, 0);
}
xSemaphoreGive(g_i2c_mutex);
}
vTaskDelay(pdMS_TO_TICKS(10)); /* 100Hz 采样 */
}
}
/* 数据处理任务(互补滤波) */
void vDataProcessTask(void *pvParameters)
{
SensorData_t raw_data;
FusionData_t fused_data;
static float gyro_x = 0, gyro_y = 0;
static float acc_roll = 0, acc_pitch = 0;
const float alpha = 0.98f; /* 互补滤波系数 */
while (1)
{
/* 接收原始数据 */
if (xQueueReceive(g_sensor_queue, &raw_data, portMAX_DELAY) == pdTRUE)
{
/* 加速度计角度计算 */
acc_roll = atan2(raw_data.acc_y, raw_data.acc_z) * 180.0f / 3.14159f;
acc_pitch = atan2(-raw_data.acc_x, raw_data.acc_z) * 180.0f / 3.14159f;
/* 互补滤波 */
gyro_x += raw_data.gyro_x * 0.01f;
gyro_y += raw_data.gyro_y * 0.01f;
fused_data.roll = alpha * gyro_x + (1 - alpha) * acc_roll;
fused_data.pitch = alpha * gyro_y + (1 - alpha) * acc_pitch;
fused_data.yaw += raw_data.gyro_z * 0.01f;
fused_data.timestamp = raw_data.timestamp;
/* 发送到融合队列 */
xQueueSend(g_fusion_queue, &fused_data, 0);
}
}
}
/* 数据发送任务 */
void vDataSendTask(void *pvParameters)
{
FusionData_t fused_data;
UART_HandleTypeDef *huart = (UART_HandleTypeDef *)pvParameters;
while (1)
{
if (xQueueReceive(g_fusion_queue, &fused_data, portMAX_DELAY) == pdTRUE)
{
/* 格式化输出 */
printf("Roll: %.2f, Pitch: %.2f, Yaw: %.2f\r\n",
fused_data.roll, fused_data.pitch, fused_data.yaw);
}
}
}
/* 主函数 */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_I2C1_Init();
MX_UART1_Init();
/* 创建通信机制 */
g_sensor_queue = xQueueCreate(QUEUE_SIZE, sizeof(SensorData_t));
g_fusion_queue = xQueueCreate(QUEUE_SIZE, sizeof(FusionData_t));
g_i2c_mutex = xSemaphoreCreateMutex();
if (g_sensor_queue && g_fusion_queue && g_i2c_mutex)
{
/* 创建任务 */
xTaskCreate(vSensorCollectTask, "SensorCollect", 256,
&hi2c1, 3, NULL);
xTaskCreate(vDataProcessTask, "DataProcess", 256,
NULL, 2, NULL);
xTaskCreate(vDataSendTask, "DataSend", 128,
&huart1, 1, NULL);
vTaskStartScheduler();
}
while (1);
}---
3.2 Tickless 低功耗模式
配置步骤
1. FreeRTOSConfig.h 配置
/* FreeRTOSConfig.h */
#define configUSE_TICKLESS_IDLE 2 /* 启用 Tickless */
#define configPRE_SLEEP_PROCESSING(x) vPreSleepProcessing(x)
#define configPOST_SLEEP_PROCESSING(x) vPostSleepProcessing(x)
/* 外部声明 */
extern void vPreSleepProcessing(uint32_t *expected_idle_time);
extern void vPostSleepProcessing(uint32_t *expected_idle_time);2. Tickless 实现代码
#include "main.h"
#include "FreeRTOS.h"
/* 低功耗模式配置 */
#define STOP_MODE_TIMEOUT pdMS_TO_TICKS(100) /* STOP 模式超时 */
/* 进入低功耗前的处理 */
void vPreSleepProcessing(uint32_t *expected_idle_time)
{
/* 降低时钟频率 */
if (*expected_idle_time > STOP_MODE_TIMEOUT)
{
/* 配置为 STOP 模式 */
/* 此处根据具体芯片配置 */
__HAL_RCC_PWR_CLK_ENABLE();
/* 降低主频 */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
}
}
/* 退出低功耗后的处理 */
void vPostSleepProcessing(uint32_t *expected_idle_time)
{
(void)expected_idle_time;
/* 恢复时钟频率 */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/* 重新配置时钟 */
SystemClock_Config();
}
/* 空闲任务钩子(FreeRTOS 自动调用) */
void vApplicationIdleHook(void)
{
/* 可以在此处进入最低功耗模式 */
__WFI(); /* Wait For Interrupt */
}3. STOP 模式实现
#include "stm32l4xx_hal_pwr.h"
#include "stm32l4xx_ll_pwr.h"
void vEnterStopMode(uint32_t timeout_ms)
{
/* 保存当前时钟 */
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
HAL_RCC_OscConfig(&RCC_OscInitStruct);
/* 清除唤醒标志 */
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
/* 进入 STOP 模式 */
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,
PWR_STOPENTRY_WFI);
}
void vStopModeTask(void *pvParameters)
{
while (1)
{
/* 正常模式运行 */
vTaskDelay(pdMS_TO_TICKS(100));
/* 进入低功耗模式 */
printf("进入 STOP 模式\r\n");
/* 此处应使用 vPortSuppressTicksAndSleep() */
/* 实际使用 FreeRTOS 自动处理 */
printf("退出 STOP 模式\r\n");
}
}---
3.3 STM32CubeMX 配置说明
FreeRTOS 中间件配置
STM32CubeMX → Middleware → FreeRTOS
Config Parameters:
├── VERSION: V10 (or CMSIS-R2)
├── TOTAL_HEAP_SIZE: 3072 (bytes)
├── USE_IDLE_HOOK: ✓
├── USE_TICK_HOOK: ✗
├── USE_MUTEXES: ✓
├── USE_RECURSIVE_MUTEXES: ✓
├── USE_COUNTING_SEMAPHORES: ✓
├── USE_TASK_NOTIFICATIONS: ✓
├── ENFORCE_SYSTEM_VIEWER: ✗ (默认)
└── CHECK_FOR_STACK_OVERFLOW: 2 (Method 2)NVIC 配置
STM32CubeMX → Configuration → NVIC
┌─────────────────────────────────────────────────────────────┐
│ NVIC [x] │
├─────────────────────────────────────────────────────────────┤
│ NVIC_Priority_Group: NVIC_PRIORITYGROUP_4 │
├─────────────────────────────────────────────────────────────┤
│ 优先级表: │
│ │
│ System │ 优先级 │ 子优先级 │ 抢占使能 │ │
│ ─────────────────┼────────┼──────────┼──────────┤ │
│ NonMaskableInt │ 0 │ 0 │ - │ │
│ HardFault │ 0 │ 0 │ - │ │
│ MemoryManagement│ 0 │ 0 │ - │ │
│ BusFault │ 0 │ 0 │ - │ │
│ UsageFault │ 0 │ 0 │ - │ │
│ SVCall │ 0 │ 0 │ - │ │
│ DebugMonitor │ 0 │ 0 │ - │ │
│ PendSV │ 15 │ 0 │ - │ FreeRTOS│
│ SysTick │ 15 │ 0 │ - │ FreeRTOS│
│ USART1 │ 5 │ 0 │ ✓ │ 外设 │
│ DMA1_Channel1 │ 6 │ 0 │ ✓ │ 外设 │
│ ... │ ... │ ... │ ... │ ... │
└─────────────────────────────────────────────────────────────┘
关键点:
1. PendSV 和 SysTick 必须设为最低优先级(15)
2. FreeRTOS 内核中断优先级为 5(configKERNEL_INTERRUPT_PRIORITY)
3. 外设中断优先级应高于内核(5-15)外设 + DMA 配置
UART1 配置:
├── Mode: Asynchronous
├── Baud Rate: 115200
├── Data Bits: 8
├── Parity: None
├── Stop Bits: 1
├── DMA Settings: ✓
│ ├── USART1_TX: DMA1 Channel 4
│ └── USART1_RX: DMA1 Channel 5
└── NVIC Settings:
├── USART1 global interrupt: ✓ (Priority 5)
└── DMA1 channel global interrupt: ✓ (Priority 6)
ADC1 配置:
├── Mode: Scan Continuous Conversion
├── Enable Regular Conversions: ✓
├── Number of Conversion: 3
├── DMA Continuos Requests: ✓
├── DMA Settings: ✓
│ └── ADC1: DMA1 Channel 1
└── NVIC Settings:
└── DMA1 channel 1/2/3 interrupt: ✓ (Priority 6)生成的代码修改
/* 生成的 main.c 需要修改 */
/* 1. HAL 初始化放在 FreeRTOS 之前 */
int main(void)
{
HAL_Init();
SystemClock_Config();
/* MX 初始化(不包含 FreeRTOS) */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* 创建任务 */
StartDefaultTask();
vTaskStartScheduler(); /* 在此处启动调度器 */
}
/* 2. 删除默认生成的 MX_FREERTOS_Init() */
/* 3. 任务函数放在 main 之前或独立文件 */
/* 4. 任务堆栈大小根据需求调整 */基础示例
本文档包含 FreeRTOS 基础组件的完整示例代码。
---
1.1 动态任务创建
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
/* 任务函数 */
void vTask1(void *pvParameters)
{
while (1)
{
/* 任务代码 */
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
printf("Task1 运行\r\n");
/* 延时 500ms */
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void vTask2(void *pvParameters)
{
while (1)
{
/* 任务代码 */
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
printf("Task2 运行\r\n");
/* 延时 1000ms */
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
/* 创建任务 */
BaseType_t ret1 = xTaskCreate(vTask1, "Task1",
128, /* 堆栈深度(字) */
NULL, /* 参数 */
2, /* 优先级 */
NULL);/* 句柄 */
BaseType_t ret2 = xTaskCreate(vTask2, "Task2",
128,
NULL,
2,
NULL);
if (ret1 == pdPASS && ret2 == pdPASS)
{
printf("任务创建成功,启动调度器\r\n");
vTaskStartScheduler();
}
else
{
printf("任务创建失败\r\n");
}
while (1);
}---
1.2 静态任务创建
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
/* 静态任务控制块和堆栈(全局变量) */
static StaticTask_t xTask1_TCB;
static StackType_t xTask1_Stack[128];
static StaticTask_t xTask2_TCB;
static StackType_t xTask2_Stack[256];
/* 任务函数 */
void vTask1(void *pvParameters)
{
while (1)
{
printf("静态任务1运行\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void vTask2(void *pvParameters)
{
while (1)
{
printf("静态任务2运行\r\n");
vTaskDelay(pdMS_TO_TICKS(500));
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
/* 创建静态任务 */
TaskHandle_t handle1 = xTaskCreateStatic(vTask1, "Task1",
128, /* 堆栈深度(字) */
NULL, /* 参数 */
2, /* 优先级 */
xTask1_Stack,
&xTask1_TCB);
TaskHandle_t handle2 = xTaskCreateStatic(vTask2, "Task2",
256,
NULL,
2,
xTask2_Stack,
&xTask2_TCB);
if (handle1 != NULL && handle2 != NULL)
{
printf("静态任务创建成功,启动调度器\r\n");
vTaskStartScheduler();
}
else
{
printf("静态任务创建失败\r\n");
}
while (1);
}---
1.3 队列(生产者-消费者)
#include "main.h"
#include "FreeRTOS.h"
#include "queue.h"
/* 数据帧定义 */
typedef struct {
uint32_t id;
float value;
uint32_t timestamp;
} DataFrame_t;
/* 全局队列 */
QueueHandle_t g_data_queue;
/* 生产者任务 */
void vProducerTask(void *pvParameters)
{
DataFrame_t frame;
uint32_t count = 0;
while (1)
{
/* 产生数据 */
frame.id = count;
frame.value = (float)(count * 0.1f);
frame.timestamp = HAL_GetTick();
/* 发送到队列 */
if (xQueueSend(g_data_queue, &frame, 0) == pdTRUE)
{
printf("生产者: 发送数据 #%lu\r\n", count);
}
else
{
printf("生产者: 队列满,丢弃数据\r\n");
}
count++;
vTaskDelay(pdMS_TO_TICKS(200));
}
}
/* 消费者任务 */
void vConsumerTask(void *pvParameters)
{
DataFrame_t frame;
while (1)
{
/* 从队列接收数据(无限等待) */
if (xQueueReceive(g_data_queue, &frame, portMAX_DELAY) == pdTRUE)
{
printf("消费者: 收到数据 #%lu, 值=%.2f, 时间=%lu\r\n",
frame.id, frame.value, frame.timestamp);
}
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
/* 创建队列:10 个元素,每个元素大小 sizeof(DataFrame_t) */
g_data_queue = xQueueCreate(10, sizeof(DataFrame_t));
if (g_data_queue != NULL)
{
/* 创建任务 */
xTaskCreate(vProducerTask, "Producer", 128, NULL, 2, NULL);
xTaskCreate(vConsumerTask, "Consumer", 128, NULL, 2, NULL);
vTaskStartScheduler();
}
else
{
printf("队列创建失败\r\n");
}
while (1);
}---
1.4 二值信号量(任务同步)
#include "main.h"
#include "FreeRTOS.h"
#include "semphr.h"
/* 二值信号量 */
SemaphoreHandle_t g_button_sem;
/* 按钮中断回调 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == BUTTON_Pin)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* 释放信号量(从 ISR) */
xSemaphoreGiveFromISR(g_button_sem, &xHigherPriorityTaskWoken);
/* 如果有高优先级任务被唤醒,进行上下文切换 */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
/* 按钮处理任务 */
void vButtonTask(void *pvParameters)
{
while (1)
{
/* 等待信号量(阻塞) */
if (xSemaphoreTake(g_button_sem, portMAX_DELAY) == pdTRUE)
{
printf("按钮按下!\r\n");
/* 执行按钮处理逻辑 */
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
/* 创建二值信号量 */
g_button_sem = xSemaphoreCreateBinary();
if (g_button_sem != NULL)
{
xTaskCreate(vButtonTask, "ButtonTask", 128, NULL, 2, NULL);
vTaskStartScheduler();
}
while (1);
}---
1.5 互斥锁(共享资源保护)
#include "main.h"
#include "FreeRTOS.h"
#include "semphr.h"
/* 互斥锁 */
SemaphoreHandle_t g_uart_mutex;
/* 共享资源:UART */
UART_HandleTypeDef huart1;
/* 线程安全的打印函数 */
void thread_safe_print(char *str)
{
/* 获取互斥锁 */
if (xSemaphoreTake(g_uart_mutex, pdMS_TO_TICKS(100)) == pdTRUE)
{
/* 访问共享资源 */
printf("%s", str);
/* 释放互斥锁 */
xSemaphoreGive(g_uart_mutex);
}
}
/* 任务1:使用 UART */
void vTaskUart1(void *pvParameters)
{
while (1)
{
thread_safe_print("Task1 正在使用 UART\r\n");
vTaskDelay(pdMS_TO_TICKS(300));
}
}
/* 任务2:使用 UART */
void vTaskUart2(void *pvParameters)
{
while (1)
{
thread_safe_print("Task2 正在使用 UART\r\n");
vTaskDelay(pdMS_TO_TICKS(500));
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
/* 创建互斥锁(带优先级继承) */
g_uart_mutex = xSemaphoreCreateMutex();
if (g_uart_mutex != NULL)
{
xTaskCreate(vTaskUart1, "UartTask1", 128, NULL, 2, NULL);
xTaskCreate(vTaskUart2, "UartTask2", 128, NULL, 2, NULL);
vTaskStartScheduler();
}
while (1);
}---
1.6 事件组
#include "main.h"
#include "FreeRTOS.h"
#include "event_groups.h"
/* 事件位定义 */
#define EVENT_BIT_0 (1 << 0) /* 0x01 */
#define EVENT_BIT_1 (1 << 1) /* 0x02 */
#define EVENT_BIT_2 (1 << 2) /* 0x04 */
#define EVENT_ALL_BITS (EVENT_BIT_0 | EVENT_BIT_1 | EVENT_BIT_2)
/* 事件组 */
EventGroupHandle_t g_event_group;
/* 任务1:设置 EVENT_BIT_0 */
void vTask1(void *pvParameters)
{
while (1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
xEventGroupSetBits(g_event_group, EVENT_BIT_0);
printf("Task1 设置 EVENT_BIT_0\r\n");
}
}
/* 任务2:设置 EVENT_BIT_1 */
void vTask2(void *pvParameters)
{
while (1)
{
vTaskDelay(pdMS_TO_TICKS(1500));
xEventGroupSetBits(g_event_group, EVENT_BIT_1);
printf("Task2 设置 EVENT_BIT_1\r\n");
}
}
/* 任务3:等待所有事件位 */
void vTask3(void *pvParameters)
{
EventBits_t bits;
while (1)
{
/* 等待所有位(AND 逻辑) */
bits = xEventGroupWaitBits(g_event_group,
EVENT_ALL_BITS,
pdTRUE, /* 退出时清除位 */
pdTRUE, /* 等待所有位(AND) */
portMAX_DELAY);
if ((bits & EVENT_ALL_BITS) == EVENT_ALL_BITS)
{
printf("Task3: 收到所有事件!bits=0x%02X\r\n", (unsigned int)bits);
}
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
/* 创建事件组 */
g_event_group = xEventGroupCreate();
if (g_event_group != NULL)
{
xTaskCreate(vTask1, "Task1", 128, NULL, 2, NULL);
xTaskCreate(vTask2, "Task2", 128, NULL, 2, NULL);
xTaskCreate(vTask3, "Task3", 128, NULL, 3, NULL); /* 高优先级 */
vTaskStartScheduler();
}
while (1);
}---
1.7 任务通知
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
/* 任务通知句柄 */
TaskHandle_t g_notify_task_handle;
/* 发送通知的任务 */
void vSenderTask(void *pvParameters)
{
uint32_t count = 0;
while (1)
{
vTaskDelay(pdMS_TO_TICKS(500));
/* 发送通知(带累加值) */
xTaskNotify(g_notify_task_handle, count, eSetValueWithOverwrite);
printf("发送通知: %lu\r\n", count);
count++;
}
}
/* 接收通知的任务 */
void vReceiverTask(void *pvParameters)
{
uint32_t notification_value;
while (1)
{
/* 等待通知(阻塞) */
if (xTaskNotifyWait(0, 0xFFFFFFFF, ¬ification_value,
portMAX_DELAY) == pdTRUE)
{
printf("收到通知: %lu\r\n", notification_value);
}
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
/* 创建接收任务并获取句柄 */
xTaskCreate(vReceiverTask, "Receiver", 128, NULL, 2,
&g_notify_task_handle);
xTaskCreate(vSenderTask, "Sender", 128, NULL, 2, NULL);
vTaskStartScheduler();
while (1);
}外设驱动模板
本文档包含 UART、ADC、I2C、printf 重定向等外设驱动的完整模板代码。
---
2.1 UART DMA + 队列 + IDLE 中断
头文件(uart_driver.h)
#ifndef UART_DRIVER_H
#define UART_DRIVER_H
#include "main.h"
#include "FreeRTOS.h"
#include "queue.h"
#define UART_RX_BUFFER_SIZE 256
typedef struct {
uint8_t data[UART_RX_BUFFER_SIZE];
uint16_t length;
} UartRxMessage_t;
extern QueueHandle_t g_uart_rx_queue;
void vUartDriverInit(void);
void vUartRxTask(void *pvParameters);
#endif /* UART_DRIVER_H */实现文件(uart_driver.c)
#include "uart_driver.h"
#include "string.h"
#include "stdio.h"
static uint8_t g_rx_buffer[UART_RX_BUFFER_SIZE];
static volatile uint16_t g_rx_index = 0;
static volatile uint8_t g_rx_complete = 0;
QueueHandle_t g_uart_rx_queue;
void vUartDriverInit(void)
{
/* 使能 IDLE 中断 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
/* 启动 DMA 接收 */
HAL_UART_Receive_DMA(&huart1, g_rx_buffer, UART_RX_BUFFER_SIZE);
/* 创建队列 */
g_uart_rx_queue = xQueueCreate(20, sizeof(UartRxMessage_t));
}
void HAL_UART_IDLECallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
/* 计算接收长度 */
uint16_t dma_counter = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
g_rx_index = UART_RX_BUFFER_SIZE - dma_counter;
g_rx_complete = 1;
/* 停止 DMA */
HAL_UART_DMAStop(&huart1);
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
g_rx_index = UART_RX_BUFFER_SIZE;
g_rx_complete = 1;
HAL_UART_DMAStop(&huart1);
}
}
void vUartRxTask(void *pvParameters)
{
UartRxMessage_t msg;
while (1)
{
/* 等待数据接收完成 */
while (g_rx_complete == 0)
{
vTaskDelay(pdMS_TO_TICKS(10));
}
/* 复制数据 */
memcpy(msg.data, g_rx_buffer, g_rx_index);
msg.length = g_rx_index;
/* 发送到队列 */
if (xQueueSend(g_uart_rx_queue, &msg, 0) == pdTRUE)
{
/* 重新启动 DMA */
g_rx_complete = 0;
HAL_UART_Receive_DMA(&huart1, g_rx_buffer,
UART_RX_BUFFER_SIZE);
}
else
{
printf("队列满,丢弃数据\r\n");
g_rx_complete = 0;
HAL_UART_Receive_DMA(&huart1, g_rx_buffer,
UART_RX_BUFFER_SIZE);
}
}
}
/* 数据处理任务示例 */
void vDataProcessTask(void *pvParameters)
{
UartRxMessage_t msg;
while (1)
{
if (xQueueReceive(g_uart_rx_queue, &msg, portMAX_DELAY) == pdTRUE)
{
printf("收到 %d 字节数据: ", msg.length);
/* 回显数据 */
for (int i = 0; i < msg.length; i++)
{
printf("%02X ", msg.data[i]);
}
printf("\r\n");
}
}
}---
2.2 ADC DMA + 任务通知
头文件(adc_driver.h)
#ifndef ADC_DRIVER_H
#define ADC_DRIVER_H
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#define ADC_CHANNEL_COUNT 3
extern TaskHandle_t g_adc_task_handle;
extern uint32_t g_adc_values[ADC_CHANNEL_COUNT];
void MX_ADC1_Init(void);
void vAdcTask(void *pvParameters);
#endif /* ADC_DRIVER_H */实现文件(adc_driver.c)
#include "adc_driver.h"
#include "string.h"
#define ADC_BUFFER_SIZE ADC_CHANNEL_COUNT
static uint32_t g_adc_buffer[ADC_BUFFER_SIZE];
uint32_t g_adc_values[ADC_CHANNEL_COUNT];
TaskHandle_t g_adc_task_handle;
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/* 配置 ADC */
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = ADC_CHANNEL_COUNT;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
HAL_ADC_Init(&hadc1);
/* 配置通道 */
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 3;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
/* 启动 DMA */
HAL_ADC_Start_DMA(&hadc1, g_adc_buffer, ADC_BUFFER_SIZE);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC1)
{
/* 复制数据 */
memcpy(g_adc_values, g_adc_buffer, sizeof(g_adc_values));
/* 发送任务通知 */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(g_adc_task_handle,
&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
void vAdcTask(void *pvParameters)
{
uint32_t tick_count = 0;
float voltage;
while (1)
{
/* 等待 ADC 转换完成 */
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
tick_count++;
/* 处理 ADC 数据 */
printf("ADC 采样 #%lu:\r\n", tick_count);
for (int i = 0; i < ADC_CHANNEL_COUNT; i++)
{
/* 转换为电压值 */
voltage = (float)g_adc_values[i] * 3.3f / 4095.0f;
printf(" CH%d: %.3f V (0x%04lX)\r\n",
i, voltage, g_adc_values[i]);
}
printf("\r\n");
}
}---
2.3 I2C 传感器读取(BME280)
头文件(bme280_driver.h)
#ifndef BME280_DRIVER_H
#define BME280_DRIVER_H
#include "main.h"
#include "FreeRTOS.h"
#include "queue.h"
#define BME280_ADDR 0x76
typedef struct {
float temperature; /* 温度 */
float humidity; /* 湿度 */
float pressure; /* 气压 */
} Bme280Data_t;
extern QueueHandle_t g_bme280_queue;
void vBme280Task(void *pvParameters);
HAL_StatusTypeDef BME280_Init(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef BME280_Read(I2C_HandleTypeDef *hi2c, Bme280Data_t *data);
#endif /* BME280_DRIVER_H */实现文件(bme280_driver.c)
#include "bme280_driver.h"
#include "string.h"
#define BME280_REG_ID 0xD0
#define BME280_REG_RESET 0xE0
#define BME280_REG_CTRL_HUM 0xF2
#define BME280_REG_CTRL_MEAS 0xF4
#define BME280_REG_CONFIG 0xF5
#define BME280_REG_DATA 0xF7
static uint8_t g_tx_buffer[4];
static uint8_t g_rx_buffer[8];
static int32_t g_compensate_t;
static uint32_t g_compensate_p;
static uint32_t g_compensate_h;
QueueHandle_t g_bme280_queue;
HAL_StatusTypeDef BME280_Init(I2C_HandleTypeDef *hi2c)
{
uint8_t id;
/* 读取芯片 ID */
g_tx_buffer[0] = BME280_REG_ID;
HAL_I2C_Master_Transmit(hi2c, BME280_ADDR << 1,
g_tx_buffer, 1, 100);
HAL_I2C_Master_Receive(hi2c, BME280_ADDR << 1,
&id, 1, 100);
if (id != 0x60)
{
return HAL_ERROR;
}
/* 软件复位 */
g_tx_buffer[0] = BME280_REG_RESET;
g_tx_buffer[1] = 0xB6;
HAL_I2C_Master_Transmit(hi2c, BME280_ADDR << 1,
g_tx_buffer, 2, 100);
vTaskDelay(pdMS_TO_TICKS(10));
/* 配置湿度采样 */
g_tx_buffer[0] = BME280_REG_CTRL_HUM;
g_tx_buffer[1] = 0x05; /* 16x 过采样 */
HAL_I2C_Master_Transmit(hi2c, BME280_ADDR << 1,
g_tx_buffer, 2, 100);
/* 配置测量 */
g_tx_buffer[0] = BME280_REG_CTRL_MEAS;
g_tx_buffer[1] = 0xB7; /* 温度 16x + 压力 16x */
HAL_I2C_Master_Transmit(hi2c, BME280_ADDR << 1,
g_tx_buffer, 2, 100);
/* 配置过滤器 */
g_tx_buffer[0] = BME280_REG_CONFIG;
g_tx_buffer[1] = 0x00; /* 无过滤器 */
HAL_I2C_Master_Transmit(hi2c, BME280_ADDR << 1,
g_tx_buffer, 2, 100);
return HAL_OK;
}
/* 简化的补偿计算(实际应使用 BME280 官方算法) */
static void BME280_Compensate(int32_t adc_t, int32_t adc_p,
int32_t adc_h)
{
g_compensate_t = adc_t / 100;
g_compensate_p = adc_p / 256;
g_compensate_h = adc_h / 1024;
}
HAL_StatusTypeDef BME280_Read(I2C_HandleTypeDef *hi2c, Bme280Data_t *data)
{
/* 发送数据寄存器地址 */
g_tx_buffer[0] = BME280_REG_DATA;
HAL_I2C_Master_Transmit(hi2c, BME280_ADDR << 1,
g_tx_buffer, 1, 100);
/* 读取 8 字节数据 */
HAL_I2C_Master_Receive(hi2c, BME280_ADDR << 1,
g_rx_buffer, 8, 100);
/* 解析数据 */
int32_t adc_p = (g_rx_buffer[0] << 12) | (g_rx_buffer[1] << 4) |
(g_rx_buffer[2] >> 4);
int32_t adc_t = (g_rx_buffer[3] << 12) | (g_rx_buffer[4] << 4) |
(g_rx_buffer[5] >> 4);
int32_t adc_h = (g_rx_buffer[6] << 8) | g_rx_buffer[7];
/* 补偿计算 */
BME280_Compensate(adc_t, adc_p, adc_h);
/* 填充结果 */
data->temperature = (float)g_compensate_t / 100.0f;
data->pressure = (float)g_compensate_p / 256.0f;
data->humidity = (float)g_compensate_h / 1024.0f;
return HAL_OK;
}
void vBme280Task(void *pvParameters)
{
Bme280Data_t sensor_data;
I2C_HandleTypeDef *hi2c1 = (I2C_HandleTypeDef *)pvParameters;
/* 初始化传感器 */
while (BME280_Init(hi2c1) != HAL_OK)
{
printf("BME280 初始化失败,重试...\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
printf("BME280 初始化成功\r\n");
/* 创建队列 */
g_bme280_queue = xQueueCreate(5, sizeof(Bme280Data_t));
while (1)
{
/* 读取传感器 */
if (BME280_Read(hi2c1, &sensor_data) == HAL_OK)
{
printf("温度: %.1f°C, 湿度: %.1f%%, 气压: %.1f hPa\r\n",
sensor_data.temperature,
sensor_data.humidity,
sensor_data.pressure);
/* 发送到队列 */
xQueueSend(g_bme280_queue, &sensor_data, 0);
}
else
{
printf("BME280 读取失败\r\n");
}
vTaskDelay(pdMS_TO_TICKS(2000)); /* 0.5Hz 采样 */
}
}---
2.4 printf 重定向(ITM_SendChar)
头文件(retarget.h)
#ifndef RETARGET_H
#define RETARGET_H
#include <stdio.h>
/* 重定向函数声明 */
int __attribute__((weak)) _write(int file, char *ptr, int len);
/* ITM 调试输出宏 */
#define ITM_Port8(n) (*((volatile unsigned char *)(0xE0000000 + 4 * n)))
#define ITM_Port16(n) (*((volatile unsigned short *)(0xE0000000 + 4 * n)))
#define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000 + 4 * n)))
#define DEMCR (*((volatile unsigned long *)(0xE000EDFC)))
#define TRCENA 0x01000000
#endif /* RETARGET_H */实现文件(retarget.c)
#include "retarget.h"
#include "core_cm4.h"
/* ITM 调试输出初始化 */
void ITM_Init(void)
{
/* 使能 TRACE 调试 */
DEMCR |= TRCENA;
/* 配置 SWO 引脚(根据具体芯片) */
/* 此处省略 GPIO 配置,ST-Link 默认配置 SWO */
/* 设置 ITM 端口 0 */
ITM->TCR = 0x00000009; /* ITMENA | ATVALID */
ITM->TER = 0x00000001; /* 使能端口 0 */
}
/* 重定向 fputc 到 ITM */
int __attribute__((weak)) _write(int file, char *ptr, int len)
{
(void)file;
for (int i = 0; i < len; i++)
{
/* 等待 SWO 端口就绪 */
while (ITM->PORT[0].u32 == 0) {}
ITM->PORT[0].u8 = (uint8_t)ptr[i];
}
return len;
}
/* 重定向 puts */
int __attribute__((weak)) _puts(const char *str)
{
int i = 0;
while (str[i])
{
if (str[i] == '\n')
{
ITM_Port8(0) = '\r';
while (ITM_Port8(0) == 0) {}
}
ITM_Port8(0) = str[i];
while (ITM_Port8(0) == 0) {}
i++;
}
return i;
}
/* 使用示例 */
void vItmExampleTask(void *pvParameters)
{
ITM_Init();
while (1)
{
printf("Hello from ITM! Tick: %lu\r\n", HAL_GetTick());
vTaskDelay(pdMS_TO_TICKS(1000));
}
}设计模式
本文档介绍生产者-消费者、发布-订阅、状态机、资源池等设计模式的实现方法。
---
1.1 生产者-消费者(Producer-Consumer)
模式说明
生产者-消费者模式是嵌入式系统中最常用的模式之一,适用于:
- 数据采集与处理分离
- 传感器数据缓冲
- 串口数据接收与解析
实现架构
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 生产者任务 │ ---> │ 队列 │ ---> │ 消费者任务 │
│ 采集数据 │ │ 缓冲数据 │ │ 处理数据 │
└─────────────┘ └─────────────┘ └─────────────┘完整实现
#include "main.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#define QUEUE_LENGTH 20
#define QUEUE_ITEM_SIZE sizeof(uint32_t)
typedef struct {
uint32_t sensor_id;
uint32_t value;
uint32_t timestamp;
} SensorMessage_t;
/* 队列和信号量 */
QueueHandle_t g_sensor_queue;
SemaphoreHandle_t g_data_ready_sem;
/* 生产者任务:模拟传感器采集 */
void vSensorTask(void *pvParameters)
{
SensorMessage_t msg;
uint32_t count = 0;
while (1)
{
/* 采集传感器数据 */
msg.sensor_id = 1;
msg.value = HAL_ADC_GetValue(&hadc1); /* 假设已配置 ADC */
msg.timestamp = HAL_GetTick();
/* 发送数据到队列 */
if (xQueueSend(g_sensor_queue, &msg, 0) == pdTRUE)
{
/* 发送数据就绪信号 */
xSemaphoreGive(g_data_ready_sem);
}
count++;
vTaskDelay(pdMS_TO_TICKS(100)); /* 10Hz 采样 */
}
}
/* 消费者任务:处理数据 */
void vProcessTask(void *pvParameters)
{
SensorMessage_t msg;
while (1)
{
/* 等待数据就绪信号 */
if (xSemaphoreTake(g_data_ready_sem, portMAX_DELAY) == pdTRUE)
{
/* 从队列获取数据 */
while (xQueueReceive(g_sensor_queue, &msg, 0) == pdTRUE)
{
/* 处理数据 */
printf("传感器 #%lu: %lu (时间: %lu)\r\n",
msg.sensor_id, msg.value, msg.timestamp);
}
}
}
}
void vProducerConsumerDemo(void)
{
/* 创建队列 */
g_sensor_queue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE);
/* 创建二值信号量 */
g_data_ready_sem = xSemaphoreCreateBinary();
/* 创建任务 */
xTaskCreate(vSensorTask, "Sensor", 128, NULL, 2, NULL);
xTaskCreate(vProcessTask, "Process", 128, NULL, 2, NULL);
}变体:多生产者-多消费者
/* 多个生产者共享队列 */
void vSensorTask1(void *pvParameters)
{
SensorMessage_t msg;
msg.sensor_id = 1;
while (1) {
msg.value = read_sensor1();
xQueueSend(g_sensor_queue, &msg, 0);
vTaskDelay(pdMS_TO_TICKS(50));
}
}
void vSensorTask2(void *pvParameters)
{
SensorMessage_t msg;
msg.sensor_id = 2;
while (1) {
msg.value = read_sensor2();
xQueueSend(g_sensor_queue, &msg, 0);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
/* 消费者通过 sensor_id 区分数据源 */
void vProcessTaskMulti(void *pvParameters)
{
SensorMessage_t msg;
while (1) {
xQueueReceive(g_sensor_queue, &msg, portMAX_DELAY);
switch (msg.sensor_id) {
case 1:
process_sensor1_data(msg.value);
break;
case 2:
process_sensor2_data(msg.value);
break;
}
}
}---
1.2 发布-订阅(Publish-Subscribe)
模式说明
发布-订阅模式使用事件组实现,适用于:
- 多条件触发
- 广播通知
- 状态机事件
实现代码
#include "main.h"
#include "FreeRTOS.h"
#include "event_groups.h"
/* 事件位定义 */
#define EVENT_BUTTON_PRESS (1 << 0)
#define EVENT_SENSOR_ALARM (1 << 1)
#define EVENT_TIMEOUT (1 << 2)
#define EVENT_ALL (EVENT_BUTTON_PRESS | EVENT_SENSOR_ALARM | EVENT_TIMEOUT)
/* 事件组 */
EventGroupHandle_t g_event_group;
/* 订阅者任务 */
void vAlarmTask(void *pvParameters)
{
EventBits_t bits;
while (1)
{
/* 订阅所有事件 */
bits = xEventGroupWaitBits(g_event_group,
EVENT_ALL,
pdTRUE, /* 清除位 */
pdFALSE, /* 任一事件(OR) */
portMAX_DELAY);
if (bits & EVENT_BUTTON_PRESS)
{
printf("报警任务:收到按钮事件\r\n");
}
if (bits & EVENT_SENSOR_ALARM)
{
printf("报警任务:收到传感器报警\r\n");
/* 触发报警动作 */
}
}
}
/* 发布者任务 */
void vButtonTask(void *pvParameters)
{
while (1)
{
if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET)
{
/* 延时消抖 */
vTaskDelay(pdMS_TO_TICKS(50));
if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET)
{
/* 发布按钮事件 */
xEventGroupSetBits(g_event_group, EVENT_BUTTON_PRESS);
printf("按钮任务:发布按钮事件\r\n");
}
/* 等待释放 */
while (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET)
{
vTaskDelay(pdMS_TO_TICKS(10));
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void vPublishSubscribeDemo(void)
{
g_event_group = xEventGroupCreate();
xTaskCreate(vAlarmTask, "Alarm", 128, NULL, 2, NULL);
xTaskCreate(vButtonTask, "Button", 128, NULL, 2, NULL);
}---
1.3 状态机(State Machine)
模式说明
状态机模式适用于:
- 协议解析
- 业务流程控制
- 模式切换
实现代码
#include "main.h"
#include "FreeRTOS.h"
/* 状态机状态定义 */
typedef enum {
STATE_IDLE = 0,
STATE_INIT,
STATE_CONNECTING,
STATE_CONNECTED,
STATE_DISCONNECTED,
STATE_ERROR
} AppState_t;
/* 事件定义 */
typedef enum {
EVENT_NONE = 0,
EVENT_START,
EVENT_CONNECT,
EVENT_DISCONNECT,
EVENT_TIMEOUT,
EVENT_ERROR
} AppEvent_t;
/* 状态机控制块 */
typedef struct {
AppState_t current_state;
AppState_t next_state;
AppEvent_t pending_event;
TickType_t last_state_time;
} StateMachine_t;
/* 状态机实例 */
static StateMachine_t g_sm;
/* 状态处理函数 */
static void handle_idle(AppEvent_t event)
{
switch (event) {
case EVENT_START:
printf("状态机:从 IDLE 切换到 INIT\r\n");
g_sm.next_state = STATE_INIT;
break;
default:
break;
}
}
static void handle_init(AppEvent_t event)
{
switch (event) {
case EVENT_CONNECT:
printf("状态机:初始化完成,开始连接\r\n");
g_sm.next_state = STATE_CONNECTING;
break;
case EVENT_ERROR:
printf("状态机:初始化失败,进入错误状态\r\n");
g_sm.next_state = STATE_ERROR;
break;
default:
break;
}
}
static void handle_connecting(AppEvent_t event)
{
switch (event) {
case EVENT_TIMEOUT:
printf("状态机:连接超时,重试\r\n");
/* 重连逻辑 */
g_sm.next_state = STATE_CONNECTING;
break;
case EVENT_CONNECT:
printf("状态机:连接成功\r\n");
g_sm.next_state = STATE_CONNECTED;
break;
default:
break;
}
}
static void handle_connected(AppEvent_t event)
{
switch (event) {
case EVENT_DISCONNECT:
printf("状态机:断开连接\r\n");
g_sm.next_state = STATE_DISCONNECTED;
break;
case EVENT_ERROR:
printf("状态机:连接错误\r\n");
g_sm.next_state = STATE_ERROR;
break;
default:
break;
}
}
/* 状态机处理函数 */
void vStateMachineTask(void *pvParameters)
{
/* 初始化状态机 */
g_sm.current_state = STATE_IDLE;
g_sm.next_state = STATE_IDLE;
g_sm.pending_event = EVENT_NONE;
while (1)
{
/* 处理当前状态 */
switch (g_sm.current_state) {
case STATE_IDLE:
handle_idle(g_sm.pending_event);
break;
case STATE_INIT:
handle_init(g_sm.pending_event);
break;
case STATE_CONNECTING:
handle_connecting(g_sm.pending_event);
break;
case STATE_CONNECTED:
handle_connected(g_sm.pending_event);
break;
default:
break;
}
/* 状态转换 */
if (g_sm.next_state != g_sm.current_state)
{
printf("状态机转换: %d -> %d\r\n",
g_sm.current_state, g_sm.next_state);
g_sm.current_state = g_sm.next_state;
g_sm.last_state_time = xTaskGetTickCount();
}
/* 清空事件 */
g_sm.pending_event = EVENT_NONE;
vTaskDelay(pdMS_TO_TICKS(10));
}
}
/* 事件发送接口 */
void StateMachine_SendEvent(AppEvent_t event)
{
g_sm.pending_event = event;
}---
1.4 资源池管理
模式说明
资源池模式适用于:
- 固定大小缓冲区管理
- 动态对象复用
- 内存碎片避免
实现代码
#include "main.h"
#include "FreeRTOS.h"
#include "semphr.h"
#define BUFFER_POOL_SIZE 10
#define BUFFER_SIZE 128
/* 缓冲区项 */
typedef struct {
uint8_t data[BUFFER_SIZE];
uint32_t length;
uint32_t timestamp;
} BufferItem_t;
/* 缓冲区池 */
typedef struct {
BufferItem_t items[BUFFER_POOL_SIZE];
uint8_t used[BUFFER_POOL_SIZE];
SemaphoreHandle_t mutex;
SemaphoreHandle_t free_count;
} BufferPool_t;
/* 全局缓冲区池 */
static BufferPool_t g_buffer_pool;
/* 初始化缓冲区池 */
void BufferPool_Init(void)
{
memset(&g_buffer_pool, 0, sizeof(g_buffer_pool));
g_buffer_pool.mutex = xSemaphoreCreateMutex();
g_buffer_pool.free_count = xSemaphoreCreateCounting(BUFFER_POOL_SIZE,
BUFFER_POOL_SIZE);
/* 初始时所有缓冲区都可用 */
for (int i = 0; i < BUFFER_POOL_SIZE; i++)
{
xSemaphoreGive(g_buffer_pool.free_count);
}
}
/* 从池中获取缓冲区 */
BufferItem_t *BufferPool_Alloc(void)
{
BufferItem_t *item = NULL;
/* 等待空闲缓冲区 */
if (xSemaphoreTake(g_buffer_pool.free_count, pdMS_TO_TICKS(100)) == pdFALSE)
{
return NULL; /* 超时,无可用缓冲区 */
}
/* 获取互斥锁 */
if (xSemaphoreTake(g_buffer_pool.mutex, pdMS_TO_TICKS(50)) == pdFALSE)
{
/* 归还信号量 */
xSemaphoreGive(g_buffer_pool.free_count);
return NULL;
}
/* 查找空闲缓冲区 */
for (int i = 0; i < BUFFER_POOL_SIZE; i++)
{
if (g_buffer_pool.used[i] == 0)
{
g_buffer_pool.used[i] = 1;
item = &g_buffer_pool.items[i];
break;
}
}
xSemaphoreGive(g_buffer_pool.mutex);
return item;
}
/* 归还缓冲区到池 */
void BufferPool_Free(BufferItem_t *item)
{
if (item == NULL) return;
xSemaphoreTake(g_buffer_pool.mutex, portMAX_DELAY);
for (int i = 0; i < BUFFER_POOL_SIZE; i++)
{
if (&g_buffer_pool.items[i] == item)
{
g_buffer_pool.used[i] = 0;
memset(item, 0, sizeof(BufferItem_t));
break;
}
}
xSemaphoreGive(g_buffer_pool.mutex);
xSemaphoreGive(g_buffer_pool.free_count);
}
/* 获取池状态 */
void BufferPool_GetStats(uint32_t *total, uint32_t *used)
{
xSemaphoreTake(g_buffer_pool.mutex, portMAX_DELAY);
*total = BUFFER_POOL_SIZE;
*used = 0;
for (int i = 0; i < BUFFER_POOL_SIZE; i++)
{
if (g_buffer_pool.used[i]) (*used)++;
}
xSemaphoreGive(g_buffer_pool.mutex);
}
/* 使用示例 */
void vBufferPoolDemoTask(void *pvParameters)
{
BufferItem_t *buf;
uint32_t total, used;
BufferPool_Init();
while (1)
{
/* 分配缓冲区 */
buf = BufferPool_Alloc();
if (buf != NULL)
{
/* 使用缓冲区 */
buf->length = sprintf((char *)buf->data,
"消息 #%lu", HAL_GetTick());
buf->timestamp = HAL_GetTick();
printf("分配缓冲区 #%lu, 剩余: ", HAL_GetTick());
BufferPool_GetStats(&total, &used);
printf("%lu/%lu\r\n", total - used, total);
/* 模拟处理 */
vTaskDelay(pdMS_TO_TICKS(500));
/* 归还缓冲区 */
BufferPool_Free(buf);
}
else
{
printf("缓冲区池已满,等待...\r\n");
vTaskDelay(pdMS_TO_TICKS(100));
}
}
}中断最佳实践
本文档介绍 FromISR 函数使用规则、portYIELD_FROM_ISR 用法、NVIC 优先级配置等中断相关最佳实践。
---
2.1 FromISR 函数使用规则
基本规则
| 规则 | 说明 |
|---|---|
| 不阻塞 | FromISR 函数不能阻塞,只能在有限时间内返回 |
| 使用 pdFALSE | xTicksToWait 应设为 0 或 pdFALSE |
| 检查返回值 | 检查返回值确定是否需要上下文切换 |
| 调用 portYIELD_FROM_ISR | 如果 *pxHigherPriorityTaskWoken 为 pdTRUE,必须调用 |
正确用法
void UART1_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
{
uint8_t data = (uint8_t)(huart1.Instance->DR & 0xFF);
/* 从 ISR 发送数据到队列(不阻塞) */
xQueueSendFromISR(g_uart_queue, &data, &xHigherPriorityTaskWoken);
}
/* 如果有高优先级任务被唤醒,进行上下文切换 */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}错误用法
/* 错误:阻塞等待 */
void vBadIsrExample(void)
{
uint8_t data;
xQueueReceiveFromISR(g_uart_queue, &data, portMAX_DELAY); /* 错误! */
}
/* 错误:忘记调用 portYIELD_FROM_ISR */
void vBadIsrExample2(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(g_uart_queue, &data, &xHigherPriorityTaskWoken);
/* 忘记调用 portYIELD_FROM_ISR(xHigherPriorityTaskWoken); */
}
/* 错误:传递错误的超时值 */
void vBadIsrExample3(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(g_uart_queue, &data, &xHigherPriorityTaskWoken);
/* 传递了非 pdFALSE 值,虽然 FreeRTOS 会忽略,但仍不规范 */
}FromISR 函数列表
/* 队列 FromISR */
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken);
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,
void *pvBuffer,
BaseType_t *pxHigherPriorityTaskWoken);
/* 信号量 FromISR */
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken);
/* 事件组 FromISR */
BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t *pxHigherPriorityTaskWoken);
/* 任务通知 FromISR */
BaseType_t xTaskNotifyFromISR(TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction,
BaseType_t *pxHigherPriorityTaskWoken);
void vTaskNotifyGiveFromISR(TaskHandle_t xTaskHandle,
BaseType_t *pxHigherPriorityTaskWoken);---
2.2 portYIELD_FROM_ISR 用法
语法
void portYIELD_FROM_ISR(BaseType_t xHigherPriorityTaskWoken);作用
当 xHigherPriorityTaskWoken 为 pdTRUE 时,触发 PendSV 中断进行上下文切换,唤醒等待该信号的高优先级任务。
完整示例
void DMA1_Stream5_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
uint32_t flag = DMA1->HISR;
/* 清除中断标志 */
DMA1->HIFCR = flag;
if (flag & DMA_HISR_TCIF5)
{
/* DMA 传输完成 */
g_dma_complete = 1;
/* 通知任务 */
vTaskNotifyGiveFromISR(g_dma_task_handle,
&xHigherPriorityTaskWoken);
}
/* 必要时进行上下文切换 */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}简化版(FreeRTOS v10+)
/* FreeRTOS v10.0+ 支持的简化宏 */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
/* 等价于: */
if (xHigherPriorityTaskWoken == pdTRUE)
{
portYIELD();
}完整的中断处理流程
void USART1_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* 处理中断源 */
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
{
uint8_t data = huart1.Instance->DR;
/* 发送数据到队列(FromISR,不阻塞) */
if (xQueueSendFromISR(g_uart_queue, &data, &xHigherPriorityTaskWoken) == pdTRUE)
{
/* 队列接收成功,可能有高优先级任务被唤醒 */
}
}
/* 处理其他中断源... */
/* 清除中断标志 */
__HAL_UART_CLEAR_PEFLAG(&huart1);
/* 必要时进行上下文切换 */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}---
2.3 NVIC 优先级配置
Cortex-M 优先级分组
/* NVIC priority group 配置(STM32CubeMX 默认) */
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); /* 4 位抢占优先级,0 位子优先级 */
/* 优先级数值越小,优先级越高 */
/* 优先级范围:0(最高)- 15(最低) */FreeRTOS 优先级要求
/* FreeRTOSConfig.h */
/* 内核中断优先级(PendSV、SysTick) */
#define configKERNEL_INTERRUPT_PRIORITY 255 /* (15 << 4) */
/* 最大系统调用优先级(高于此值不能调用 FromISR API) */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 80 /* (5 << 4) */
/* 结论:
* - 优先级 0-4:只能用于内核,不能调用任何 FreeRTOS API
* - 优先级 5-15:可以调用 FromISR 结尾的 API
* - 优先级 15:最低(PendSV、SysTick 默认)
*/正确配置示例
/* 定时器中断(需要触发任务)→ 优先级 5 */
HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
/* UART 中断(需要 FromISR 发送队列)→ 优先级 6 */
HAL_NVIC_SetPriority(USART1_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/* DMA 中断(需要 FromISR 通知任务)→ 优先级 7 */
HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 7, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
/* 外部中断(按钮唤醒)→ 优先级 10 */
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 10, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
/* 注意:数值越小优先级越高 */错误配置示例
/* 错误:外设中断优先级太低(高于 kernel) */
HAL_NVIC_SetPriority(USART1_IRQn, 4, 0); /* 错误!会阻止中断服务程序调用 FreeRTOS API */
/* 正确:优先级 >= 5 */
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); /* 正确 */优先级数值与实际优先级的关系
/* Cortex-M 的优先级配置寄存器是 8 位,但通常只使用高 4 位 */
/* 优先级数值左移 4 位后写入 NVIC */
NVIC_SetPriority(IRQn, priority << 4);
/* 示例 */
NVIC_SetPriority(USART1_IRQn, 6 << 4); /* 优先级 6 */
/* 实际优先级计算 */
#define PRIORITY_SHIFT 4
#define NVIC_EncodePriority(Group, PreemptPriority, SubPriority) \
(((PreemptPriority) & 0x07) << PRIORITY_SHIFT) | \
((SubPriority) & 0x00)中断优先级配置最佳实践
/* 1. 先设置优先级分组,再配置具体中断 */
void MX_NVIC_Init(void)
{
/* 设置优先级分组:4 位抢占优先级,0 位子优先级 */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* 配置系统异常优先级(必须在分组之后) */
HAL_NVIC_SetPriority(MemoryManagement_IRQn, 0, 0);
HAL_NVIC_SetPriority(BusFault_IRQn, 0, 0);
HAL_NVIC_SetPriority(UsageFault_IRQn, 0, 0);
HAL_NVIC_SetPriority(SVCall_IRQn, 0, 0);
HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0); /* 最低 */
HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0); /* 最低 */
/* 配置外设中断 */
HAL_NVIC_SetPriority(USART1_IRQn, 6, 0);
HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 7, 0);
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 10, 0);
/* 使能中断 */
HAL_NVIC_EnableIRQ(USART1_IRQn);
HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}---
中断设计最佳实践
1. 最小化 ISR 执行时间
/* 不好:在 ISR 中做复杂处理 */
void vBadIsr(void)
{
/* 复杂的数据处理 - 耗时 */
process_data_in_isr();
/* 发送队列 */
xQueueSendFromISR(g_queue, &data, &xHigherPriorityTaskWoken);
}
/* 好:只在 ISR 中发送数据,复杂处理在任务中 */
void vGoodIsr(void)
{
/* 快速获取数据 */
uint8_t data = huart1.Instance->DR;
/* 发送到队列,让任务处理 */
xQueueSendFromISR(g_queue, &data, &xHigherPriorityTaskWoken);
}2. 用任务通知替代信号量
/* 用信号量 */
void vBadIsr(void)
{
xSemaphoreGiveFromISR(g_sem, &xHigherPriorityTaskWoken);
}
void vTask(void)
{
while (1)
{
xSemaphoreTake(g_sem, portMAX_DELAY);
/* 处理 */
}
}
/* 用任务通知(更轻量) */
void vGoodIsr(void)
{
vTaskNotifyGiveFromISR(g_task_handle, &xHigherPriorityTaskWoken);
}
void vTask(void)
{
while (1)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
/* 处理 */
}
}3. 避免在 ISR 中做浮点运算
/* 避免 */
void vBadIsr(void)
{
float result = complex_calculation(); /* 耗时 */
xQueueSendFromISR(g_queue, &result, &xHigherPriorityTaskWoken);
}
/* 推荐 */
void vGoodIsr(void)
{
uint32_t raw_data = get_raw_data();
xQueueSendFromISR(g_queue, &raw_data, &xHigherPriorityTaskWoken);
}
void vTask(void)
{
while (1)
{
uint32_t data;
xQueueReceive(g_queue, &data, portMAX_DELAY);
/* 在任务中做浮点运算 */
float result = (float)data * 0.123f;
}
}4. 避免在 ISR 中使用大内存
/* 避免 */
void vBadIsr(void)
{
uint8_t buffer[1024]; /* 栈上分配大数组 */
/* 处理 */
}
/* 推荐 */
static uint8_t g_isr_buffer[1024]; /* 静态分配 */
void vGoodIsr(void)
{
/* 使用静态缓冲区 */
memcpy(g_isr_buffer, source, 1024);
xQueueSendFromISR(g_queue, g_isr_buffer, &xHigherPriorityTaskWoken);
}常见陷阱
本文档介绍 FreeRTOS 开发中常见的陷阱:优先级反转、堆栈溢出、死锁、资源泄漏。
---
3.1 优先级反转
问题说明
高优先级任务 ────────────┐
│ 等待资源
中优先级任务 ────┐ │
│ 持有资源
低优先级任务 ────┘ │
结果:高优先级任务被低优先级任务阻塞,中优先级任务也无法运行优先级反转的危害
1. 高优先级任务等待低优先级任务释放资源 2. 中优先级任务抢占 CPU,导致低优先级任务无法运行 3. 高优先级任务被无限期阻塞
解决方案:优先级继承
/* 互斥锁自动实现优先级继承 */
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();
/* 使用互斥锁保护共享资源 */
void vAccessResource(void)
{
if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(100)) == pdPASS)
{
/* 访问共享资源 */
// ...
xSemaphoreGive(xMutex);
}
}完整示例
#include "main.h"
#include "FreeRTOS.h"
#include "semphr.h"
/* 任务优先级定义 */
#define PRIORITY_HIGH 3
#define PRIORITY_MEDIUM 2
#define PRIORITY_LOW 1
SemaphoreHandle_t g_resource_mutex;
uint32_t g_shared_resource = 0;
/* 低优先级任务:占用资源时间长 */
void vLowPriorityTask(void *pvParameters)
{
while (1)
{
if (xSemaphoreTake(g_resource_mutex, portMAX_DELAY) == pdPASS)
{
g_shared_resource++;
printf("低优先级任务:资源值 = %lu\r\n", g_shared_resource);
/* 模拟长时间占用 */
vTaskDelay(pdMS_TO_TICKS(2000));
xSemaphoreGive(g_resource_mutex);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
/* 高优先级任务:快速访问资源 */
void vHighPriorityTask(void *pvParameters)
{
TickType_t start_time;
while (1)
{
start_time = xTaskGetTickCount();
if (xSemaphoreTake(g_resource_mutex, pdMS_TO_TICKS(500)) == pdPASS)
{
g_shared_resource++;
printf("高优先级任务:资源值 = %lu\r\n", g_shared_resource);
xSemaphoreGive(g_resource_mutex);
}
else
{
printf("高优先级任务:等待超时\r\n");
}
printf("高优先级任务:等待时间 = %lu ms\r\n",
pdTICKS_TO_MS(xTaskGetTickCount() - start_time));
vTaskDelay(pdMS_TO_TICKS(500));
}
}
/* 解决优先级反转问题 */
void vPriorityInversionDemo(void)
{
/* 使用互斥锁,自动启用优先级继承 */
g_resource_mutex = xSemaphoreCreateMutex();
/* 创建任务 */
xTaskCreate(vLowPriorityTask, "Low", 128, NULL,
PRIORITY_LOW, NULL);
xTaskCreate(vHighPriorityTask, "High", 128, NULL,
PRIORITY_HIGH, NULL);
}优先级继承原理
/* xSemaphoreCreateMutex 内部实现 */
SemaphoreHandle_t xSemaphoreCreateMutex(void)
{
SemaphoreHandle_t xSemaphore;
/* 创建二值信号量 */
xSemaphore = xSemaphoreCreateBinary();
if (xSemaphore != NULL)
{
/* 设置为互斥锁模式,启用优先级继承 */
xSemaphore->ucType = queueQUEUE_TYPE_MUTEX;
/* 当高优先级任务等待互斥锁时,提升持有者任务的优先级 */
prvLockMutex(xSemaphore);
}
return xSemaphore;
}---
3.2 堆栈溢出
检测方法
/* FreeRTOSConfig.h */
#define configCHECK_FOR_STACK_OVERFLOW 2 /* 方法 2:检查堆栈指针 */
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
printf("堆栈溢出!任务: %s\r\n", pcTaskName);
/* 进入死循环,便于调试 */
while (1)
{
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
vTaskDelay(pdMS_TO_TICKS(100));
}
}检测方法对比
| 方法 | 说明 | 配置 |
|---|---|---|
| 方法 1 | 检查堆栈指针是否超出范围 | configCHECK_FOR_STACK_OVERFLOW = 1 |
| 方法 2 | 检查堆栈指针和堆栈内容 | configCHECK_FOR_STACK_OVERFLOW = 2 |
| 方法 3 | 任务上下文切换时检查 | configCHECK_FOR_STACK_OVERFLOW = 3 |
预防措施
/* 1. 合理设置堆栈大小 */
#define TASK_STACK_SIZE 256 /* 256 字 = 1024 字节 */
/* 2. 避免大数组在栈上分配 */
#define LARGE_BUFFER_SIZE 1024
/* 错误:栈上分配大数组 */
void vBadTask(void *pvParameters)
{
uint8_t large_buffer[1024]; /* 可能溢出 */
// ...
}
/* 正确:静态分配 */
static uint8_t g_buffer[LARGE_BUFFER_SIZE]; /* 静态 */
void vGoodTask(void *pvParameters)
{
/* 使用静态缓冲区 */
memcpy(g_buffer, source, LARGE_BUFFER_SIZE);
}
/* 3. 定期检查堆栈使用 */
void vStackMonitorTask(void *pvParameters)
{
TaskHandle_t tasks[10];
UBaseType_t count;
while (1)
{
count = uxTaskGetSystemState(tasks, 10, NULL);
for (UBaseType_t i = 0; i < count; i++)
{
UBaseType_t watermark = uxTaskGetStackHighWaterMark(tasks[i]);
TaskStatus_t *status = pxTaskGetTaskStatus(tasks[i]);
printf("任务 %s: 堆栈剩余 %lu 字\r\n",
status->pcTaskName, watermark);
/* 警告 */
if (watermark < 20)
{
printf("警告: %s 堆栈即将耗尽!\r\n", status->pcTaskName);
}
}
vTaskDelay(pdMS_TO_TICKS(5000));
}
}堆栈大小估算规则
/* 任务堆栈估算(字为单位) */
#define TASK_STACK_DEPTH_BASE 128 /* 基础需求 */
#define TASK_STACK_DEPTH_UART 256 /* UART 处理任务 */
#define TASK_STACK_DEPTH_COM 512 /* 通信任务 */
#define TASK_STACK_DEPTH_LARGE 1024 /* 复杂任务 */
/* 规则:
* - 简单循环任务: 128-256 字
* - 有字符串处理: +64-128 字
* - 有递归调用: +256 字
* - 有局部大数组: +数组大小
* - 使用 printf: +50-100 字
*/
/* 示例:复杂任务 */
xTaskCreate(vComplexTask, "Complex", 512, /* 512 字堆栈 */
NULL, osPriorityNormal, NULL);---
3.3 死锁
问题说明
两个或多个任务相互等待对方释放资源,导致永久阻塞。
死锁的四个必要条件
1. 互斥:资源不能被共享,只能独占 2. 持有并等待:任务持有资源,同时等待其他资源 3. 非抢占:资源不能被强制夺走 4. 循环等待:任务间形成等待环
示例
/* 死锁示例 */
void vDeadlockExample(void)
{
SemaphoreHandle_t mutex_a = xSemaphoreCreateMutex();
SemaphoreHandle_t mutex_b = xSemaphoreCreateMutex();
/* 任务1:先获取 A,再获取 B */
void vTask1(void *pvParameters)
{
while (1)
{
xSemaphoreTake(mutex_a, portMAX_DELAY);
vTaskDelay(1); /* 让出 CPU */
xSemaphoreTake(mutex_b, portMAX_DELAY); /* 等待 B */
// 使用资源
xSemaphoreGive(mutex_b);
xSemaphoreGive(mutex_a);
}
}
/* 任务2:先获取 B,再获取 A */
void vTask2(void *pvParameters)
{
while (1)
{
xSemaphoreTake(mutex_b, portMAX_DELAY);
vTaskDelay(1); /* 让出 CPU */
xSemaphoreTake(mutex_a, portMAX_DELAY); /* 等待 A */
// 使用资源
xSemaphoreGive(mutex_a);
xSemaphoreGive(mutex_b);
}
}
/* 可能发生死锁:
* 1. 任务1获取A,任务2获取B
* 2. 任务1尝试获取B(被任务2持有)
* 3. 任务2尝试获取A(被任务1持有)
* 4. 双方永久等待
*/
}解决方案
/* 解决方案1:统一获取顺序 */
void vTask1Fixed(void *pvParameters)
{
while (1)
{
/* 始终先获取 A,再获取 B */
xSemaphoreTake(mutex_a, portMAX_DELAY);
xSemaphoreTake(mutex_b, portMAX_DELAY);
// 使用资源
xSemaphoreGive(mutex_b);
xSemaphoreGive(mutex_a);
}
}
void vTask2Fixed(void *pvParameters)
{
while (1)
{
/* 同样先获取 A,再获取 B */
xSemaphoreTake(mutex_a, portMAX_DELAY);
xSemaphoreTake(mutex_b, portMAX_DELAY);
// 使用资源
xSemaphoreGive(mutex_b);
xSemaphoreGive(mutex_a);
}
}
/* 解决方案2:使用单一互斥锁 */
void vTaskWithSingleMutex(void *pvParameters)
{
SemaphoreHandle_t resource_mutex = xSemaphoreCreateMutex();
while (1)
{
/* 只使用一个互斥锁 */
if (xSemaphoreTake(resource_mutex, portMAX_DELAY) == pdPASS)
{
/* 访问所有共享资源 */
// access_resource_a();
// access_resource_b();
xSemaphoreGive(resource_mutex);
}
}
}
/* 解决方案3:超时获取 */
void vTaskWithTimeout(void *pvParameters)
{
while (1)
{
if (xSemaphoreTake(mutex_a, pdMS_TO_TICKS(100)) == pdPASS)
{
if (xSemaphoreTake(mutex_b, pdMS_TO_TICKS(100)) == pdPASS)
{
// 使用资源
xSemaphoreGive(mutex_b);
}
else
{
/* 超时,释放已获取的锁 */
printf("获取 mutex_b 超时,释放 mutex_a\r\n");
xSemaphoreGive(mutex_a);
}
}
else
{
vTaskDelay(pdMS_TO_TICKS(50));
}
}
}---
3.4 资源泄漏
问题说明
资源(内存、信号量、队列)未正确释放,导致系统资源耗尽。
内存泄漏
/* 内存泄漏示例 */
void vMemoryLeakTask(void *pvParameters)
{
while (1)
{
/* 每次循环分配内存但不释放 */
char *buffer = pvPortMalloc(256);
if (buffer != NULL)
{
process_data(buffer);
/* 忘记 vPortFree(buffer); */
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
/* 正确做法:在任务外部分配 */
char *g_buffer = NULL;
void vNoLeakTask(void *pvParameters)
{
g_buffer = pvPortMalloc(256);
if (g_buffer != NULL)
{
while (1)
{
process_data(g_buffer);
vTaskDelay(pdMS_TO_TICKS(100));
}
vPortFree(g_buffer); /* 任务删除时释放 */
}
}队列泄漏
/* 队列泄漏示例 */
void vQueueLeakTask(void *pvParameters)
{
QueueHandle_t local_queue;
while (1)
{
/* 每次循环创建新队列 */
local_queue = xQueueCreate(10, sizeof(uint32_t));
/* 发送数据 */
uint32_t data = HAL_GetTick();
xQueueSend(local_queue, &data, 0);
vTaskDelay(pdMS_TO_TICKS(1000));
/* 忘记删除队列 */
/* vQueueDelete(local_queue); */
}
}
/* 正确做法:使用全局队列 */
QueueHandle_t g_global_queue;
void vCorrectQueueTask(void *pvParameters)
{
/* 初始化时创建队列 */
g_global_queue = xQueueCreate(10, sizeof(uint32_t));
while (1)
{
uint32_t data = HAL_GetTick();
xQueueSend(g_global_queue, &data, 0);
vTaskDelay(pdMS_TO_TICKS(1000));
}
/* 不需要删除,因为队列是全局的 */
}
/* 或在任务结束时清理 */
void vTaskWithCleanup(void *pvParameters)
{
QueueHandle_t local_queue = xQueueCreate(10, sizeof(uint32_t));
if (local_queue != NULL)
{
while (1)
{
/* 使用队列 */
if (should_exit())
{
break;
}
}
/* 任务结束前清理 */
vQueueDelete(local_queue);
}
vTaskDelete(NULL); /* 删除自己 */
}信号量泄漏
/* 信号量泄漏示例 */
void vSemaphoreLeakTask(void *pvParameters)
{
while (1)
{
SemaphoreHandle_t sem = xSemaphoreCreateBinary();
/* 使用信号量 */
xSemaphoreTake(sem, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(1000));
/* 忘记删除 */
/* vSemaphoreDelete(sem); */
}
}
/* 正确做法:使用全局信号量 */
SemaphoreHandle_t g_button_sem;
void vButtonTaskCorrect(void *pvParameters)
{
/* 初始化时创建 */
g_button_sem = xSemaphoreCreateBinary();
while (1)
{
if (xSemaphoreTake(g_button_sem, portMAX_DELAY) == pdTRUE)
{
/* 处理按钮事件 */
}
}
}
/* 清理函数 */
void vCleanup(void)
{
if (g_button_sem != NULL)
{
vSemaphoreDelete(g_button_sem);
g_button_sem = NULL;
}
}资源管理最佳实践
/* 1. 资源在初始化时创建 */
typedef struct {
QueueHandle_t queue;
SemaphoreHandle_t mutex;
TaskHandle_t task;
} AppResources_t;
AppResources_t g_app_resources;
void vAppInit(void)
{
/* 创建所有资源 */
g_app_resources.queue = xQueueCreate(10, sizeof(uint32_t));
g_app_resources.mutex = xSemaphoreCreateMutex();
xTaskCreate(vTask, "App", 256, NULL, 2, &g_app_resources.task);
}
/* 2. 资源在清理时释放 */
void vAppCleanup(void)
{
if (g_app_resources.task != NULL)
{
vTaskDelete(g_app_resources.task);
g_app_resources.task = NULL;
}
if (g_app_resources.mutex != NULL)
{
vSemaphoreDelete(g_app_resources.mutex);
g_app_resources.mutex = NULL;
}
if (g_app_resources.queue != NULL)
{
vQueueDelete(g_app_resources.queue);
g_app_resources.queue = NULL;
}
}使用前将 README.md 和 USER_GUIDE.md 从本文件夹移出。 详细使用方法请看 USER_GUIDE.md。
调试工具配置
本文档介绍 SEGGER SystemView、Percep TRACEalyzer、ITM/SWO 等调试工具的配置方法。
---
SEGGER SystemView(Keil/IAR 环境)
作用
- 实时系统分析
- 任务切换可视化
- 中断耗时统计
- CPU 使用率分析
- 事件追踪记录
配置步骤
1. 添加 SystemView 库
- 下载 SEGGER SystemView 固件库
- 添加
SEGGER_SYSVIEW.c到工程 - 添加
SEGGER_SYSVIEW_FreeRTOS.c(FreeRTOS 接口)
2. 初始化 SystemView
#include "SEGGER_SYSVIEW.h"
void vSystemView_Init(void)
{
/* 初始化 J-Link(ST-Link 不支持 SystemView) */
SEGGER_SYSVIEW_Init(SYSTEM_VIEW_TIMESTAMP_FREQ,
CPU_CORE_CLOCK,
&SYSVIEW_X_OS_Config,
&SEGGER_SYSVIEW_Conf);
/* 开始录制 */
SEGGER_SYSVIEW_Start();
}3. 配置 ITM/SWO
/* ITM 端口使能 */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
ITM->TCR |= ITM_TCR_ITMENA_Msk;
ITM->TPR |= ITM_TPR_PRIVMASK_Msk;
/* SWO 引脚配置(根据芯片手册) */
GPIOB->MODER &= ~GPIO_MODER_MODE3_Msk;
GPIOB->MODER |= GPIO_MODER_MODE3_0; /* AF */
GPIOB->AFR[0] &= ~GPIO_AFRH_AFRH3_Msk;
GPIOB->AFR[0] |= 0x0 << 12; /* AF0 (SWO) */4. 任务追踪
#include "SEGGER_SYSVIEW.h"
void vTaskExample(void *pvParameters)
{
SEGGER_SYSVIEW_Print("任务启动");
while (1)
{
SEGGER_SYSVIEW_RecordEnterTask();
/* 任务代码 */
SEGGER_SYSVIEW_RecordExitTask();
vTaskDelay(pdMS_TO_TICKS(100));
}
}---
Percep TRACEalyzer(FreeRTOS 环境)
作用
- trace 事件录制和分析
- 时序图可视化
- 任务资源使用统计
- 性能瓶颈定位
- 内存使用追踪
配置步骤
1. 启用 trace 功能
在 FreeRTOSConfig.h 中:
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
#define configRECORD_STACK_HIGH_ADDRESS 12. 添加 trcRecorder.c
- 从 FreeRTOS+Trace 或 Percep TRACEalyzer 获取
trcRecorder.c - 添加到工程
- 配置
trcConfig.h:
#define TRC_CFG_TRACE_CONTROLLER_SUPPORTS_64_BIT_EVENTS 0
#define TRC_CFG_HARDWARE_PORT Cortex-M
#define TRC_CFG_FREERTOS_VERSION FREERTOS_VERSION
#define TRC_CFG_RECORDER_MODE TRC_RECORDER_MODE_STREAMING3. 初始化 recorder
#include "trcRecorder.h"
void vTrace_Init(void)
{
vTraceEnable(TRC_START); /* 开始录制 */
}
void vTrace_Stop(void)
{
vTraceDisable(); /* 停止录制 */
}4. 导出 trace 数据
void vExportTrace(void)
{
traceHandle trace_file;
/* 打开文件 */
trace_file = xTraceOpenWrite("w", "trace.ptd");
if (trace_file != TRC_NULL)
{
/* 写入数据 */
xTraceWrite(trace_file, NULL, 0);
xTraceClose(trace_file);
}
}5. 使用 TRACEalyzer 分析
1. 通过 J-Link/ST-Link 导出 .ptd 文件 2. 用 Percep TRACEalyzer 打开 3. 查看时序图、任务统计、资源使用
---
ITM/SWO 配置步骤
ST-Link 配置
/* 使能 SWO 输出(ST-Link) */
volatile uint32_t *DBGMCU_CR = (volatile uint32_t *)0xE0042004;
*DBGMCU_CR |= 0x27; /* TRACE_IOEN | TRACE_MODE_0 | TRACE_MODE_1 */
/* 配置 TPIU(Trace Port Interface Unit) */
TPI->ACPR = 0; /* 1:1 分频 */
TPI->SPPR = 2; /* SWO NRZ 编码 */
TPI->FFCR = 0; /* 禁用 FIFO */
/* 使能 ITM 端口 0 */
ITM->TCR = 0x10009; /* ITMENA | SyncFieldsEnable */
ITM->TER = 1; /* 使能端口 0 */J-Link 配置
/* J-Link SWO 初始化(Segger 提供) */
void SEGGER_SWO_Init(void)
{
/* 配置 SWO 引脚 */
GPIOA->MODER &= ~(GPIO_MODER_MODE9_Msk);
GPIOA->MODER |= GPIO_MODER_MODE9_1; /* AF */
GPIOA->AFR[1] &= ~GPIO_AFRH_AFRH1_Msk;
GPIOA->AFR[1] |= 0x0 << 4; /* AF0 */
/* 初始化 SWO 波特率 */
SEGGER_SYSVIEW_SetSWOBaud(1000000);
}---
FreeRTOS 统计功能
任务状态统计
#include "task.h"
void vTaskStatsPrint(void)
{
TaskStatus_t *pxTaskStatusArray;
UBaseType_t uxArraySize;
uint32_t ulTotalRunTime;
char buffer[512];
/* 获取任务数 */
uxArraySize = uxTaskGetNumberOfTasks();
/* 分配内存 */
pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
if (pxTaskStatusArray == NULL)
{
printf("内存分配失败\r\n");
return;
}
/* 获取统计 */
uxTaskGetSystemState(pxTaskStatusArray, uxArraySize,
&ulTotalRunTime);
/* 打印统计 */
snprintf(buffer, sizeof(buffer), "\r\n任务统计:\r\n");
printf(buffer);
for (UBaseType_t i = 0; i < uxArraySize; i++)
{
TaskStatus_t *pxTask = &pxTaskStatusArray[i];
/* 计算 CPU 使用率 */
uint32_t ulStatsAsPercentage =
(pxTask->ulRunTimeCounter * 100UL) / ulTotalRunTime;
snprintf(buffer, sizeof(buffer),
"%-16s 优先级: %2lu 状态: %c CPU: %2lu%% 堆栈: %lu\r\n",
pxTask->pcTaskName,
(unsigned long)pxTask->uxCurrentPriority,
(pxTask->eCurrentState == eRunning) ? 'R' :
(pxTask->eCurrentState == eReady) ? 'r' :
(pxTask->eCurrentState == eBlocked) ? 'B' :
(pxTask->eCurrentState == eSuspended) ? 'S' : '?',
(unsigned long)ulStatsAsPercentage,
(unsigned long)pxTask->usStackHighWaterMark);
printf(buffer);
}
/* 释放内存 */
vPortFree(pxTaskStatusArray);
}运行时统计
void vTaskRunTimeStats(void)
{
TaskStatus_t *pxTaskStatusArray;
UBaseType_t uxArraySize;
uint32_t ulTotalRunTime;
char buffer[256];
uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
if (pxTaskStatusArray != NULL)
{
uxTaskGetSystemState(pxTaskStatusArray, uxArraySize,
&ulTotalRunTime);
printf("\r\n运行时统计:\r\n");
printf("名称 运行时间(%) 周期数\r\n");
printf("----------------------------------------\r\n");
for (UBaseType_t i = 0; i < uxArraySize; i++)
{
TaskStatus_t *pxTask = &pxTaskStatusArray[i];
uint32_t ulStatsAsPercentage =
(pxTask->ulRunTimeCounter * 100UL) / ulTotalRunTime;
snprintf(buffer, sizeof(buffer),
"%-16s %2lu.%02lu%% %lu\r\n",
pxTask->pcTaskName,
(unsigned long)ulStatsAsPercentage,
(unsigned long)(ulStatsAsPercentage * 100) % 100,
(unsigned long)pxTask->ulRunTimeCounter);
printf(buffer);
}
vPortFree(pxTaskStatusArray);
}
}堆栈溢出检测
/* FreeRTOSConfig.h */
#define configCHECK_FOR_STACK_OVERFLOW 2
void vApplicationStackOverflowHook(TaskHandle_t xTask,
char *pcTaskName)
{
printf("堆栈溢出: %s\r\n", pcTaskName);
while (1); /* 进入死循环便于调试 */
}FreeRTOS API 参考
本文档包含 FreeRTOS 原生 API 和 CMSIS-RTOS v2 API 的完整参考。
---
1.1 原生 FreeRTOS API(v10+)
任务管理
/* 动态任务创建 */
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode, // 任务函数指针
const char * const pcName, // 任务名称
const uint16_t usStackDepth, // 堆栈深度(字为单位)
void * const pvParameters, // 传递给任务的参数
UBaseType_t uxPriority, // 任务优先级
TaskHandle_t * const pxCreatedTask // 任务句柄(输出)
);
/* 静态任务创建(自行分配内存) */
TaskHandle_t xTaskCreateStatic(
TaskFunction_t pxTaskCode,
const char * const pcName,
uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const pxStackBuffer,
StaticTask_t * const pxTaskBuffer
);
/* 任务删除 */
void vTaskDelete(TaskHandle_t xTask);
/* 任务延时 */
void vTaskDelay(const TickType_t xTicksToDelay); // 相对延时
void vTaskDelayUntil(TickType_t *pxPreviousWakeTime, // 绝对延时
const TickType_t xTimeIncrement);
/* 任务优先级 */
void vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority);
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask);
/* 堆栈溢出检测 */
UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask);队列(Queue)
/* 创建队列 */
QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, // 队列长度
UBaseType_t uxItemSize); // 单个元素大小
/* 发送数据(任务中) */
BaseType_t xQueueSend(QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait);
/* 接收数据(任务中) */
BaseType_t xQueueReceive(QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait);
/* ISR 中发送(带优先级继承) */
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken);
/* ISR 中接收 */
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,
void *pvBuffer,
BaseType_t *pxHigherPriorityTaskWoken);信号量(Semaphore)
/* 创建二值信号量 */
SemaphoreHandle_t xSemaphoreCreateBinary(void);
/* 创建计数信号量 */
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount);
/* 创建互斥锁(带优先级继承) */
SemaphoreHandle_t xSemaphoreCreateMutex(void);
/* 创建递归互斥锁 */
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);
/* 获取信号量 */
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait);
/* 释放信号量 */
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
/* ISR 中释放 */
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken);事件组(Event Groups)
/* 创建事件组 */
EventGroupHandle_t xEventGroupCreate(void);
/* 设置事件位 */
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet);
/* 清除事件位 */
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear);
/* 等待事件位(AND/OR 逻辑) */
EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait);
/* ISR 中设置事件位 */
BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t *pxHigherPriorityTaskWoken);任务通知(Task Notifications)
/* 发送通知(直接唤醒任务) */
BaseType_t xTaskNotify(TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction);
/* 发送通知(ISR 版本) */
BaseType_t xTaskNotifyFromISR(TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction,
BaseType_t *pxHigherPriorityTaskWoken);
/* 接收通知 */
BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait);---
1.2 CMSIS-RTOS v2 API 映射
线程(Task)
/* 创建线程 */
osThreadId_t osThreadNew(osThreadFunc_t func, void *arg,
const osThreadAttr_t *attr);
/* 线程延时 */
osStatus_t osDelay(uint32_t ticks);
osStatus_t osDelayUntil(uint32_t *ticks, uint32_t period);
/* 线程终止 */
osStatus_t osThreadTerminate(osThreadId_t thread_id);
/* 线程退出 */
void osThreadExit(void);队列(Queue)
/* 创建队列 */
osMessageQueueId_t osMessageQueueNew(uint32_t msg_count,
uint32_t msg_size,
const osMessageQueueAttr_t *attr);
/* 发送消息 */
osStatus_t osMessageQueuePut(osMessageQueueId_t mq_id,
const void *msg_ptr,
uint8_t msg_prio,
uint32_t timeout);
/* 接收消息 */
osStatus_t osMessageQueueGet(osMessageQueueId_t mq_id,
void *msg_ptr,
uint8_t *msg_prio,
uint32_t timeout);信号量(Semaphore)
/* 创建信号量 */
osSemaphoreId_t osSemaphoreNew(uint32_t max_count,
uint32_t initial_count,
const osSemaphoreAttr_t *attr);
/* 获取信号量 */
osStatus_t osSemaphoreAcquire(osSemaphoreId_t semaphore_id,
uint32_t timeout);
/* 释放信号量 */
osStatus_t osSemaphoreRelease(osSemaphoreId_t semaphore_id);互斥锁(Mutex)
/* 创建互斥锁 */
osMutexId_t osMutexNew(const osMutexAttr_t *attr);
/* 获取互斥锁 */
osStatus_t osMutexAcquire(osMutexId_t mutex_id, uint32_t timeout);
/* 释放互斥锁 */
osStatus_t osMutexRelease(osMutexId_t mutex_id);事件(Event)
/* 创建事件 */
osEventFlagsId_t osEventFlagsNew(const osEventFlagsAttr_t *attr);
/* 设置事件 */
uint32_t osEventFlagsSet(osEventFlagsId_t ef_id, uint32_t flags);
/* 清除事件 */
uint32_t osEventFlagsClear(osEventFlagsId_t ef_id, uint32_t flags);
/* 等待事件 */
uint32_t osEventFlagsWait(osEventFlagsId_t ef_id,
uint32_t flags,
uint32_t options,
uint32_t timeout);---
内存优化
静态分配 vs 动态分配
/* 静态任务控制块和堆栈 */
StaticTask_t xTask1_TCB;
StackType_t xTask1_Stack[128]; /* 128 * 4 = 512 字节 */
/* 静态队列控制块 */
typedef struct {
uint8_t buffer[10][32]; /* 10 个元素,每个 32 字节 */
StaticQueue_t queue;
} StaticQueue_t;
void vCreateStaticObjects(void)
{
/* 创建静态任务 */
xTaskCreateStatic(vTaskFunction, "StaticTask1",
128, NULL, osPriorityNormal,
xTask1_Stack, &xTask1_TCB);
/* 创建静态队列 */
QueueHandle_t queue = xQueueCreateStatic(10, 32,
g_queue_buffer,
&g_queue_cb);
}堆栈估算方法
void vTaskMonitorStack(TaskHandle_t xTask)
{
UBaseType_t high_water_mark;
if (xTask == NULL)
{
high_water_mark = uxTaskGetStackHighWaterMark(NULL);
}
else
{
high_water_mark = uxTaskGetStackHighWaterMark(xTask);
}
printf("堆栈剩余: %lu 字 (%lu 字节)\r\n",
high_water_mark, high_water_mark * 4);
if (high_water_mark < 20)
{
printf("警告: 堆栈即将耗尽!\r\n");
}
}堆栈溢出配置
/* FreeRTOSConfig.h */
#define configCHECK_FOR_STACK_OVERFLOW 2 /* 方法 2 */
void vApplicationStackOverflowHook(TaskHandle_t xTask,
char *pcTaskName)
{
printf("堆栈溢出: %s\r\n", pcTaskName);
while (1);
}heap 碎片避免
#define MEMPOOL_ITEM_SIZE 64
#define MEMPOOL_ITEMS 20
typedef struct {
uint8_t data[MEMPOOL_ITEM_SIZE];
} MemPoolItem_t;
typedef struct {
MemPoolItem_t items[MEMPOOL_ITEMS];
uint8_t used[MEMPOOL_ITEMS];
} MemPool_t;
void *pvMemPoolAlloc(MemPool_t *pool)
{
for (int i = 0; i < MEMPOOL_ITEMS; i++)
{
if (pool->used[i] == 0)
{
pool->used[i] = 1;
memset(pool->items[i].data, 0, MEMPOOL_ITEM_SIZE);
return pool->items[i].data;
}
}
return NULL;
}
void vMemPoolFree(MemPool_t *pool, void *p)
{
for (int i = 0; i < MEMPOOL_ITEMS; i++)
{
if (pool->items[i].data == p)
{
pool->used[i] = 0;
break;
}
}
}HAL 外设驱动集成
本文档介绍 UART、ADC、I2C、TIM 等外设与 FreeRTOS 的集成方法。
---
UART DMA + 队列(IDLE 中断)
头文件(uart_rx.h)
#ifndef UART_RX_H
#define UART_RX_H
#include "main.h"
#include "FreeRTOS.h"
#include "queue.h"
#define UART_RX_BUFFER_SIZE 256
typedef struct {
uint8_t data[UART_RX_BUFFER_SIZE];
uint16_t length;
} UartRxMessage_t;
extern QueueHandle_t g_uart_rx_queue;
extern TaskHandle_t g_uart_task_handle;
void vUartRxTask(void *pvParameters);
void MX_UART1_Init(void);
void HAL_UART_IDLECallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
#endif /* UART_RX_H */实现文件(uart_rx.c)
#include "uart_rx.h"
#include "dma.h"
static uint8_t g_rx_buffer[UART_RX_BUFFER_SIZE];
static uint8_t g_rx_temp[UART_RX_BUFFER_SIZE];
static volatile uint16_t g_rx_length = 0;
static volatile uint8_t g_rx_flag = 0;
QueueHandle_t g_uart_rx_queue;
TaskHandle_t g_uart_task_handle;
void MX_UART1_Init(void)
{
/* 使能 IDLE 中断和 DMA */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1, g_rx_buffer, UART_RX_BUFFER_SIZE);
/* 创建队列 */
g_uart_rx_queue = xQueueCreate(10, sizeof(UartRxMessage_t));
}
void HAL_UART_IDLECallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
/* 计算接收长度 */
uint16_t dma_counter = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
g_rx_length = UART_RX_BUFFER_SIZE - dma_counter;
g_rx_flag = 1;
/* 停止 DMA */
HAL_UART_DMAStop(&huart1);
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
g_rx_length = UART_RX_BUFFER_SIZE;
g_rx_flag = 1;
}
}
void vUartRxTask(void *pvParameters)
{
UartRxMessage_t rx_msg;
while (1)
{
/* 等待数据就绪 */
while (g_rx_flag == 0)
{
vTaskDelay(pdMS_TO_TICKS(10));
}
/* 复制数据 */
memcpy(rx_msg.data, g_rx_buffer, g_rx_length);
rx_msg.length = g_rx_length;
/* 发送到队列 */
if (xQueueSend(g_uart_rx_queue, &rx_msg, 0) == pdTRUE)
{
/* 发送成功,重新启动 DMA */
g_rx_flag = 0;
HAL_UART_Receive_DMA(&huart1, g_rx_buffer,
UART_RX_BUFFER_SIZE);
}
else
{
/* 队列满,丢弃数据 */
g_rx_flag = 0;
HAL_UART_Receive_DMA(&huart1, g_rx_buffer,
UART_RX_BUFFER_SIZE);
}
}
}---
ADC DMA + 任务通知
头文件(adc_process.h)
#ifndef ADC_PROCESS_H
#define ADC_PROCESS_H
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#define ADC_CHANNEL_NUM 3
#define ADC_BUFFER_SIZE ADC_CHANNEL_NUM
extern TaskHandle_t g_adc_task_handle;
extern uint32_t g_adc_values[ADC_CHANNEL_NUM];
void vAdcProcessTask(void *pvParameters);
void MX_ADC1_Init(void);
#endif /* ADC_PROCESS_H */实现文件(adc_process.c)
#include "adc_process.h"
#include "dma.h"
static uint32_t g_adc_buffer[ADC_BUFFER_SIZE];
uint32_t g_adc_values[ADC_CHANNEL_NUM];
TaskHandle_t g_adc_task_handle;
void MX_ADC1_Init(void)
{
/* 配置 ADC DMA */
HAL_ADC_Start_DMA(&hadc1, g_adc_buffer, ADC_BUFFER_SIZE);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC1)
{
/* 复制数据 */
for (int i = 0; i < ADC_CHANNEL_NUM; i++)
{
g_adc_values[i] = g_adc_buffer[i];
}
/* 发送任务通知 */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(g_adc_task_handle,
&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
void vAdcProcessTask(void *pvParameters)
{
uint32_t notification_value;
uint32_t adc_raw;
while (1)
{
/* 等待 ADC 转换完成通知 */
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
/* 处理 ADC 数据 */
for (int i = 0; i < ADC_CHANNEL_NUM; i++)
{
adc_raw = g_adc_values[i];
/* 转换为电压值(假设 3.3V 参考) */
float voltage = (float)adc_raw * 3.3f / 4095.0f;
/* 发送数据到其他任务或输出 */
printf("CH%d: %.3f V\r\n", i, voltage);
}
}
}---
I2C 传感器读取
头文件(i2c_sensor.h)
#ifndef I2C_SENSOR_H
#define I2C_SENSOR_H
#include "main.h"
#include "FreeRTOS.h"
#include "queue.h"
#define I2C_SENSOR_ADDR 0x68 /* MPU6050 地址 */
typedef struct {
int16_t acc_x, acc_y, acc_z;
int16_t gyro_x, gyro_y, gyro_z;
} Mpu6050_Data_t;
extern QueueHandle_t g_sensor_queue;
void vI2cSensorTask(void *pvParameters);
HAL_StatusTypeDef MPU6050_Init(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef MPU6050_ReadAll(I2C_HandleTypeDef *hi2c,
Mpu6050_Data_t *data);
#endif /* I2C_SENSOR_H */实现文件(i2c_sensor.c)
#include "i2c_sensor.h"
#include "string.h"
static const uint8_t MPU6050_WHO_AM_I = 0x75;
static const uint8_t MPU6050_PWR_MGMT_1 = 0x6B;
static uint8_t g_i2c_tx_buffer[2];
static uint8_t g_i2c_rx_buffer[14];
QueueHandle_t g_sensor_queue;
HAL_StatusTypeDef MPU6050_Init(I2C_HandleTypeDef *hi2c)
{
/* 唤醒传感器 */
g_i2c_tx_buffer[0] = MPU6050_PWR_MGMT_1;
g_i2c_tx_buffer[1] = 0x00;
return HAL_I2C_Master_Transmit(hi2c, I2C_SENSOR_ADDR << 1,
g_i2c_tx_buffer, 2, 100);
}
HAL_StatusTypeDef MPU6050_ReadAll(I2C_HandleTypeDef *hi2c,
Mpu6050_Data_t *data)
{
HAL_StatusTypeDef status;
/* 发送寄存器地址 */
g_i2c_tx_buffer[0] = 0x3B; /* ACCEL_XOUT_H */
status = HAL_I2C_Master_Transmit(hi2c, I2C_SENSOR_ADDR << 1,
g_i2c_tx_buffer, 1, 100);
if (status != HAL_OK) return status;
/* 读取 14 字节数据 */
status = HAL_I2C_Master_Receive(hi2c, I2C_SENSOR_ADDR << 1,
g_i2c_rx_buffer, 14, 100);
if (status != HAL_OK) return status;
/* 解析数据 */
data->acc_x = (g_i2c_rx_buffer[0] << 8) | g_i2c_rx_buffer[1];
data->acc_y = (g_i2c_rx_buffer[2] << 8) | g_i2c_rx_buffer[3];
data->acc_z = (g_i2c_rx_buffer[4] << 8) | g_i2c_rx_buffer[5];
data->gyro_x = (g_i2c_rx_buffer[8] << 8) | g_i2c_rx_buffer[9];
data->gyro_y = (g_i2c_rx_buffer[10] << 8) | g_i2c_rx_buffer[11];
data->gyro_z = (g_i2c_rx_buffer[12] << 8) | g_i2c_rx_buffer[13];
return HAL_OK;
}
void vI2cSensorTask(void *pvParameters)
{
Mpu6050_Data_t sensor_data;
I2C_HandleTypeDef *hi2c1 = (I2C_HandleTypeDef *)pvParameters;
/* 初始化传感器 */
while (MPU6050_Init(hi2c1) != HAL_OK)
{
printf("MPU6050 初始化失败,重试中...\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
printf("MPU6050 初始化成功\r\n");
while (1)
{
/* 读取传感器数据 */
if (MPU6050_ReadAll(hi2c1, &sensor_data) == HAL_OK)
{
/* 发送到队列 */
xQueueSend(g_sensor_queue, &sensor_data, 0);
}
else
{
printf("I2C 读取失败\r\n");
}
vTaskDelay(pdMS_TO_TICKS(20)); /* 50Hz 采样 */
}
}---
TIM 定时器 + 任务触发
头文件(tim_trigger.h)
#ifndef TIM_TRIGGER_H
#define TIM_TRIGGER_H
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
extern TaskHandle_t g_tim_task_handle;
void MX_TIM6_Init(void);
void vTimTriggerTask(void *pvParameters);
#endif /* TIM_TRIGGER_H */实现文件(tim_trigger.c)
#include "tim_trigger.h"
#include "dma.h"
TaskHandle_t g_tim_task_handle;
void MX_TIM6_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* 配置 TIM6 作为系统节拍替代 */
htim6.Instance = TIM6;
htim6.Init.Prescaler = 83; /* 84MHz / 84 = 1MHz */
htim6.Init.Period = 1000 - 1; /* 1ms 中断 */
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_MspInit(&htim6);
HAL_TIM_Base_Init(&htim6);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig);
/* 使能更新中断 */
HAL_TIM_Base_Start_IT(&htim6);
}
void TIM6_DAC_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim6);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
/* 发送任务通知(替代 systick) */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(g_tim_task_handle,
&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
void vTimTriggerTask(void *pvParameters)
{
uint32_t tick_count = 0;
while (1)
{
/* 等待定时器通知 */
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
/* 执行周期任务 */
tick_count++;
if ((tick_count % 1000) == 0)
{
printf("运行时间: %lu ms\r\n", tick_count);
}
}
}标准库集成
本文档介绍 stdio.h、string.h、stdlib.h 与 FreeRTOS 的集成使用方法。
---
stdio.h(printf 重定向)
ITM_SendChar(SWO 输出)
#include <stdio.h>
/* 重定向 printf 到 ITM/SWO */
int __attribute__((weak)) _write(int file, char *ptr, int len)
{
(void)file;
int i;
for (i = 0; i < len; i++)
{
ITM_SendChar(ptr[i]); /* 通过 SWO 输出 */
}
return len;
}
/* 使用示例 */
void vTaskITM(void *pvParameters)
{
while (1)
{
printf("堆栈剩余: %lu 字节\r\n",
uxTaskGetStackHighWaterMark(NULL));
vTaskDelay(pdMS_TO_TICKS(1000));
}
}UART 重定向
#include <stdio.h>
#include "usart.h"
int __attribute__((weak)) _write(int file, char *ptr, int len)
{
(void)file;
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
return len;
}snprintf 安全格式化
#include <stdio.h>
#include <string.h>
void vTaskPrintStatus(void *pvParameters)
{
char buffer[128];
TaskStatus_t *pxTaskStatusArray;
uint32_t ulTotalRunTime, ulStatsAsPercentage;
/* 获取任务状态 */
UBaseType_t uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
if (pxTaskStatusArray != NULL)
{
uxTaskGetSystemState(pxTaskStatusArray, uxArraySize,
&ulTotalRunTime);
/* 安全格式化(防止缓冲区溢出) */
snprintf(buffer, sizeof(buffer),
"任务数: %lu\r\n", uxArraySize);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer,
strlen(buffer), HAL_MAX_DELAY);
vPortFree(pxTaskStatusArray);
}
}---
string.h(DMA 内存操作)
memcpy 在 DMA 中的使用
#include <string.h>
#include "dma.h"
#define RX_BUFFER_SIZE 256
uint8_t g_rx_buffer[RX_BUFFER_SIZE];
uint8_t g_rx_temp[RX_BUFFER_SIZE];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
/* 将接收数据复制到处理缓冲区 */
memcpy(g_rx_temp, g_rx_buffer, RX_BUFFER_SIZE);
/* 通知处理任务 */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(g_uart_queue, g_rx_temp,
&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
/* 重新启动 DMA 接收 */
HAL_UART_Receive_IT(&huart1, g_rx_buffer, RX_BUFFER_SIZE);
}
}memset 初始化控制块
#include <string.h>
StaticTask_t xTaskBuffer;
StackType_t xStack[128];
void vTaskCreateStaticExample(void)
{
/* 清零控制块(推荐) */
memset(&xTaskBuffer, 0, sizeof(StaticTask_t));
/* 创建静态任务 */
xTaskCreateStatic(vTaskFunction, "StaticTask",
128, NULL, osPriorityNormal,
xStack, &xTaskBuffer);
}---
stdlib.h(动态内存注意事项)
避免在任务中频繁 malloc/free
#include <stdlib.h>
#include "main.h"
/* 不推荐:每次处理都分配内存 */
void vTaskBadExample(void *pvParameters)
{
while (1)
{
char *p_data = pvPortMalloc(256); /* 频繁分配 */
if (p_data != NULL)
{
/* 处理数据 */
process_data(p_data);
vPortFree(p_data); /* 释放 */
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
/* 推荐:使用静态缓冲区池 */
#define BUFFER_POOL_SIZE 10
#define BUFFER_SIZE 256
static uint8_t g_buffer_pool[BUFFER_POOL_SIZE][BUFFER_SIZE];
static uint8_t g_buffer_used[BUFFER_POOL_SIZE];
void *pvAllocateBuffer(void)
{
for (int i = 0; i < BUFFER_POOL_SIZE; i++)
{
if (g_buffer_used[i] == 0)
{
g_buffer_used[i] = 1;
memset(g_buffer_pool[i], 0, BUFFER_SIZE);
return g_buffer_pool[i];
}
}
return NULL; /* 池已满 */
}
void vFreeBuffer(void *p_buffer)
{
for (int i = 0; i < BUFFER_POOL_SIZE; i++)
{
if (g_buffer_pool[i] == p_buffer)
{
g_buffer_used[i] = 0;
break;
}
}
}pvPortMalloc vs malloc
/* 使用 FreeRTOS 内存分配函数 */
void *pvPortMalloc(size_t xSize); /* 分配内存 */
void vPortFree(void *pv); /* 释放内存 */
/* 与标准库的区别:
* - pvPortMalloc 使用 FreeRTOS 的 heap 内存管理
* - 可以与 xTaskCreate 等函数配合使用
* - 支持静态分配策略
*/heap 内存监控
void vHeapStatsPrint(void)
{
HeapStats_t heap_stats;
xPortGetHeapStats(&heap_stats);
printf("\r\nHeap 统计:\r\n");
printf(" 可分配最小: %lu 字节\r\n", heap_stats.xMinimumEverFreeBytesRemaining);
printf(" 当前可用: %lu 字节\r\n", heap_stats.xAvailableHeapSpaceInBytes);
printf(" 分配块数: %lu\r\n", heap_stats.xNumberOfAllocations);
printf(" 释放块数: %lu\r\n", heap_stats.xNumberOfFrees);
printf(" 最大分配: %lu 字节\r\n", heap_stats.xSizeOfLargestFreeBlockInBytes);
}#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FreeRTOSConfig.h 配置文件验证工具
功能:
- 检查 FreeRTOSConfig.h 关键配置宏定义
- 验证配置值的有效性
- 输出 JSON 格式结果(便于 CI 集成)
- 支持命令行参数和标准输入
使用方法:
python freertos_config_check.py FreeRTOSConfig.h
python freertos_config_check.py FreeRTOSConfig.h --json
cat FreeRTOSConfig.h | python freertos_config_check.py --stdin
作者:STM32 + FreeRTOS Agent Skill
"""
import re
import sys
import json
import argparse
from typing import Dict, List, Optional, Tuple, Any, Sequence
from dataclasses import dataclass, asdict
from enum import Enum
class CheckStatus(Enum):
"""检查结果状态"""
PASS = "pass"
FAIL = "fail"
WARNING = "warning"
SKIP = "skip"
@dataclass
class ConfigCheck:
"""单个配置检查结果"""
name: str # 宏名称
expected: str # 期望值
actual: Optional[str] # 实际值
status: str # pass/fail/warning/skip
message: str # 说明信息
@dataclass
class CheckResult:
"""整体检查结果"""
file: str
checks: List[Dict[str, Any]]
summary: Dict[str, int]
class FreeRTOSConfigChecker:
"""FreeRTOSConfig.h 配置文件检查器"""
# 必需配置项及期望值
REQUIRED_CHECKS = [
("configUSE_PREEMPTION", "1", "启用抢占式调度"),
("configUSE_PORT_OPTIMISED_TASK_SELECTION", "1", "使用硬件优先级选择"),
("configTICK_RATE_HZ", None, "系统节拍频率(通常为 1000)"),
]
# 推荐配置项及期望值
RECOMMENDED_CHECKS = [
("configUSE_IDLE_HOOK", "1", "启用空闲任务钩子(低功耗用)"),
("configUSE_TICK_HOOK", "0", "禁用系统节拍钩子(除非必要)"),
("configCHECK_FOR_STACK_OVERFLOW", "2", "启用堆栈溢出检测"),
("configUSE_TRACE_FACILITY", "1", "启用 trace 功能"),
("configUSE_STATS_FORMATTING_FUNCTIONS", "1", "启用统计格式化函数"),
("configGENERATE_RUN_TIME_STATS", "0", "运行时统计(调试时可开启)"),
("configUSE_CO_ROUTINES", "0", "禁用协程(推荐使用任务)"),
("configMAX_PRIORITIES", None, "最大任务优先级数(通常 32)"),
("configMINIMAL_STACK_SIZE", None, "最小堆栈大小(通常 128)"),
("configTOTAL_HEAP_SIZE", None, "动态分配堆大小(根据需求设置)"),
]
# 可选配置项及期望值
OPTIONAL_CHECKS = [
("configUSE_MUTEXES", "1", "启用互斥锁"),
("configUSE_RECURSIVE_MUTEXES", "1", "启用递归互斥锁"),
("configUSE_COUNTING_SEMAPHORES", "1", "启用计数信号量"),
("configUSE_TASK_NOTIFICATIONS", "1", "启用任务通知"),
("configUSE_TIMERS", "1", "启用软件定时器"),
("configTIMER_TASK_PRIORITY", None, "定时器任务优先级"),
("configTIMER_QUEUE_LENGTH", None, "定时器队列长度"),
("configTIMER_TASK_STACK_DEPTH", None, "定时器任务堆栈深度"),
("configUSE_TICKLESS_IDLE", "0", "禁用 Tickless 低功耗(需要额外配置)"),
("configCPU_CLOCK_HZ", None, "CPU 时钟频率"),
("configSYSTICK_CLOCK_HZ", None, "SysTick 时钟频率"),
("configKERNEL_INTERRUPT_PRIORITY", None, "内核中断优先级"),
("configMAX_SYSCALL_INTERRUPT_PRIORITY", None, "最大系统调用中断优先级"),
("configASSERT(x)", None, "断言定义"),
]
# 禁用配置项
DISABLED_CHECKS: List[Tuple[str, Optional[str], str]] = [
("configUSE_16_BIT_TICKS", "0", "禁用 16 位 tick(必须为 0)"),
]
def __init__(self, verbose: bool = False):
self.verbose = verbose
self.checks: List[ConfigCheck] = []
def parse_config_file(self, content: str) -> Dict[str, str]:
"""
解析 FreeRTOSConfig.h 文件内容
Args:
content: 文件内容字符串
Returns:
宏定义字典 {宏名: 值}
"""
defines = {}
# 匹配 #define 宏定义
pattern = r'^\s*#\s*define\s+(\w+)(?:\s+([^\/\n]+))?'
for match in re.finditer(pattern, content, re.MULTILINE):
name = match.group(1)
value = match.group(2)
if value:
# 清理值
value = value.strip()
# 移除行尾注释
comment_idx = value.find('//')
if comment_idx >= 0:
value = value[:comment_idx].strip()
defines[name] = value
return defines
def check_value(self, name: str, expected: Optional[str],
actual: Optional[str]) -> Tuple[CheckStatus, str]:
"""
检查配置值
Args:
name: 宏名称
expected: 期望值(None 表示只需存在)
actual: 实际值
Returns:
(状态, 说明信息)
"""
if actual is None:
return CheckStatus.FAIL, f"宏 {name} 未定义"
# 处理特殊期望值
if expected is None:
# 只需要存在
return CheckStatus.PASS, f"{name} = {actual}"
# 精确匹配
if actual == expected:
return CheckStatus.PASS, f"{name} = {actual}"
# 数值匹配(处理进制前缀)
try:
if actual.startswith(('0x', '0X')):
actual_int = int(actual, 16)
elif actual.startswith('0') and len(actual) > 1:
actual_int = int(actual, 8)
else:
actual_int = int(actual)
if expected.startswith(('0x', '0X')):
expected_int = int(expected, 16)
else:
expected_int = int(expected)
if actual_int == expected_int:
return CheckStatus.PASS, f"{name} = {actual}"
except ValueError:
pass
# 值不匹配
if expected in ('0', '1'):
return CheckStatus.FAIL, f"{name} 应为 {expected},实际为 {actual}"
else:
return CheckStatus.WARNING, f"{name} 推荐值为 {expected},实际为 {actual}"
def run_checks(self, defines: Dict[str, str],
check_list: Sequence[Tuple[str, Optional[str], str]],
category: str) -> None:
"""
运行一组检查
Args:
defines: 宏定义字典
check_list: 检查项列表
category: 检查类别(用于输出)
"""
for name, expected, description in check_list:
actual = defines.get(name)
status, message = self.check_value(name, expected, actual)
if status == CheckStatus.FAIL and actual is None:
message = f"{description}:{message}"
self.checks.append(ConfigCheck(
name=name,
expected=str(expected) if expected else "defined",
actual=actual,
status=status.value,
message=message
))
if self.verbose and status != CheckStatus.PASS:
print(f"[{category}] {name}: {message}")
def check_config(self, content: str, filename: str = "FreeRTOSConfig.h") -> CheckResult:
"""
执行完整的配置检查
Args:
content: 文件内容
filename: 文件名(用于输出)
Returns:
检查结果
"""
defines = self.parse_config_file(content)
self.checks = []
# 运行所有检查
self.run_checks(defines, self.REQUIRED_CHECKS, "必需")
self.run_checks(defines, self.RECOMMENDED_CHECKS, "推荐")
self.run_checks(defines, self.OPTIONAL_CHECKS, "可选")
self.run_checks(defines, self.DISABLED_CHECKS, "禁用")
# 计算摘要
summary = {
"passed": sum(1 for c in self.checks if c.status == "pass"),
"failed": sum(1 for c in self.checks if c.status == "fail"),
"warnings": sum(1 for c in self.checks if c.status == "warning"),
"skipped": sum(1 for c in self.checks if c.status == "skip"),
"total": len(self.checks)
}
# 构建结果
result = CheckResult(
file=filename,
checks=[asdict(c) for c in self.checks],
summary=summary
)
return result
def print_result(self, result: CheckResult) -> None:
"""打印人类可读的结果"""
print(f"\n{'='*60}")
print(f"FreeRTOSConfig.h 检查结果: {result.file}")
print(f"{'='*60}")
# 打印摘要
summary = result.summary
print(f"\n摘要: 通过={summary['passed']}, "
f"失败={summary['failed']}, "
f"警告={summary['warnings']}, "
f"总计={summary['total']}")
# 打印详细信息
print(f"\n详细结果:")
print("-" * 60)
# 先打印失败的
for check in result.checks:
if check["status"] == "fail":
self._print_check(check, "❌")
# 再打印警告的
for check in result.checks:
if check["status"] == "warning":
self._print_check(check, "⚠️")
# 最后打印通过的
passed_count = 0
for check in result.checks:
if check["status"] == "pass":
passed_count += 1
if passed_count <= 10: # 只显示前 10 个
self._print_check(check, "✅")
if passed_count > 10:
print(f"... 还有 {passed_count - 10} 个检查通过")
print("-" * 60)
# 打印建议
failed_checks = [c for c in result.checks if c["status"] == "fail"]
if failed_checks:
print(f"\n建议修复项:")
for check in failed_checks:
print(f" - {check['name']}: {check['message']}")
def _print_check(self, check: Dict, icon: str) -> None:
"""打印单个检查结果"""
print(f"{icon} {check['name']}")
print(f" 期望: {check['expected']}, 实际: {check['actual']}")
print(f" 说明: {check['message']}")
def main():
"""主函数"""
parser = argparse.ArgumentParser(
description="FreeRTOSConfig.h 配置文件验证工具",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
%(prog)s FreeRTOSConfig.h
%(prog)s FreeRTOSConfig.h --json
%(prog)s FreeRTOSConfig.h --verbose
cat FreeRTOSConfig.h | %(prog)s --stdin
"""
)
parser.add_argument("filename", nargs="?", help="配置文件路径")
parser.add_argument("--json", action="store_true",
help="输出 JSON 格式")
parser.add_argument("--stdin", action="store_true",
help="从标准输入读取")
parser.add_argument("--verbose", "-v", action="store_true",
help="详细输出")
parser.add_argument("--output", "-o", help="输出到文件")
args = parser.parse_args()
# 读取配置文件
content = ""
if args.stdin:
content = sys.stdin.read()
filename = "<stdin>"
elif args.filename:
try:
with open(args.filename, "r", encoding="utf-8") as f:
content = f.read()
filename = args.filename
except FileNotFoundError:
print(f"错误: 文件未找到: {args.filename}")
sys.exit(1)
except Exception as e:
print(f"错误: 读取文件失败: {e}")
sys.exit(1)
else:
parser.print_help()
sys.exit(1)
# 检查文件内容
if not content.strip():
print("错误: 配置文件为空")
sys.exit(1)
# 运行检查
checker = FreeRTOSConfigChecker(verbose=args.verbose)
result = checker.check_config(content, filename)
# 输出结果
if args.json:
# JSON 格式输出
output = json.dumps(asdict(result), indent=2, ensure_ascii=False)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output)
else:
print(output)
else:
# 人类可读格式
checker.print_result(result)
# 如果指定输出文件
if args.output:
output = json.dumps(asdict(result), indent=2, ensure_ascii=False)
with open(args.output, "w", encoding="utf-8") as f:
f.write(output)
print(f"\n结果已保存到: {args.output}")
# 返回退出码
if result.summary["failed"] > 0:
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
main()
Related skills
How it compares
Pick stm32-freertos-developer for STM32 HAL plus FreeRTOS firmware patterns; use bare-metal skills when RTOS overhead is unnecessary for simple GPIO blink projects.
FAQ
Which STM32 series does stm32-freertos-developer support?
stm32-freertos-developer supports ARM Cortex-M F0, F1, F3, F4, F7, H7, G0, L0, L4, and L5 series with FreeRTOS v10+ and CMSIS-RTOS v2 API, typically on STM32CubeMX-generated projects.
What FreeRTOS patterns does the skill include?
stm32-freertos-developer includes dynamic and static task creation, queues, semaphores, mutex priority inheritance, event groups, task notifications, UART DMA with IDLE interrupt, Tickless low-power mode, and SEGGER SystemView debugging guidance.
Is Stm32 Freertos Developer safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.