文章目录
在单片机上使用FreeRTOS时,信号量是任务间同步、互斥或资源管理的核心机制。FreeRTOS中常用的信号量类型包括 二进制信号量 、 计数信号量 、 互斥信号量(Mutex) 和 递归互斥信号量 ,它们的应用场景有明确区分,具体如下:
1. 二进制信号量(Binary Semaphore)
二进制信号量只有两种状态:可用(1) 和不可用(0) ,核心用途是同步(任务间或中断与任务间的"事件通知")。
典型应用场景:
-
中断与任务同步 :
单片机的外部中断(如按键、传感器触发)通常需要快速响应,复杂逻辑(如按键防抖、数据解析)适合放在任务中处理。此时,中断服务程序(ISR)可通过释放二进制信号量"通知"任务,任务则阻塞等待信号量,实现"中断触发→任务处理"的同步。
例:按键按下触发外部中断,ISR中调用
xSemaphoreGiveFromISR()释放信号量,按键处理任务通过xSemaphoreTake()等待信号量,获取后执行按键逻辑(避免在ISR中做耗时操作)。 -
任务间同步 :
一个任务完成某操作后,通知另一个任务开始执行。例如:传感器采集任务(A)完成数据采集后,释放信号量,数据处理任务(B)等待信号量后开始处理数据,实现"A完成→B启动"的顺序控制。
2. 计数信号量(Counting Semaphore)
计数信号量的状态是一个非负整数(计数),可用于资源数量管理 或事件次数统计 ,计数的范围由创建时的uxMaxCount限制。
典型应用场景:
-
有限资源管理 :
当多个任务需要竞争访问有限数量的共享资源时(如缓冲区、外设通道),计数信号量的初始值设为资源总数,任务获取资源时计数减1,释放时计数加1。
例:单片机有3个UART发送缓冲区,计数信号量初始化为3。任务需要发送数据时,先通过
xSemaphoreTake()获取信号量(占用1个缓冲区),发送完成后通过xSemaphoreGive()释放(归还缓冲区),确保缓冲区不被超额占用。 -
事件计数 :
统计某事件发生的次数,任务可一次性处理多次事件。例如:高频传感器(如加速度计)每采集一次数据就释放一次信号量(计数+1),数据处理任务可通过
xSemaphoreTake()一次获取多个计数(如一次处理10次采集的数据),减少任务唤醒频率。
3. 互斥信号量(Mutex)
互斥信号量本质是一种特殊的二进制信号量,用于保护共享资源 (防止多个任务同时访问导致数据混乱),核心特性是优先级继承(避免优先级反转)。
典型应用场景:
- 共享外设/资源的互斥访问 :
单片机中多个任务可能需要访问同一外设(如I2C总线、SPI设备、全局变量),此时需用Mutex确保"同一时间只有一个任务访问资源"。
例:任务A(低优先级)和任务B(高优先级)都需要通过I2C访问温湿度传感器。任务A获取Mutex后正在操作I2C时,任务B就绪:若用普通二进制信号量,任务B会一直等待(优先级反转),而Mutex的优先级继承机制会临时提升任务A的优先级,让其快速释放资源,避免高优先级任务长期阻塞。
4. 递归互斥信号量(Recursive Mutex)
递归互斥信号量允许同一任务多次获取信号量(需对应多次释放),用于任务中存在"嵌套访问共享资源"的场景,避免自我死锁。
典型应用场景:
- 嵌套函数访问共享资源 :
当一个任务中的函数A调用函数B,且A和B都需要访问同一共享资源时,普通Mutex会导致死锁(任务已持有Mutex,再次获取时会阻塞自己)。递归Mutex允许同一任务多次获取,只需释放次数与获取次数相同即可。
例:单片机的日志模块中,log_debug()函数调用log_write()函数,两者都需要访问日志缓冲区(共享资源)。用递归Mutex保护缓冲区,log_debug()获取后,log_write()可再次获取,避免死锁。
总结
- 二进制信号量:侧重"同步"(事件通知),如中断-任务、任务-任务的触发。
- 计数信号量:侧重"数量管理",如有限资源分配或事件次数统计。
- 互斥信号量:侧重"共享资源保护",解决多任务竞争,带优先级继承。
- 递归互斥信号量:侧重"嵌套访问保护",避免同一任务的自我死锁。
实际使用时需根据场景选择,避免滥用(如用Mutex做同步会浪费优先级继承的开销,用二进制信号量做互斥可能导致优先级反转)。