3、结合STM32CubeMX学习FreeRTOS实时操作系统——队列
目录
前言
1、FreeRTOS队列核心概念与特性
2、队列的常用函数API
3、队列的创建和常见配置
4、在任务中使用队列
5、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~
前言
之前的文章已经介绍了《如何使用STM32CubeMX在项目中添加FreeRTOS操作系统》和《FreeRTOS任务的创建和使用》,本文开始介绍FreeRTOS中队列相关的知识。
1、FreeRTOS队列核心概念与特性
FreeRTOS队列(Queue)是任务间通信的基础组件,使用队列可以在一个任务中获取数据(例如传感器数据),在另一个或多个任务中处理数据,实现线程安全的数据传输,队列具有以下核心特性:
特性 | 说明 |
先进先出(FIFO) | 数据按发送顺序排列,最早入队的数据最先被读取 |
线程安全 | 提供互斥保护,允许多任务并发访问无需额外同步 |
阻塞机制 | 当队列空/满时,任务可选择等待数据/空间可用 |
ISR支持 | 通过 |
多数据类型 | 支持传输值类型(struct/uint等)或指针类型(void*) |
可配置深度 | 创建时指定可存储的最大项目数量(1~65535) |
2、队列的常用函数API
操作 | API函数 | 参数说明 | 返回值 |
发送数据 |
| 尾部添加,阻塞等待空间 | pdPASS/pdFAIL |
紧急发送 |
| 头部插入(插队) | 同上 |
覆盖发送 |
| 队列满时覆盖最旧数据 | 总是成功 |
接收数据 |
| 头部取出并移除数据 | pdPASS/pdFAIL |
查看数据 |
| 头部查看但不移除数据 | 同上 |
如果在中断程序中使用队列的话,需要使用特定的带尾缀FromISR的函数,可以保证在中断函数中安全的进行队列操作。如下所示:
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);
其中,pxHigherPriorityTaskWoken是输出参数,若因发送唤醒了更高优先级任务则置为pdTRUE,当pxHigherPriorityTaskWoken = pdTRUE时,使用后必须调用portYIELD_FROM_ISR()函数触发上下文切换,否则出了中断程序后会出现任务调度异常。
3、队列的创建和常见配置
队列的配置原则如下表所示:
参数 | 优化建议 |
深度(Length) | 避免过深(浪费内存),过浅(频繁阻塞) |
项目大小(Item Size) | 大于20字节时建议传递指针(启用 |
阻塞时间 | 生产者和消费者使用不同的超时策略,根据实际应用场景设置 |
队列的选型指南如下表所示 :
通信需求 | 推荐组件 | 特点比较 |
数据传输(带内容) | 标准队列 | 支持任意数据结构 |
轻量级通知 | 队列(零字节项目) | 比二进制信号量更节省内存 |
高频小数据 | 流缓冲(Stream Buffer) | 无项目边界,更高吞吐 |
结构化流 | 消息缓冲(Message Buffer) | 带长度标识,支持变长数据 |
广播通信 | 任务通知(Task Notificaiton) | 点对点,零拷贝,最高效 |
由于队列常用在任务之间的通信,因此系统要配置两个任务来进行队列通信演示:
4、在任务中使用队列
本例程的两个任务中,一个任务负责记录数据,模拟传感器数据写入,另一个任务读出数据,模拟数据处理,通过Keil仿真中的变量内存查看窗口或者硬件LED灯闪烁状态可观察到代码实现的功能现象。
/* 创建队列 */
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;elseLED0_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 */
}