3、结合STM32CubeMX学习FreeRTOS实时操作系统——队列

目录

前言

1、FreeRTOS队列核心概念与特性

2、队列的常用函数API

3、队列的创建和常见配置

4、在任务中使用队列

[5、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~](#5、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~)


前言

之前的文章已经介绍了《如何使用STM32CubeMX在项目中添加FreeRTOS操作系统》和《FreeRTOS任务的创建和使用》,本文开始介绍FreeRTOS中队列相关的知识。

1、FreeRTOS队列核心概念与特性

FreeRTOS队列(Queue)是任务间通信的基础组件,使用队列可以在一个任务中获取数据(例如传感器数据),在另一个或多个任务中处理数据,实现线程安全的数据传输,队列具有以下核心特性:

特性 说明
​****先进先出(FIFO) 数据按发送顺序排列,最早入队的数据最先被读取
​****线程安全 提供互斥保护,允许多任务并发访问无需额外同步
​****阻塞机制 当队列空/满时,任务可选择等待数据/空间可用
​ISR****支持 通过FromISR后缀API支持中断服务程序操作
​****多数据类型 支持传输值类型(struct/uint等)或指针类型(void*)
​****可配置深度 创建时指定可存储的最大项目数量(1~65535)

2、队列的常用函数API

操作 API 函数 参数说明 返回值
​****发送数据 xQueueSend() 尾部添加,阻塞等待空间 pdPASS/pdFAIL
​****紧急发送 xQueueSendToFront() 头部插入(插队) 同上
​****覆盖发送 xQueueOverwrite() 队列满时覆盖最旧数据 总是成功
​****接收数据 xQueueReceive() 头部取出并移除数据 pdPASS/pdFAIL
​****查看数据 xQueuePeek() 头部查看但不移除数据 同上

如果在中断程序中使用队列的话需要使用特定的带尾缀FromISR的函数,可以保证在中断函数中安全的进行队列操作。如下所示:

cpp 复制代码
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,

                             const void *pvItemToQueue,

                             BaseType_t *pxHigherPriorityTaskWoken);

其中,pxHigherPriorityTaskWoken是输出参数,若因发送唤醒了更高优先级任务则置为pdTRUE,当pxHigherPriorityTaskWoken = pdTRUE时,使用后必须调用portYIELD_FROM_ISR()函数触发上下文切换,否则出了中断程序后会出现任务调度异常。

3、队列的创建和常见配置

队列的配置原则如下表所示:

参数 优化建议
​****深度(Length) 避免过深(浪费内存),过浅(频繁阻塞)
​****项目大小(Item Size) 大于20字节时建议传递指针(启用configSUPPORT_DYNAMIC_ALLOCATION)
​****阻塞时间 生产者和消费者使用不同的超时策略,根据实际应用场景设置

队列的选型指南如下表所示 :

通信需求 推荐组件 特点比较
数据传输(带内容) 标准队列 支持任意数据结构
轻量级通知 队列(零字节项目) 比二进制信号量更节省内存
高频小数据 流缓冲(Stream Buffer) 无项目边界,更高吞吐
结构化流 消息缓冲(Message Buffer) 带长度标识,支持变长数据
广播通信 任务通知(Task Notificaiton) 点对点,零拷贝,最高效

由于队列常用在任务之间的通信,因此系统要配置两个任务来进行队列通信演示:

4、在任务中使用队列

本例程的两个任务中,一个任务负责记录数据,模拟传感器数据写入,另一个任务读出数据,模拟数据处理,通过Keil仿真中的变量内存查看窗口或者硬件LED灯闪烁状态可观察到代码实现的功能现象。

cpp 复制代码
/* 创建队列 */
osMessageQStaticDef(myQueue01, 16, uint16_t, myQueue01Buffer, &myQueue01ControlBlock);
myQueue01Handle = osMessageCreate(osMessageQ(myQueue01), NULL);


/* 创建任务2 */
osThreadStaticDef(myTask02, StartTask02, osPriorityBelowNormal, 0, 128, myTask02Buffer, &myTask02ControlBlock);
myTask02Handle = osThreadCreate(osThread(myTask02), NULL);


/* 创建任务3 */
osThreadStaticDef(myTask03, StartTask03, osPriorityNormal, 0, 128, myTask03Buffer, &myTask03ControlBlock);
myTask03Handle = osThreadCreate(osThread(myTask03), NULL);


/* 任务2的API接口,在里面实现具体功能 */
void StartTask02(void const * argument)
{
  /* USER CODE BEGIN StartTask02 */
    for( ; ; )
    {
        static uint32_t u32Rec = 0;    //创建队列接收缓存

        //队列接收数据,如果队列空,会进入阻塞状态等待队列中数据就绪,直到超时
        if( xQueueReceive(myQueue01Handle, &u32Rec, pdMS_TO_TICKS(50)) != pdTRUE ) 
        {
            //接收失败处理
        }
        else    //如果队列数据接收成功
        {
            if(u32Rec % 2 == 0)    //根据接收到的数据进行分类处理
                LED0_ON;
            else
                LED0_OFF;
        }
        osDelay(200);   //延时,任务进入阻塞态
  }
  /* USER CODE END StartTask02 */
}


/* 任务3的API接口,在里面实现具体功能 */
void StartTask03(void const * argument)
{
  /* USER CODE BEGIN StartTask03 */
    for( ; ; )
    {
        static uint32_t u32Numbers = 0;    //创建数据发送变量,模拟传感器数据

        //队列发送数据,如果队列满,会进入阻塞状态等队列中出现数据空位,如果等待超时,数据丢弃
        if( xQueueSend(myQueue01Handle, &u32Numbers, pdMS_TO_TICKS(100)) != pdTRUE)    
        {
            //发送数据失败处理
        }
        else
        {
            //数据发送成功处理
        }
        u32Numbers++;    //模拟传感器数据变化
        osDelay(50);     //延时,任务进入阻塞态
}
  /* USER CODE END StartTask03 */
}

5、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~

相关推荐
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
Lester_11015 天前
STM32霍尔传感器输入口设置为复用功能输入口时,还能用GPIO函数直接读取IO的状态吗
stm32·单片机·嵌入式硬件·电机控制
LCG元5 天前
低功耗显示方案:STM32L0驱动OLED,动态波形绘制与优化
stm32·嵌入式硬件·信息可视化
三佛科技-187366133975 天前
120W小体积碳化硅电源方案(LP8841SC极简方案12V10A/24V5A输出)
单片机·嵌入式硬件
z20348315205 天前
STM32F103系列单片机定时器介绍(二)
stm32·单片机·嵌入式硬件
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
古译汉书5 天前
【IoT死磕系列】Day 7:只传8字节怎么控机械臂?学习工业控制 CANopen 的“对象字典”(附企业级源码)
数据结构·stm32·物联网·http