FreeRTOS 二值信号量使用示例-笔记
系统设计背景
演示二值信号量在周期性ADC数据采集中的应用。在定时器中断服务程序中,每500毫秒进行一次ADC数据采集,转换结果被写入缓存变量并释放信号量。任务持续尝试获取信号量,成功后读取ADC转换结果并显示在LCD上。
二值信号量的核心价值
二值信号量本质上是一个长度为1的队列,只有0和1两种状态。它在系统中扮演着"事件通知"的角色,当ADC转换完成时释放信号量,显示任务获取信号量后执行数据处理,实现"数据就绪"的同步机制。
系统架构设计
系统采用以下架构实现:
- 定时器触发ADC转换
- ADC转换完成后触发中断
- 中断服务程序释放二值信号量
- 显示任务等待信号量,获取后处理数据
详细设计
系统频率设置

定时器定时触发ADC采样
在"Trigger event selection"部分,选择更新事件"update event"。

外部触发转换源设置为定时器3触发

要开启ADC的NVIC

创建一个二值信号量

/* 进程间同步,程序跑到ADC中断处肯定是有数据的,刚好释放二值信号量。到了显示任务,刚好获取到信号量数,数据已准备就绪,可以进行显示。 */
代码实现详解

1. 二值信号量的创建
c
/* 创建二值信号量 */
BinSem_DataReadyHandle = osSemaphoreNew(1, 1, &BinSem_DataReady_attributes);
这里创建了一个初始值为1的二值信号量,表示"数据就绪"状态。
2. 定时器配置
在STM32CubeMX中配置定时器,设置"Trigger event selection"为"update event",使定时器每500毫秒触发一次ADC转换。
3. 中断服务程序处理
c
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC1)
{
/* 获取ADC转换结果并保存到全局变量 */
adc_value = HAL_ADC_GetValue(hadc);
/* 在中断服务例程中释放二值信号量,通知任务数据已准备就绪 */
BaseType_t highTaskWoken = pdFALSE;
if (BinSem_DataReadyHandle != NULL)
{
/*xSemaphoreGiveFromISR 用于释放一个信号量,从而通知等待该信号量的任务可以继续执行。它支持二进制信号量和计数信号量,但不支持互斥信号量(Mutex).返回值 pdTRUE: 表示信号量成功释放。*/
xSemaphoreGiveFromISR(BinSem_DataReadyHandle, &highTaskWoken);
portYIELD_FROM_ISR(highTaskWoken); // 申请一次任务调度
}
}
}
在ADC转换完成中断中,将转换结果保存到全局变量adc_value,并释放二值信号量。xSemaphoreGiveFromISR是专门用于中断中释放信号量的API,portYIELD_FROM_ISR用于判断是否需要进行任务调度。
4. 显示任务实现
c
void AppTask_Show(void *argument)
{
for (;;)
{
/* 等待数据准备就绪信号量,获取成功后显示ADC数据 */
if (xSemaphoreTake(BinSem_DataReadyHandle, portMAX_DELAY) == pdTRUE)
{
uint8_t temp_str[20];
/* 显示ADC原始数值 */
sprintf(temp_str, "ADC Value = %d ", adc_value);
lcd_show_str(10, 10 + 1 * 30, 24, temp_str, RED);
/* 显示转换后的电压值,ADC参考电压3.3V,12位精度 */
sprintf(temp_str, "Voltage = %d mV ", adc_value * 3300 >> 12);
lcd_show_str(10, 10 + 2 * 30, 24, temp_str, RED);
}
}
}
显示任务持续等待二值信号量,当信号量被释放后,任务获取信号量并读取adc_value,在LCD上显示ADC原始值和转换后的电压值。
工作流程分析
- 定时器触发:定时器每500毫秒触发一次,启动ADC转换
- ADC转换:ADC开始转换模拟信号
- 转换完成:ADC转换完成后触发中断
- 信号量释放:中断服务程序中释放二值信号量
- 任务唤醒 :显示任务获取信号量,从
adc_value读取数据 - 数据显示:在LCD上显示采集到的ADC值和计算出的电压值
优势分析
- 资源高效利用:显示任务在等待信号量时处于阻塞状态,不会占用CPU资源
- 实时响应:数据采集完成后立即通知显示任务,实现快速响应
- 代码简洁:通过二值信号量实现任务间同步,避免了轮询和全局标志位的使用
- 中断安全 :使用
xSemaphoreGiveFromISR和portYIELD_FROM_ISR确保中断安全