5.2 FreeRTOS 二值信号量使用示例-笔记

FreeRTOS 二值信号量使用示例-笔记

系统设计背景

演示二值信号量在周期性ADC数据采集中的应用。在定时器中断服务程序中,每500毫秒进行一次ADC数据采集,转换结果被写入缓存变量并释放信号量。任务持续尝试获取信号量,成功后读取ADC转换结果并显示在LCD上。

二值信号量的核心价值

二值信号量本质上是一个长度为1的队列,只有0和1两种状态。它在系统中扮演着"事件通知"的角色,当ADC转换完成时释放信号量,显示任务获取信号量后执行数据处理,实现"数据就绪"的同步机制。

系统架构设计

系统采用以下架构实现:

  1. 定时器触发ADC转换
  2. ADC转换完成后触发中断
  3. 中断服务程序释放二值信号量
  4. 显示任务等待信号量,获取后处理数据

详细设计

系统频率设置


定时器定时触发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原始值和转换后的电压值。

工作流程分析

  1. 定时器触发:定时器每500毫秒触发一次,启动ADC转换
  2. ADC转换:ADC开始转换模拟信号
  3. 转换完成:ADC转换完成后触发中断
  4. 信号量释放:中断服务程序中释放二值信号量
  5. 任务唤醒 :显示任务获取信号量,从adc_value读取数据
  6. 数据显示:在LCD上显示采集到的ADC值和计算出的电压值

优势分析

  1. 资源高效利用:显示任务在等待信号量时处于阻塞状态,不会占用CPU资源
  2. 实时响应:数据采集完成后立即通知显示任务,实现快速响应
  3. 代码简洁:通过二值信号量实现任务间同步,避免了轮询和全局标志位的使用
  4. 中断安全 :使用xSemaphoreGiveFromISRportYIELD_FROM_ISR确保中断安全
相关推荐
我的老子姓彭34 分钟前
QT6开发笔记
笔记
LO嘉嘉VE42 分钟前
学习笔记二十二:神经网络的几起几落
笔记·神经网络·学习
wdfk_prog1 小时前
[Linux]学习笔记系列 -- [block]fops
linux·笔记·学习
C语言不精1 小时前
一种在 ESP32-S3 上取巧的清晰度检测方案
c语言·stm32·嵌入式硬件·学习
丝斯20111 小时前
AI学习笔记整理(23)—— AI核心技术(深度学习7)
人工智能·笔记·学习
dlwlrma_5161 小时前
STM32使用TIM定时触发ADC通过HAL库实现采样 模拟ADC使用DMA数据传输时发生OVR溢出和恢复DMA
stm32
三块可乐两块冰1 小时前
【第二十二周】机器学习笔记二十
人工智能·笔记·机器学习
Bin二叉1 小时前
南京大学cpp复习——第二部分(继承)
开发语言·c++·笔记·学习
empti_1 小时前
《大规模 3D 城市布局的语义与结构引导可控生成》翻译
论文阅读·笔记