1. 项目概述
在嵌入式实时操作系统开发中,任务同步与资源共享是两个永恒的话题。作为一名长期从事STM32开发的工程师,我经常看到新手在使用FreeRTOS时对信号量和互斥量的混淆。本文将从一个真实的工业项目案例出发,详细剖析如何在DMA串口发送场景中正确使用这两种同步机制。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 多任务环境下的资源竞争
2.1 典型问题场景
假设我们有一个基于STM32F407的工业控制器,需要三个任务通过USART1发送数据:
- 监控任务(优先级3):每1秒发送设备状态
- 通信任务(优先级5):响应上位机指令
- 报警任务(优先级7):实时发送报警信息
如果不加保护直接操作串口,可能会出现以下问题:
- 任务A刚配置好DMA,就被高优先级任务B抢占
- 任务B修改了DMA配置,导致任务A的数据发送错误
- 多个任务同时操作USART寄存器导致硬件异常
2.2 同步机制的必要性
在裸机系统中,我们常用以下方式保护共享资源:
- 关闭中断
- 使用标志位变量
- 硬件FIFO缓冲
但在RTOS环境中,这些方法存在明显缺陷:
- 关中断时间过长会影响系统实时性
- 标志位检查无法解决优先级反转问题
- 硬件FIFO容量有限,无法解决根本问题
3. 信号量深度解析
3.1 信号量的本质
信号量本质上是一个带阻塞机制的计数器,其核心操作:
c复制// 创建二值信号量
SemaphoreHandle_t xSemaphoreCreateBinary(void);
// 获取信号量(阻塞式)
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
// 释放信号量
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
3.2 二值信号量的典型应用
在DMA传输场景中,二值信号量最常用于事件通知:
- 任务启动DMA传输后立即阻塞等待信号量
- DMA传输完成中断中释放信号量
- 任务被唤醒,继续执行后续操作
这种模式相比轮询方式可以节省90%以上的CPU资源。
3.3 计数信号量的资源管理
当系统有多个相同类型的资源时(如内存池、缓冲区),计数信号量是理想选择:
c复制// 创建管理5个缓冲区的信号量
SemaphoreHandle_t buf_sem = xSemaphoreCreateCounting(5, 5);
// 任务获取缓冲区
if(xSemaphoreTake(buf_sem, 100) == pdTRUE) {
// 成功获取缓冲区
// 使用完毕后释放
xSemaphoreGive(buf_sem);
}
4. 互斥量的特殊机制
4.1 优先级继承原理
互斥量最核心的特性是优先级继承,其工作流程
