🎯 FreeRTOS API 实战应用总结手册
🎯 FreeRTOS API 实战应用总结手册
created by lwPigKing
最好先学习一下《操作系统》,这样理解起来会更简单;但是不学习《操作系统》也不影响对《FreeRTOS》的学习。
📚 一、任务管理 API
1.1 xTaskCreate() - 创建任务
🔍 现实比喻:招聘新员工
- 招聘需求:公司需要新的职能岗位
- 面试过程:
xTaskCreate()函数调用 - 员工信息:职责(函数)、任务名、工位大小(栈)、薪资等级(优先级)
- 员工编号:任务句柄(TaskHandle_t)
🎯 什么时候用:
- ✅ 系统启动时 → 公司开业,一次性招聘所有基础岗位员工
- ✅ 动态功能 → 临时项目需要,招聘项目组(如固件升级任务)
- ✅ 模块化设计 → 不同部门独立运作,互不影响
💡 典型场景:
/** System_Monitor_Task的任务配置 **/
#define System_Monitor_Task_STACK 128
#define System_Monitor_Task_PRIORITY 3
TaskHandle_t System_Monitor_Task_handle;
void System_Monitor_Task(void * pvParameters);
// 智能家居公司:招聘各部门员工
void Hire_Company_Staff(void) {
// 招聘CEO:系统监控任务(最高优先级)
xTaskCreate(
(TaskFunction_t)System_Monitor_Task, // 任务函数地址
(char *)"CEO", // 任务名称
(configSTACK_DEPTH_TYPE)System_Monitor_Task_STACK,// 任务栈大小,默认是128(最小4B)
(void *)NULL, // 传递给任务的参数
(UBaseType_t)System_Monitor_Task_PRIORITY,// 任务优先级
(TaskHandle_t *)&ceo_handle // 任务句柄地址
);
// 同上,简写,但是要做配置
// 招聘保安:传感器采集任务(中等优先级)
xTaskCreate(Sensor_Collect_Task, "保安", 256, NULL, 3, &guard_handle);
// 招聘保洁:数据清理任务(低优先级)
xTaskCreate(Data_Clean_Task, "保洁", 128, NULL, 1, &cleaner_handle);
// 招聘临时工:固件升级任务(完成后解雇)
xTaskCreate(Firmware_Update_Task, "临时工", 256, NULL, 2, &temp_worker);
}
void System_Monitor_Task(void * pvParameters) {
printf("System_Monitor_Task调度任务....");
}
💡 最常用的场景:
/**
freertos_start创建start_task,然后将以上的任务创建全部写在start_task
**/
#define START_TASK_STACK 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void * pvParameters);
/**
* @brief 启动FreeRTOS
*
*/
void freertos_start(void) {
// 1.创建一个启动任务
xTaskCreate(
(TaskFunction_t) start_task, // 任务函数地址
(char *) "start_task", // 任务名称
(configSTACK_DEPTH_TYPE) START_TASK_STACK, // 任务栈大小,默认是128(最小4B)
(void *) NULL, // 传递给任务的参数
(UBaseType_t) START_TASK_PRIORITY, // 任务优先级
(TaskHandle_t *) &start_task_handle // 任务句柄地址
);
// 2.启动调度器
vTaskStartScheduler();
}
void start_task(void * pvParameters) {
taskENTER_CRITICAL();
// 创建任务
xTaskCreate(
(TaskFunction_t) task1,
(char *) "task1",
(configSTACK_DEPTH_TYPE) TASK1_STACK,
(void *) NULL,
(UBaseType_t) TASK1_PRIORITY,
(TaskHandle_t *) &task1_handle
);
vTaskDelete(NULL); // 删除启动任务自己
taskEXIT_CRITICAL();
}
⚠️ 注意事项:
- 不要过度招聘:任务太多会导致调度开销增大
- 合理分配工位:栈空间不足会导致栈溢出(员工没地方放东西)
- 明确薪资等级:优先级不合理会导致某些任务"饿死"
1.2 vTaskDelete() - 删除任务
🔍 现实比喻:解雇员工
- 合同到期:临时任务完成使命
- 工作失职:任务出错需要重置
- 公司裁员:系统资源不足
🎯 什么时候用:
- ✅ 临时任务完成使命后(如固件升级任务)
- ✅ 错误恢复时删除故障任务
- ✅ 动态任务管理(如连接断开时删除对应处理任务)
- ❌ 不要删除系统关键任务
// 场景:完成固件升级后解雇临时工
void Firmware_Update_Task(void *pvParameters) {
// 下载固件(1天工作量)
download_firmware();
// 升级固件(2天工作量)
update_firmware();
// 验证固件(1天工作量)
verify_firmware();
// 项目完成,解雇自己
printf("固件升级完成,临时工被解雇!\n");
vTaskDelete(NULL); // NULL表示解雇自己
}
⚠️ 注意事项:
- 做好工作交接:删除前释放分配的资源
- 避免突然解雇:关键任务不能随意删除
- 注意赔偿金:动态分配的内存需要由空闲任务释放
1.3 vTaskSuspend() / vTaskResume() - 挂起/恢复任务
🔍 现实比喻:员工带薪休假
- 挂起任务:员工休年假,保留职位但不工作
- 恢复任务:假期结束,回来继续工作
🎯 什么时候用:
- ✅ 临时暂停某个功能(如暂停数据采集)
- ✅ 低功耗模式下挂起非必要任务
- ✅ 调试时挂起任务观察系统行为
// 场景:系统进入低功耗模式,部分员工放假
void Enter_Low_Power_Mode(void) {
printf("公司进入节能模式,非关键岗位放假...\n");
// 数据展示员放假(非紧急)
vTaskSuspend(display_task_handle);
// 数据分析师放假(非紧急)
vTaskSuspend(analysis_task_handle);
// 退出节能模式,召回员工
printf("退出节能模式,召回所有员工!\n");
vTaskResume(display_task_handle);
vTaskResume(analysis_task_handle);
}
⚠️ 注意事项:
- 假期带薪:挂起的任务仍然占用内存
- 多次休假:多次挂起需要多次恢复才能继续工作
- 关键岗位:保安、CEO不能放假,否则公司会出问题
1.4 FreeRTOS任务相关API函数
| 函数 | 描述 |
|---|---|
| uxTaskPriorityGet() | 获取任务优先级 |
| vTaskPrioritySet() | 设置任务优先级 |
| uxTaskGetNumberOfTasks() | 获取系统中任务的数量 |
| uxTaskGetSystemState() | 获取所有任务状态信息 |
| vTaskGetInfo() | 获取指定单个的任务信息 |
| xTaskGetCurrentTaskHandle() | 获取当前任务的任务句柄 |
| xTaskGetHandle() | 根据任务名获取该任务的任务句柄 |
| uxTaskGetStackHighWaterMark() | 获取任务的任务栈历史剩余最小值 |
| eTaskGetState() | 获取任务状态 |
| vTaskList() | 以“表格”形式获取所有任务的信息 |
| vTaskGetRunTimeStats() | 获取任务的运行时间 |
📦 二、队列 API
2.1 xQueueCreate() - 创建队列
🔍 现实比喻:建立公司内部快递系统
- 快递站点:队列对象
- 货架大小:队列长度(能放多少快递)
- 快递类型:元素大小(快递包裹尺寸)
🎯 什么时候用:
- ✅ 任务间传递数据时(传感器→处理→显示)
- ✅ 中断向任务传递数据时
- ✅ 生产者-消费者模式
// 创建公司内部的快递系统
QueueHandle_t Create_Company_Express(void *pvParameters) {
// 建立文件快递站(能存10个文件,每个文件大小是FileStruct_t)
QueueHandle_t file_express = xQueueCreate(10, sizeof(FileStruct_t));
// 建立消息快递站(能存20条消息,每条消息大小是Message_t)
QueueHandle_t message_express = xQueueCreate(20, sizeof(Message_t));
// 建立紧急命令快递站(只能存1个,最新的覆盖旧的)
QueueHandle_t urgent_express = xQueueCreate(1, sizeof(UrgentCommand_t));
return file_express;
}
2.2 xQueueSend() / xQueueReceive()
🔍 现实比喻:快递员送货 vs 客户收件
- xQueueSend():快递员把包裹放到快递站
- xQueueReceive():客户从快递站取包裹
// 场景:传感器部门向数据处理部门传递数据
QueueHandle_t sensor_data_express;
// 传感器部门(生产者):采集并发送数据
void Sensor_Department(void *pvParameters) {
SensorData_t package; // 数据包裹
while(1) {
// 采集温度数据
package.temperature = read_temperature();
package.humidity = read_humidity();
package.timestamp = xTaskGetTickCount();
// 打包发送给数据处理部门
if(xQueueSend(sensor_data_express, &package, 0) == pdPASS) {
printf("传感器部门:数据包裹已发出!\n");
} else {
printf("传感器部门:快递站满了,等会儿再发!\n");
}
vTaskDelay(pdMS_TO_TICKS(1000)); // 每秒发一次
}
}
// 数据处理部门(消费者):接收并处理数据
void Data_Processing_Department(void *pv) {
SensorData_t received_package;
while(1) {
// 等待数据包裹送达
if(xQueueReceive(sensor_data_express, &received_package, portMAX_DELAY)) {
printf("数据处理部门:收到包裹,温度=%.1f°C\n",received_package.temperature);
// 处理数据
process_sensor_data(&received_package);
}
}
}
📊 快递系统选择指南:
| 需求 | 选择 | 现实比喻 |
|---|---|---|
| 普通数据传递 | xQueueSend() |
普通快递,按顺序排队 |
| 紧急命令 | xQueueSendToFront() |
VIP快递,插队到最前面 |
| 只关心最新状态 | xQueueOverwrite()(队列长度1) |
快递站只放最新包裹,旧的扔掉 |
| 查看但不拿走 | xQueuePeek() |
只看快递单号,不取包裹 |
🚦 三、信号量 API
3.1 二值信号量 - 等待单一事件
🔍 现实比喻:公司门口的访客登记牌
- 初始状态:登记牌显示"无人来访"(信号量值=0)
- 访客到来:前台把牌子翻到"有访客"(
xSemaphoreGive()) - 员工查看:看到"有访客"后去接待,并把牌子翻回"无人来访"(
xSemaphoreTake())
🎯 什么时候用:
- ✅ 等待中断发生(按键、串口接收)
- ✅ 任务启动同步(等所有初始化完成)
- ✅ 资源空闲通知
// 场景:前台通知CEO有重要访客
SemaphoreHandle_t visitor_semaphore = xSemaphoreCreateBinary();
// 前台任务:访客到来时通知
void Reception_Task(void *pvParameters) {
while(1) {
// 等待访客按门铃(硬件中断)
wait_for_doorbell();
// 访客来了!翻登记牌
printf("前台:CEO,有访客!\n");
xSemaphoreGive(visitor_semaphore); // 牌子翻到"有访客"
vTaskDelay(pdMS_TO_TICKS(100));
}
}
// CEO任务:等待访客通知
void CEO_Task(void *pvParameters) {
while(1) {
// 等待登记牌显示"有访客"
if(xSemaphoreTake(visitor_semaphore, portMAX_DELAY) == pdTRUE) {
printf("CEO:收到,马上接待!\n");
meet_visitor(); // 接待访客
// 接待完成后,牌子会自动翻回(Take后信号量变0)
}
}
}
3.2 计数信号量 - 管理有限资源
🔍 现实比喻:公司停车场的剩余车位显示屏
- 总车位:信号量最大计数值(如100个车位)
- 当前空位:信号量当前值(如显示"剩余车位:15")
- 车辆进入:车位-1(
xSemaphoreTake()) - 车辆离开:车位+1(
xSemaphoreGive())
🎯 什么时候用:
- ✅ 连接池管理(最大同时连接数)
- ✅ 缓冲区管理(可用缓冲区数量)
- ✅ 事件计数(接收到的数据包数)
// 场景:公司停车场管理(最多停50辆车)
#define TOTAL_PARKING_SPACES 50
SemaphoreHandle_t parking_semaphore = xSemaphoreCreateCounting(
TOTAL_PARKING_SPACES, // 总车位
TOTAL_PARKING_SPACES // 初始空车位
);
// 车辆进入停车场
void Car_Enter_Parking(void) {
// 尝试获取一个车位(等待最多1分钟)
if(xSemaphoreTake(parking_semaphore, pdMS_TO_TICKS(60000)) == pdTRUE) {
printf("车辆%d:成功进入停车场,剩余车位:%d\n",
car_id, uxSemaphoreGetCount(parking_semaphore));
park_car(); // 停车
} else {
printf("车辆%d:等了1分钟还没车位,开走了!\n", car_id);
}
}
// 车辆离开停车场
void Car_Leave_Parking(void) {
// 释放一个车位
xSemaphoreGive(parking_semaphore);
printf("车辆%d:离开停车场,剩余车位:%d\n",
car_id, uxSemaphoreGetCount(parking_semaphore));
}
3.3 互斥信号量 - 保护共享资源
🔍 现实比喻:公司唯一的一间董事长办公室
- 特性:一次只能一人使用,且有优先继承机制
- 员工使用:进去时锁门(
xSemaphoreTake()),出来时开门(xSemaphoreGive()) - 优先继承:如果普通员工在使用时董事长来了,普通员工临时提升为"董事长级别"优先完成
🎯 什么时候用:
- ✅ 保护共享外设(LCD、SPI Flash)
- ✅ 保护全局变量(防止数据竞争)
- ✅ 需要防止优先级翻转的场景
// 场景:多部门共享一台彩色打印机
SemaphoreHandle_t printer_mutex = xSemaphoreCreateMutex();
// 市场部打印彩色宣传册
void Marketing_Print(void) {
// 尝试获取打印机使用权(等待最多5秒)
if(xSemaphoreTake(printer_mutex, pdMS_TO_TICKS(5000)) == pdTRUE) {
printf("市场部:开始打印宣传册(需要30秒)\n");
// 打印过程中,如果财务部(高优先级)也需要打印
// 市场部会临时提升优先级,尽快完成打印
print_color_brochure(); // 打印30秒
printf("市场部:打印完成,释放打印机\n");
xSemaphoreGive(printer_mutex); // 开门
} else {
printf("市场部:等了5秒打印机还被占用,放弃打印\n");
}
}
// 财务部打印财务报表(高优先级)
void Finance_Print(void) {
// 财务部优先级更高,但会等待市场部完成打印
xSemaphoreTake(printer_mutex, portMAX_DELAY);
printf("财务部:紧急打印财务报表!\n");
print_financial_report();
printf("财务部:打印完成\n");
xSemaphoreGive(printer_mutex);
}
🚨 信号量选择决策树:
需要管理资源吗?
├─ 是 → 资源是单个还是多个?
│ ├─ 单个 → 需要防止优先级翻转吗?
│ │ ├─ 是 → 互斥信号量(有优先继承)
│ │ └─ 否 → 二值信号量(简单事件)
│ └─ 多个 → 计数信号量(可计数)
└─ 否 → 等待事件发生?→ 二值信号量
🎪 四、事件标志组 API
xEventGroupWaitBits()
🔍 现实比喻:航空公司登机前的检查点
- 登机条件:机票√ + 身份证√ + 健康码√ + 核酸报告√
- 每位旅客:对应一个事件位
- 检查员:等待所有条件满足(AND操作),或等待任意一个条件满足(OR操作)
🎯 什么时候用:
- ✅ 等待多个条件同时满足(AND逻辑)
- ✅ 等待任意条件满足(OR逻辑)
- ✅ 复杂的任务同步
// 场景:智能家居系统启动前检查
#define BIT_WIFI_CONNECTED (1 << 0) // 位0:WiFi连接
#define BIT_CLOUD_CONNECTED (1 << 1) // 位1:云平台连接
#define BIT_SENSOR_READY (1 << 2) // 位2:传感器就绪
#define BIT_ACTUATOR_READY (1 << 3) // 位3:执行器就绪
EventGroupHandle_t smart_home_events = xEventGroupCreate();
// 网络部门任务:连接WiFi
void WiFi_Task(void *pvParameters) {
connect_to_wifi();
printf("WiFi部门:WiFi连接成功!\n");
xEventGroupSetBits(smart_home_events, BIT_WIFI_CONNECTED);
}
// 云平台部门任务:连接云端
void Cloud_Task(void *pvParameters) {
connect_to_cloud();
printf("云平台部门:云端连接成功!\n");
xEventGroupSetBits(smart_home_events, BIT_CLOUD_CONNECTED);
}
// 系统启动任务:等待所有条件满足
void System_Start_Task(void *pvParameters) {
printf("系统:等待所有启动条件...\n");
// 等待四个条件都满足(AND操作),就像登机前检查
EventBits_t ready_flags = xEventGroupWaitBits(
smart_home_events, // 事件组
BIT_WIFI_CONNECTED | BIT_CLOUD_CONNECTED |
BIT_SENSOR_READY | BIT_ACTUATOR_READY, // 等待的位
pdTRUE, // 成功后清除这些位(检查完就把登机牌打孔)
pdTRUE, // 等待所有位(必须全部满足)
portMAX_DELAY // 一直等
);
printf("系统:所有条件满足,智能家居启动!\n");
start_smart_home_system();
}
⚡ 高级用法:等待任意报警
// 场景:家庭安全系统,任一传感器报警都要处理
#define BIT_DOOR_OPENED (1 << 0) // 门被打开
#define BIT_WINDOW_BROKEN (1 << 1) // 窗户破碎
#define BIT_MOTION_DETECTED (1 << 2) // 检测到移动
#define BIT_SMOKE_DETECTED (1 << 3) // 检测到烟雾
void Security_Monitor_Task(void *pvParameters) {
// 等待任意一个报警事件(OR操作)
EventBits_t alarm_flags = xEventGroupWaitBits(
security_events,
BIT_DOOR_OPENED | BIT_WINDOW_BROKEN |
BIT_MOTION_DETECTED | BIT_SMOKE_DETECTED,
pdTRUE, // 清除触发的位
pdFALSE, // 等待任意位(不是所有位)
portMAX_DELAY
);
// 检查具体哪个报警
if(alarm_flags & BIT_SMOKE_DETECTED) {
printf("🚨 火警!启动灭火系统!\n");
activate_fire_system();
} else if(alarm_flags & BIT_DOOR_OPENED) {
printf("🚨 门被非法打开!\n");
trigger_door_alarm();
}
// ... 其他报警处理
}
📨 五、任务通知 API
xTaskNotify() / ulTaskNotifyTake()
🔍 现实比喻:公司内部的对讲机系统
- 传统方式:用广播系统(信号量/队列),所有人都能听到
- 任务通知:用对讲机直接呼叫某个人,快速私密
🎯 什么时候用:
- ✅ 替代二值信号量(节省内存,提高速度)
- ✅ 替代计数信号量(轻量级计数)
- ✅ 替代事件标志组(单个任务的事件组)
// 场景:紧急情况下,CEO直接呼叫保安队长
TaskHandle_t security_captain_handle;
// 传统方式:用广播系统(信号量)
SemaphoreHandle_t broadcast_system = xSemaphoreCreateBinary();
// 缺点:所有人都会收到,不知道具体叫谁
// 现代方式:用对讲机(任务通知)
void CEO_Emergency_Call(void) {
printf("CEO:紧急情况!保安队长请速到办公室!\n");
// 方式1:快速呼叫(计数方式)
xTaskNotifyGive(security_captain_handle); // 对讲机喊一声
// 方式2:带信息的呼叫
uint32_t emergency_type = EMERGENCY_FIRE;
xTaskNotify(security_captain_handle, emergency_type, eSetValueWithOverwrite);
}
// 保安队长:随时待命
void Security_Captain_Task(void *pvParameters) {
while(1) {
// 等待CEO呼叫
uint32_t notification_value;
// 方式1:简单等待呼叫
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
printf("保安队长:收到!马上到!\n");
handle_emergency();
// 方式2:接收带信息的呼叫
xTaskNotifyWait(0, 0xFFFFFFFF, ¬ification_value, portMAX_DELAY);
if(notification_value == EMERGENCY_FIRE) {
printf("保安队长:是火警!带灭火器!\n");
}
}
}
💡 任务通知的"呼叫方式":
| 呼叫方式 | 现实比喻 | 代码示例 |
|---|---|---|
eNoAction |
"听到请回复!"(只通知) | xTaskNotify(handle, 0, eNoAction) |
eSetBits |
"A区B区注意!"(设置标志位) | `xTaskNotify(handle, BIT_A |
eIncrement |
"这是第3次呼叫!"(计数) | xTaskNotify(handle, 0, eIncrement) |
eSetValueWithOverwrite |
"最新消息:有火情!"(覆盖) | xTaskNotify(handle, FIRE, eSetValueWithOverwrite) |
eSetValueWithoutOverwrite |
"如果没收到过消息,就记下这条"(不覆盖) | xTaskNotify(handle, msg, eSetValueWithoutOverwrite) |
⏰ 六、软件定时器 API
xTimerCreate() / xTimerStart()
🔍 现实比喻:公司的定时闹钟系统
- 硬件闹钟:必须有人手动设置,精度高(硬件定时器)
- 软件闹钟:手机APP设置,灵活但精度稍差(软件定时器)
🎯 什么时候用:
- ✅ 周期性任务(每秒采集数据)
- ✅ 延时操作(5秒后关闭LED)
- ✅ 超时检测(等待响应超时)
// 场景:公司日常定时事务
TimerHandle_t company_daily_timer;
void daily_routine_callback(TimerHandle_t timer) {
printf("⏰ 定时提醒:\n");
printf(" 1. 早上9:00:晨会\n");
printf(" 2. 下午2:00:下午茶时间\n");
printf(" 3. 晚上6:00:下班打卡\n");
}
// 系统初始化时设置公司作息
void Setup_Company_Schedule(void) {
// 创建每日定时器(24小时周期)
company_daily_timer = xTimerCreate(
"CompanySchedule", // 闹钟名称
pdMS_TO_TICKS(24 * 60 * 60 * 1000), // 24小时
pdTRUE, // 自动重载(每天重复)
(void *)1, // 闹钟ID
daily_routine_callback // 响铃时的处理函数
);
// 启动闹钟(明天9点开始)
xTimerStart(company_daily_timer, pdMS_TO_TICKS(1000));
// 创建单次闹钟:5分钟后提醒开会
TimerHandle_t meeting_timer = xTimerCreate(
"MeetingReminder",
pdMS_TO_TICKS(5 * 60 * 1000), // 5分钟后
pdFALSE, // 单次执行
NULL,
meeting_reminder_callback
);
xTimerStart(meeting_timer, 0);
}
⚠️ 软件定时器特点:
- 在定时器服务任务中执行 → 闹钟响了,是助理去处理,不是CEO亲自处理
- 精度受系统影响 → 如果系统忙,闹钟可能晚几秒响
- 不能阻塞 → 助理处理闹钟时要快速,不能打2小时电话
🔌 七、中断管理 API
taskENTER_CRITICAL() / portDISABLE_INTERRUPTS()
🔍 现实比喻:董事长的重要会议
- 轻度保护:
taskENTER_CRITICAL()→ 会议室挂"会议中"牌子,不接普通电话,但紧急电话(高优先级中断)还能接 - 重度保护:
portDISABLE_INTERRUPTS()→ 会议室锁门,拔掉电话线,任何人不得打扰
🎯 什么时候用:
- ✅ 保护关键代码段不被其他任务打断
- ✅ 原子操作(如自增计数器)
// 场景1:财务部门做年度结算(不能被打断)
void Annual_Financial_Report(void) {
// 挂上"财务结算中,请勿打扰"牌子
taskENTER_CRITICAL();
// 关键操作:计算年度总账
calculate_total_revenue();
calculate_total_expenses();
generate_financial_report();
// 取下牌子,恢复正常
taskEXIT_CRITICAL();
printf("财务部:年度报告完成!\n");
}
// 场景2:硬件工程师升级BIOS(任何中断都不能有)
void Update_System_BIOS(void) {
printf("硬件部:开始升级BIOS,系统将暂时无响应...\n");
// 锁门!拔电话线!任何事都不能打扰!
portDISABLE_INTERRUPTS();
// 敏感操作:写入BIOS芯片
write_bios_chunk(0, bios_data, 256);
delay_us(50); // 必须精确延时
write_bios_chunk(256, bios_data + 256, 256);
// 恢复门禁和电话
portENABLE_INTERRUPTS();
printf("硬件部:BIOS升级完成!\n");
}
xxxFromISR() 系列 API
🔍 现实比喻:急诊室的呼叫系统
- 普通通信:走正常流程(邮件、电话)
- 急诊通信:直接按紧急呼叫按钮(FromISR函数),立即处理
🎯 什么时候用:中断服务函数中与任务通信
// 场景:病人按了急诊呼叫按钮
QueueHandle_t emergency_queue;
// 急诊呼叫按钮被按下(硬件中断)
void Emergency_Button_IRQHandler(void) {
EmergencyInfo_t emergency;
emergency.patient_id = read_patient_id();
emergency.emergency_type = read_emergency_type();
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// 方式1:放入急诊队列(最常用)
xQueueSendFromISR(emergency_queue, &emergency, &xHigherPriorityTaskWoken);
// 方式2:直接呼叫值班医生(任务通知)
vTaskNotifyGiveFromISR(doctor_task_handle, &xHigherPriorityTaskWoken);
// 方式3:释放急诊信号量
xSemaphoreGiveFromISR(emergency_sem, &xHigherPriorityTaskWoken);
// 如果需要,立即切换任务让医生处理
if(xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
💾 八、内存管理 API
pvPortMalloc() / vPortFree()
🔍 现实比喻:公司的物资仓库
- 仓库管理员:内存管理算法(heap_1~heap_5)
- 领用物资:
pvPortMalloc()→ 填写领用单,领取物资 - 归还物资:
vPortFree()→ 填写归还单,放回仓库
🎯 什么时候用:
- ✅ 临时大缓冲区(图像处理、文件传输)
- ✅ 动态数据结构(链表、树)
- ✅ 运行时配置加载
// 场景:市场部需要临时借用展台物料
void Marketing_Exhibition(void) {
printf("市场部:准备展会,需要临时借用展台物料\n");
// 填写借物单:借100KB的展台装饰材料
uint8_t *exhibition_materials = (uint8_t *)pvPortMalloc(100 * 1024);
if(exhibition_materials != NULL) {
printf("仓库:展台物料已出库,请妥善使用\n");
// 使用物料布置展台
setup_exhibition(exhibition_materials);
hold_exhibition();
// 展会结束,归还物料
printf("市场部:展会结束,归还物料\n");
vPortFree(exhibition_materials);
printf("仓库:物料已归还,可再次借用\n");
} else {
printf("仓库:库存不足,无法出借!\n");
}
}
⚠️ 嵌入式内存管理原则:
- 优先静态分配
- 避免频繁申请释放
- 监控内存使用
- 选择合适的heap方案
📊 仓库管理方案选择:
| 管理方案 | 现实比喻 | 特点 | 适用场景 |
|---|---|---|---|
| heap_1 | 固定物资分配 | 只借不还,开业时一次性分配 | 启动时分配所有内存,永不释放 |
| heap_2 | 按大小精准分配 | 按需分配,但不整理仓库 | 分配块大小固定的场景 |
| heap_3 | 外包仓库管理 | 调用标准库的malloc/free | 已有标准库的项目 |
| heap_4 | 智能仓库管理 | 自动整理,合并碎片 | 最常用,推荐 |
| heap_5 | 多仓库联合管理 | 管理多个不连续的仓库 | 内存分布复杂的系统 |
🏢 综合小项目:智能温室控制系统
项目背景
建立一个智能温室,自动调节温度、湿度、光照,确保植物最佳生长环境。
系统设计
🌡️ 硬件组成(数据用软件模拟,就练习一下FreeRTOS的API):
- 温度传感器(DS18B20)
- 湿度传感器(DHT11)
- 光照传感器(BH1750)
- 加热器(继电器控制)
- 加湿器(继电器控制)
- LED补光灯(PWM控制)
- 液晶显示屏(OLED)
- 按键(设置参数)
🔧 软件任务:
1. 传感器采集任务(温度、湿度、光照)
2. 环境控制任务(加热、加湿、补光)
3. 显示任务(OLED显示当前环境)
4. 用户交互任务(按键处理)
5. 系统监控任务(健康检查)
6. 数据记录任务(SD卡存储)
完整代码实现
/*******************************************************
* 智能温室控制系统 - FreeRTOS综合应用
* 功能:自动调节温湿度光照,OLED显示,按键设置
*******************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "timers.h"
#include <stdio.h>
/* ========== 硬件引脚定义 ========== */
// 传感器引脚
#define TEMP_SENSOR_PIN GPIO_PIN_0
#define HUMIDITY_SENSOR_PIN GPIO_PIN_1
#define LIGHT_SENSOR_PIN GPIO_PIN_2
// 执行器引脚
#define HEATER_PIN GPIO_PIN_3
#define HUMIDIFIER_PIN GPIO_PIN_4
#define LED_LIGHT_PIN GPIO_PIN_5
// 按键引脚
#define KEY_UP_PIN GPIO_PIN_6
#define KEY_DOWN_PIN GPIO_PIN_7
#define KEY_OK_PIN GPIO_PIN_8
/* ========== 数据结构定义 ========== */
typedef struct {
float temperature; // 温度 (°C)
float humidity; // 湿度 (%)
float light; // 光照 (lux)
uint32_t timestamp; // 时间戳 (ms)
} GreenhouseData_t;
typedef enum {
MODE_AUTO = 0, // 自动模式
MODE_MANUAL, // 手动模式
MODE_SETTING // 设置模式
} SystemMode_t;
typedef struct {
float target_temp; // 目标温度
float target_humidity; // 目标湿度
float target_light; // 目标光照
SystemMode_t mode; // 系统模式
} SystemConfig_t;
/* ========== 全局变量 ========== */
// 通信队列
QueueHandle_t sensor_data_queue; // 传感器数据队列
QueueHandle_t control_command_queue; // 控制命令队列
// 信号量
SemaphoreHandle_t oled_mutex; // OLED显示互斥锁
SemaphoreHandle_t config_mutex; // 配置参数互斥锁
// 事件组
EventGroupHandle_t greenhouse_events;
#define EVENT_NEW_DATA (1 << 0) // 新数据就绪
#define EVENT_TEMP_HIGH (1 << 1) // 温度过高
#define EVENT_TEMP_LOW (1 << 2) // 温度过低
#define EVENT_HUMIDITY_HIGH (1 << 3) // 湿度过高
#define EVENT_HUMIDITY_LOW (1 << 4) // 湿度过低
#define EVENT_LIGHT_LOW (1 << 5) // 光照过低
// 软件定时器
TimerHandle_t data_log_timer; // 数据记录定时器
TimerHandle_t system_monitor_timer; // 系统监控定时器
// 任务句柄
TaskHandle_t sensor_task_handle;
TaskHandle_t control_task_handle;
TaskHandle_t display_task_handle;
TaskHandle_t ui_task_handle;
TaskHandle_t monitor_task_handle;
// 系统配置
SystemConfig_t system_config = {
.target_temp = 25.0f, // 默认目标温度25°C
.target_humidity = 60.0f, // 默认目标湿度60%
.target_light = 500.0f, // 默认目标光照500lux
.mode = MODE_AUTO // 默认自动模式
};
/* ========== 硬件模拟函数 ========== */
float read_temperature(void) {
// 模拟温度读数:在20-30°C之间波动
static float temp = 25.0f;
temp += ((rand() % 100) - 50) / 100.0f; // ±0.5°C波动
if(temp < 20.0f) temp = 20.0f;
if(temp > 30.0f) temp = 30.0f;
return temp;
}
float read_humidity(void) {
// 模拟湿度读数:在40-80%之间波动
static float humidity = 60.0f;
humidity += ((rand() % 100) - 50) / 100.0f; // ±0.5%波动
if(humidity < 40.0f) humidity = 40.0f;
if(humidity > 80.0f) humidity = 80.0f;
return humidity;
}
float read_light(void) {
// 模拟光照读数:白天高,夜晚低
static uint32_t time_counter = 0;
time_counter++;
// 模拟昼夜变化(每1000次计数一个周期)
uint32_t time_of_day = time_counter % 1000;
if(time_of_day < 300) {
// "夜晚":光照较低 0-100 lux
return (float)(rand() % 100);
} else {
// "白天":光照较高 300-800 lux
return 300.0f + (float)(rand() % 500);
}
}
void set_heater(uint8_t state) {
HAL_GPIO_WritePin(GPIOA, HEATER_PIN, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
printf("加热器:%s\n", state ? "开启" : "关闭");
}
void set_humidifier(uint8_t state) {
HAL_GPIO_WritePin(GPIOA, HUMIDIFIER_PIN, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
printf("加湿器:%s\n", state ? "开启" : "关闭");
}
void set_led_light(uint8_t brightness) {
// PWM控制LED亮度(0-100%)
printf("补光灯:亮度%d%%\n", brightness);
}
void oled_display(const char *line1, const char *line2) {
// 模拟OLED显示
printf("========== OLED显示 ==========\n");
printf("%s\n", line1);
printf("%s\n", line2);
printf("============================\n");
}
uint8_t read_key(void) {
// 模拟按键读取
static uint32_t key_counter = 0;
key_counter++;
if(key_counter % 50 == 0) return 1; // 模拟KEY_UP
if(key_counter % 70 == 0) return 2; // 模拟KEY_DOWN
if(key_counter % 90 == 0) return 3; // 模拟KEY_OK
return 0; // 无按键
}
/* ========== 任务1:传感器采集任务 ========== */
void Sensor_Task(void *pvParameters) {
GreenhouseData_t sensor_data;
TickType_t last_wake_time = xTaskGetTickCount();
printf("[传感器任务] 启动,每2秒采集一次数据\n");
while(1) {
// 采集传感器数据
sensor_data.temperature = read_temperature();
sensor_data.humidity = read_humidity();
sensor_data.light = read_light();
sensor_data.timestamp = xTaskGetTickCount();
// 发送到数据队列
if(xQueueSend(sensor_data_queue, &sensor_data, 0) != pdPASS) {
printf("[警告] 传感器数据队列已满,数据丢失!\n");
}
// 设置事件标志:新数据就绪
xEventGroupSetBits(greenhouse_events, EVENT_NEW_DATA);
// 检查温度是否异常
if(sensor_data.temperature > 28.0f) {
xEventGroupSetBits(greenhouse_events, EVENT_TEMP_HIGH);
} else if(sensor_data.temperature < 22.0f) {
xEventGroupSetBits(greenhouse_events, EVENT_TEMP_LOW);
}
// 检查湿度是否异常
if(sensor_data.humidity > 70.0f) {
xEventGroupSetBits(greenhouse_events, EVENT_HUMIDITY_HIGH);
} else if(sensor_data.humidity < 50.0f) {
xEventGroupSetBits(greenhouse_events, EVENT_HUMIDITY_LOW);
}
// 检查光照是否不足
if(sensor_data.light < 100.0f) {
xEventGroupSetBits(greenhouse_events, EVENT_LIGHT_LOW);
}
// 每2秒采集一次
vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(2000));
}
}
/* ========== 任务2:环境控制任务 ========== */
void Control_Task(void *pvParameters) {
GreenhouseData_t current_data;
EventBits_t events;
uint8_t heater_state = 0;
uint8_t humidifier_state = 0;
uint8_t light_brightness = 0;
printf("[控制任务] 启动,自动调节温室环境\n");
while(1) {
// 等待新数据就绪事件
events = xEventGroupWaitBits(greenhouse_events,
EVENT_NEW_DATA | EVENT_TEMP_HIGH | EVENT_TEMP_LOW |
EVENT_HUMIDITY_HIGH | EVENT_HUMIDITY_LOW | EVENT_LIGHT_LOW,
pdTRUE, // 清除事件位
pdFALSE, // 等待任意事件
portMAX_DELAY);
// 从队列获取最新传感器数据
if(xQueueReceive(sensor_data_queue, ¤t_data, 0) == pdPASS) {
printf("[控制任务] 收到数据:温度%.1f°C 湿度%.1f%% 光照%.0flux\n",
current_data.temperature, current_data.humidity, current_data.light);
// 自动模式:根据目标值自动控制
if(system_config.mode == MODE_AUTO) {
// 温度控制
if(current_data.temperature < system_config.target_temp - 1.0f) {
set_heater(1); // 开启加热器
heater_state = 1;
} else if(current_data.temperature > system_config.target_temp + 1.0f) {
set_heater(0); // 关闭加热器
heater_state = 0;
}
// 湿度控制
if(current_data.humidity < system_config.target_humidity - 5.0f) {
set_humidifier(1); // 开启加湿器
humidifier_state = 1;
} else if(current_data.humidity > system_config.target_humidity + 5.0f) {
set_humidifier(0); // 关闭加湿器
humidifier_state = 0;
}
// 光照控制
if(current_data.light < system_config.target_light - 100.0f) {
light_brightness = 80; // 开启补光灯
set_led_light(light_brightness);
} else if(current_data.light > system_config.target_light + 100.0f) {
light_brightness = 0; // 关闭补光灯
set_led_light(light_brightness);
}
}
// 处理异常事件
if(events & EVENT_TEMP_HIGH) {
printf("[警告] 温度过高!强制关闭加热器\n");
set_heater(0);
}
if(events & EVENT_HUMIDITY_LOW) {
printf("[警告] 湿度过低!强制开启加湿器\n");
set_humidifier(1);
}
}
}
}
/* ========== 任务3:显示任务 ========== */
void Display_Task(void *pvParameters) {
GreenhouseData_t display_data;
char line1[32], line2[32];
TickType_t last_display_time = 0;
printf("[显示任务] 启动,更新OLED显示\n");
while(1) {
// 每1秒更新一次显示
if((xTaskGetTickCount() - last_display_time) > pdMS_TO_TICKS(1000)) {
last_display_time = xTaskGetTickCount();
// 获取互斥锁,保护OLED访问
if(xSemaphoreTake(oled_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
// 尝试获取最新数据(非阻塞)
if(xQueuePeek(sensor_data_queue, &display_data, 0) == pdPASS) {
// 格式化显示内容
snprintf(line1, sizeof(line1), "T:%.1fC H:%.1f%%",
display_data.temperature, display_data.humidity);
snprintf(line2, sizeof(line2), "L:%.0flux MODE:%s",
display_data.light,
system_config.mode == MODE_AUTO ? "AUTO" :
system_config.mode == MODE_MANUAL ? "MAN" : "SET");
// 更新OLED显示
oled_display(line1, line2);
}
xSemaphoreGive(oled_mutex);
}
}
vTaskDelay(pdMS_TO_TICKS(100)); // 100ms检查一次
}
}
/* ========== 任务4:用户界面任务 ========== */
void UI_Task(void *pvParameters) {
uint8_t key_value;
uint8_t setting_item = 0; // 0:温度 1:湿度 2:光照 3:模式
printf("[UI任务] 启动,处理按键输入\n");
while(1) {
key_value = read_key();
if(key_value != 0) {
// 获取配置互斥锁
if(xSemaphoreTake(config_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
switch(key_value) {
case 1: // KEY_UP:增加
if(system_config.mode == MODE_SETTING) {
switch(setting_item) {
case 0: system_config.target_temp += 0.5f; break;
case 1: system_config.target_humidity += 1.0f; break;
case 2: system_config.target_light += 50.0f; break;
case 3: system_config.mode = MODE_AUTO; break;
}
printf("[设置] 参数增加\n");
} else {
// 切换为设置模式
system_config.mode = MODE_SETTING;
printf("[设置] 进入设置模式\n");
}
break;
case 2: // KEY_DOWN:减少
if(system_config.mode == MODE_SETTING) {
switch(setting_item) {
case 0: system_config.target_temp -= 0.5f; break;
case 1: system_config.target_humidity -= 1.0f; break;
case 2: system_config.target_light -= 50.0f; break;
case 3: system_config.mode = MODE_MANUAL; break;
}
printf("[设置] 参数减少\n");
}
break;
case 3: // KEY_OK:确认/下一项
if(system_config.mode == MODE_SETTING) {
setting_item = (setting_item + 1) % 4;
printf("[设置] 切换到下一设置项\n");
}
break;
}
xSemaphoreGive(config_mutex);
// 显示当前设置
printf("[设置] 目标温度:%.1f°C 湿度:%.1f%% 光照:%.0flux\n",
system_config.target_temp, system_config.target_humidity,
system_config.target_light);
}
}
vTaskDelay(pdMS_TO_TICKS(50)); // 50ms检测一次按键
}
}
/* ========== 任务5:系统监控任务 ========== */
void Monitor_Task(void *pvParameters) {
char task_list[512];
uint32_t last_monitor_time = 0;
printf("[监控任务] 启动,监控系统健康\n");
while(1) {
// 每10秒输出一次系统状态
if((xTaskGetTickCount() - last_monitor_time) > pdMS_TO_TICKS(10000)) {
last_monitor_time = xTaskGetTickCount();
printf("========== 系统监控 ==========\n");
printf("运行时间: %lu秒\n", xTaskGetTickCount() / 1000);
printf("剩余堆内存: %d字节\n", xPortGetFreeHeapSize());
// 显示任务状态(需要启用configUSE_TRACE_FACILITY)
vTaskList(task_list);
printf("%s\n", task_list);
// 显示队列使用情况
UBaseType_t queue_messages = uxQueueMessagesWaiting(sensor_data_queue);
printf("传感器队列: %d/10条消息\n", queue_messages);
}
vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒检查一次
}
}
/* ========== 定时器回调函数 ========== */
// 数据记录定时器回调
void Data_Log_Callback(TimerHandle_t xTimer) {
GreenhouseData_t log_data;
// 获取最新数据
if(xQueuePeek(sensor_data_queue, &log_data, 0) == pdPASS) {
printf("[数据记录] 时间:%lu, 温度:%.1f, 湿度:%.1f, 光照:%.0f\n",
log_data.timestamp, log_data.temperature,
log_data.humidity, log_data.light);
// 这里可以添加SD卡存储代码
// save_to_sd_card(&log_data);
}
}
// 系统监控定时器回调
void System_Monitor_Callback(TimerHandle_t xTimer) {
static uint32_t timer_count = 0;
timer_count++;
printf("[定时器] 系统自检 %lu 次\n", timer_count);
// 检查堆内存使用
if(xPortGetFreeHeapSize() < 1024) {
printf("[警告] 堆内存不足!\n");
}
}
/* ========== 系统初始化 ========== */
void Greenhouse_System_Init(void) {
printf("\n=================================\n");
printf(" 智能温室控制系统 v1.0\n");
printf("=================================\n\n");
// 1. 创建队列(快递系统)
sensor_data_queue = xQueueCreate(10, sizeof(GreenhouseData_t));
control_command_queue = xQueueCreate(5, sizeof(uint8_t));
if(sensor_data_queue == NULL || control_command_queue == NULL) {
printf("[错误] 队列创建失败!\n");
return;
}
// 2. 创建信号量(门锁系统)
oled_mutex = xSemaphoreCreateMutex();
config_mutex = xSemaphoreCreateMutex();
// 3. 创建事件组(检查点系统)
greenhouse_events = xEventGroupCreate();
// 4. 创建软件定时器(闹钟系统)
data_log_timer = xTimerCreate("DataLog",
pdMS_TO_TICKS(30000), // 30秒记录一次
pdTRUE, // 自动重载
NULL,
Data_Log_Callback);
system_monitor_timer = xTimerCreate("SystemMonitor",
pdMS_TO_TICKS(60000), // 60秒监控一次
pdTRUE,
NULL,
System_Monitor_Callback);
// 5. 启动定时器
xTimerStart(data_log_timer, 0);
xTimerStart(system_monitor_timer, 0);
printf("[系统] 通信资源初始化完成\n");
// 6. 创建任务(招聘员工)
xTaskCreate(Sensor_Task, "Sensor", 256, NULL, 3, &sensor_task_handle);
xTaskCreate(Control_Task, "Control", 256, NULL, 4, &control_task_handle);
xTaskCreate(Display_Task, "Display", 192, NULL, 2, &display_task_handle);
xTaskCreate(UI_Task, "UI", 192, NULL, 2, &ui_task_handle);
xTaskCreate(Monitor_Task, "Monitor", 256, NULL, 1, &monitor_task_handle);
printf("[系统] 所有任务创建完成\n");
printf("[系统] 温室控制系统启动成功!\n");
printf("=================================\n\n");
}
/* ========== 主函数 ========== */
int main(void) {
// 硬件初始化(实际项目需要)
HAL_Init();
SystemClock_Config();
// 初始化GPIO
// MX_GPIO_Init();
// 初始化串口用于调试
// MX_USART1_UART_Init();
printf("硬件初始化完成\n");
// 启动温室控制系统
Greenhouse_System_Init();
// 启动FreeRTOS调度器
vTaskStartScheduler();
// 如果调度器启动失败,执行到这里
while(1) {
printf("FreeRTOS调度器启动失败!\n");
HAL_Delay(1000);
}
return 0;
}
项目运行效果
=================================
智能温室控制系统 v1.0
=================================
[系统] 通信资源初始化完成
[系统] 所有任务创建完成
[系统] 温室控制系统启动成功!
=================================
[传感器任务] 启动,每2秒采集一次数据
[控制任务] 启动,自动调节温室环境
[显示任务] 启动,更新OLED显示
[UI任务] 启动,处理按键输入
[监控任务] 启动,监控系统健康
========== OLED显示 ==========
T:24.3C H:58.7%
L:423lux MODE:AUTO
============================
[控制任务] 收到数据:温度24.3°C 湿度58.7% 光照423lux
========== OLED显示 ==========
T:24.5C H:59.1%
L:398lux MODE:AUTO
============================
[数据记录] 时间:30000, 温度:24.5, 湿度:59.1, 光照:398
========== 系统监控 ==========
运行时间: 10秒
剩余堆内存: 8152字节
任务名 状态 优先级 剩余栈
Sensor R 3 112
Control B 4 108
Display R 2 96
UI R 2 100
Monitor R 1 120
IDLE R 0 124
Tmr Svc B 6 116
传感器队列: 3/10条消息
[设置] 进入设置模式
[设置] 目标温度:25.0°C 湿度:60.0% 光照:500lux

浙公网安备 33010602011771号