FreeRTOS(九)——队列

FreeRTOS提供了一种称为“队列”的机制,用于在多任务环境中安全地传递信息。队列采用FIFO原则,支持数据存储、多任务访问,并允许在出队和入队时设置阻塞时间。队列的创建可通过静态或动态方式,且包含多种操作函数,如发送消息、上锁解锁及读取信息等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在编写项目应用时,常常会遇到一个任务和另一个任务进行“沟通交流”的情况,在没有操作系统时,全局变量可以解决这个问题,但是如果在使用操作系统的应用中用全部变量来传递信息就会涉及到“资源管理”的问题,而且全局变量不易维护,往往逻辑复杂的程序中,无法追踪全局变量被谁使用或被谁更改。 FreeRTOS对此提供一个叫做“队列”的机制。
本文分为如下几部分:

  • 队列简介
  • 队列结构体
  • 队列创建
  • 向队列发送消息
  • 队列上锁和解锁
  • 从队列读取信息

队列简介

数据存储

队列采用先进先出(FIFO)的存储缓冲机制。数据发送到队列中会导致数据拷贝,也就是将要发送的数据拷贝到队列中,这意味着在队列中存储的是数据原始值,也就是值传递。虽然这样会浪费一点时间,但是一旦将消息发送到队列中原始的数据缓冲区就可以删除或覆写,FreeRTOS中队列传递消息虽然使用的数据拷贝,但也可以使用引用来传递消息。

多任务访问

任何任务都可以向队列中发送消息,或从队列中提取消息。

出队阻塞

当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任务从队列中读取消息无效的时候任务阻塞的时间。
比如说队列Q是空的,这是A任务来读取,此时A又三种选择:

  1. 不等待
  2. 等待一段时间
  3. 死等

选哪一个由阻塞时间决定

入队阻塞

入队是指向队列中发送消息,将消息加入到队列中,和出队阻塞一样,当一个任务向队列中发送消息的话也可以设置阻塞时间。

队列操作过程




这里写图片描述

队列结构体

结构体定义在queue.c中:

typedef struct QueueDefinition
{
   
	int8_t *pcHead;					
	int8_t *pcWriteTo;			
	{
   
### FreeRTOS 中串口通信的消息队列实现 在嵌入式开发中,FreeRTOS 的消息队列机制能够有效地处理来自不同任务之间的数据交换。对于串口通信而言,通常涉及到接收和发送两个方向上的数据流控制。 #### 创建消息队列 为了便于管理和调度,在初始化阶段应当创建专门用于保存接收到的数据以及待发送指令的消息队列: ```c // 定义全局变量来保存队列句柄 QueueHandle_t xRxedChars; // 接收字符缓冲区 QueueHandle_t xStringsToSend; // 发送字符串缓冲区 void vInitQueues(void){ // 初始化接收队列, 可容纳20个unsigned char类型的元素 xRxedChars = xQueueCreate(20, sizeof(unsigned char)); // 初始化发送队列, 存储指向char*指针的数组 xStringsToSend = xQueueCreate(10, sizeof(char *)); } ``` 上述代码片段展示了如何利用 `xQueueCreate()` 来建立两个不同类型的消息队列实例[^2]。第一个参数表示该队列为最大可存储项的数量;第二个参数则指定每条消息所占字节数大小。 #### 处理串口中断事件 当发生UART中断时(比如新字符到达),应立即将这些信息推送到相应的消息队列里等待后续的任务去消费它们: ```c void UART_IRQHandler(void){ portBASE_TYPE xHigherPriorityWoken = pdFALSE; unsigned char receivedChar; while(UART_GetFlagStatus(UART_FLAG_RXNE)){ /* 获取新的输入字符 */ receivedChar = UART_ReceiveData(); /* 将其放入到接收队列中 */ xQueueSendToBackFromISR(xRxedChars,&receivedChar,&xHigherPriorityWoken); } /* 如果有更高优先级的任务被唤醒,则在此处执行上下文切换 */ portYIELD_FROM_ISR(xHigherPriorityWoken); } ``` 这段C语言代码实现了从硬件层面上捕获到来自外部设备传来的单个ASCII码值,并通过调用`xQueueSendToBackFromISR()`将其安全地加入到之前定义好的接收队列当中[^3]。注意这里的最后一个布尔型参数用来指示是否有更高级别的线程因为这次操作而变得就绪状态,从而可能触发立即性的任务重排。 #### 构建消费者/生产者模型 最后一步就是编写具体的应用逻辑——即所谓的“生产者”负责准备要传输的信息,“消费者”则是实际完成物理I/O动作的部分: ```c static void prvUartTxTask(void *pvParameters){ const TickType_t xBlockTime = pdMS_TO_TICKS(200UL); // 设置阻塞时间长度 for (;;){ char *pcString; if(pdPASS == xQueueReceive(xStringsToSend,(void *)&pcString,xBlockTime)){ // 执行具体的发送过程... printf("%s", pcString); // 假设我们使用动态分配内存的方式构建了这个字符串, // 则在这里释放它以防止泄露。 vPortFree(pcString); } } } static void prvUartRxTask(void *pvParameters){ static char cInputBuffer[8]; size_t uxIndex = 0; for (;;){ unsigned char receivedChar; if (pdTRUE == xQueueReceive(xRxedChars, &receivedChar, portMAX_DELAY)) { // 对于简单回显功能来说可以直接打印出来 putchar(receivedChar); // 或者收集一系列连续键入形成完整的命令行再做进一步解析... if ('\r' != receivedChar && '\n' != receivedChar) { cInputBuffer[uxIndex++] = receivedChar; if (sizeof(cInputBuffer)-1 == uxIndex || ' '==receivedChar ) { cInputBuffer[uxIndex]='\0'; // 进行必要的业务处理 uxIndex=0; } } } } } ``` 以上两段伪代码分别代表了一个典型的发送端(`prvUartTxTask`) 和接收端 (`prvUartRxTask`)工作流程。前者不断尝试从未满载的状态下获取下一个待发报文并输出之;后者持续监听着已填充完毕的新鲜资料以便即时响应用户的交互请求[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值