FreeRTOS——流和消息缓冲区

FreeRTOS 基础系列文章

 基本对象
  FreeRTOS——任务
  FreeRTOS——队列
  FreeRTOS——信号量
  FreeRTOS——互斥量
  FreeRTOS——任务通知
  FreeRTOS——流和消息缓冲区
  FreeRTOS——软件定时器
  FreeRTOS——事件组

 内存管理
  FreeRTOS——静态与动态内存分配
  FreeRTOS——堆内存管理
  FreeRTOS——栈溢出保护

 代码组织
  FreeRTOS——源代码组织
  FreeRTOS——创建新的项目
  FreeRTOS——配置文件


流缓冲区和消息缓冲区

从 FreeRTOS V10.0.0 可用

流缓冲区是 RTOS 任务到 RTOS 任务,中断到任务通信原语。与大多数其他 FreeRTOS 通信原语不同,它们针对单读取者单写入者场景进行了优化。例如将数据从中断服务程序传递到任务,或在双核 CPU 上从一个微控制器内核传递到另一个内核。数据通过复制传递 —— 数据由发送方复制到缓冲区中,并通过读取从缓冲区中复制出来。

流缓冲区传递一个连续的字节流。消息缓冲区传递大小可变但离散的消息。消息缓冲区使用流缓冲区进行数据传输

流缓冲区一次可以写入任意数量的字节,一次可以读取任意数量的字节,因此是连续的。消息缓冲区每次可以写入变长的消息,但每次读取必须是读取完整的一条消息,因此是离散的。每条消息的长度会在消息之前被写入消息缓冲区,因此实际所需的RAM空间比最大的消息长度还要多一点点,在设计消息缓冲区大小时需要注意这一点。

重要说明:在 FreeRTOS 对象中独一无二的是,流缓冲区实现(消息缓冲区实现也是如此,因为消息缓冲区构建在流缓冲区之上)假定只有一个任务或中断会写入缓冲区(写入者),并且只有一个任务或中断将从缓冲区(读取者)中读取。写入者和读取者不是同一个任务或中断也是安全的,但是,与其他 FreeRTOS 对象不同,有多个不同的写入者或多个不同的读取者是不安全的。如果要有多个不同的写入者,那么应用程序设计者必须将每次调用写入API函数(如 xStreamBufferSend())放在一个临界区中,并使用发送阻塞时间为0。同样,如果有多个不同的读取者,那么应​​用程序设计者必须将每次调用读取 API 函数(例如xStreamBufferReceive())放在临界区中并使用接收阻塞时间为0


流缓冲区

流缓冲区允许将字节流从中断服务程序传递到任务,或从一个任务传递到另一个任务。字节流可以是任意长度,不一定有开始或结束。一次可以写入任意数量的字节,一次可以读取任意数量的字节。数据通过复制传递 —— 数据由发送方复制到缓冲区中,并通过读取从缓冲区中复制出来。

通过在构建中包含FreeRTOS/source/stream_buffer.c源文件来启用流缓冲区功能。

流缓冲区实现使用直接到任务通知。因此,调用那些将调用任务置于阻塞状态的流缓冲区 API 函数可以更改调用任务的通知状态和值。

阻塞读取和触发级别

xStreamBufferReceive()用于在 RTOS 任务中读取流缓冲区的数据。 xStreamBufferReceiveFromISR()) 用于在中断服务程序 (ISR) 中读取流缓冲区的数据。

xStreamBufferReceive() 允许指定阻塞时间。如果指定了非零的阻塞时间,当任务使用xStreamBufferReceive()从流缓冲区读取数据时,如果流缓冲区恰好为空,则该任务将被置于阻塞状态(因此它不消耗任何 CPU 时间并且其他任务可以运行)直到流缓冲区中有指定数量的数据可用,或者阻塞时间到期。等待数据的任务被移出阻塞状态前流缓冲区中必须包含的数据量被称为称为流缓冲区的触发级别。例如:

  • 如果任务在读取触发级别为 1 的空流缓冲区时被阻塞,则当单个字节写入缓冲区或任务的阻塞时间到期时,任务将被解除阻塞。
  • 如果任务在读取触发级别为 10 的空流缓冲区时被阻塞,则在流缓冲区包含至少 10 个字节或任务的阻塞时间到期之前,任务不会被解除阻塞。

如果在达到触发级别之前读取任务的阻塞时间到期,则任务仍然会接收到实际可用的字节。

注意

  • 将触发级别设置为 0 是无效的。尝试将触发级别设置为 0 将导致使用触发级别 1。
  • 指定大于流缓冲区大小的触发级别也是无效的。

流缓冲区的触发级别最初是在创建流缓冲区时设置的 ,然后可以使用xStreamBufferSetTriggerLevel() API 函数进行更改。

阻塞写入

xStreamBufferSend() 用于将数据从 RTOS 任务发送到流缓冲区。 xStreamBufferSendFromISR() 用于将数据从中断服务程序 (ISR) 发送到流缓冲区。

如果在任务使用 xStreamBufferSend() 写入恰好已满的流缓冲区时指定了非零阻塞时间,则该任务将被置于阻塞状态(因此它不消耗任何 CPU 时间并且其他任务可以运行)直到流缓冲区中有可用空间,或者阻塞时间到期。

发送完成和接收完成宏

sbSEND_COMPLETED()(和 sbSEND_COMPLETED_FROM_ISR())

sbSEND_COMPLETED() 是一个宏,在将数据写入流缓冲区时调用(在 FreeRTOS API 函数内部)。它接受一个参数,即要更新数据的流缓冲区的句柄

sbSEND_COMPLETED() 检查流缓冲区上是否有任务被阻塞以等待数据,如果是,则将该任务从阻塞状态中移除。

应用程序设计者可以通过在 FreeRTOSConfig.h 中提供他们自己的 sbSEND_COMPLETED() 实现来更改此默认行为。当流缓冲区用于在多核处理器上的内核之间传递数据时,这很有用。在这种情况下,可以实现 sbSEND_COMPLETED() 以在其他 CPU 内核中生成中断,然后中断服务程序可以使用 xStreamBufferSendCompletedFromISR() API 函数检查并在必要时解除阻塞等待数据的任务。

sbRECEIVE_COMPLETED()(和 sbRECEIVE_COMPLETED_FROM_ISR())

sbRECEIVE_COMPLETED()sbSEND_COMPLETED() 的接收等效项。当从流缓冲区读取数据时,它会被调用(在 FreeRTOS API 函数内部)。该宏检查是否有任务阻塞在流缓冲区上等待缓冲区内的可用空间,如果有,则从阻塞状态中删除任务。与 sbSEND_COMPLETED() 一样,sbRECEIVE_COMPLETED() 的默认行为通过在 FreeRTOSConfig.h 中提供替代实现。


消息缓冲区

消息缓冲区允许将可变长度的离散消息从中断服务程序传递到任务,或从一个任务传递到另一个任务。例如,长度为 10、20 和 123 字节的消息都可以写入和读取同一个消息缓冲区。与使用流缓冲区时不同,10 字节的消息只能作为 10 字节的消息读出,而不能作为单个字节读出 (此所谓离散) 。消息缓冲区建立在流缓冲区之上(即,它们使用流缓冲区实现)。

数据以复制的方式通过消息缓冲区传递 —— 数据由发送方复制到缓冲区中,并通过读取从缓冲区中复制出来。

通过在构建中包含FreeRTOS/source/stream_buffer.c源文件来启用消息缓冲区功能。

消息缓冲区实现使用直接到任务通知。因此,调用那些将调用任务置于阻塞状态的消息缓冲区 API 函数可以更改调用任务的通知状态和值。

调整消息缓冲区的大小

为了使消息缓冲区能够处理可变大小的消息,每条消息的长度在消息本身之前写入消息缓冲区(这在 FreeRTOS API 函数内部发生)。长度存储在一个变量中,其类型由 FreeRTOSConfig.h 中的 configMESSAGE_BUFFER_LENGTH_TYPE 常量设置。如果未定义,configMESSAGE_BUFFER_LENGTH_TYPE 默认为 size_t 类型。size_t 在 32 位架构上通常为 4 字节。因此,例如,configMESSAGE_BUFFER_LENGTH_TYPE 为 4 字节时,将 10 字节的消息写入消息缓冲区实际上会消耗 14 字节的缓冲区空间。同样,将 100 字节的消息写入消息缓冲区实际上将存储 104 字节的缓冲区空间。

阻塞读取和写入

xMessageBufferReceive() 用于在 RTOS 任务中读取消息缓冲区的数据。 xMessageBufferReceiveFromISR() 用于在中断服务程序 (ISR) 中读取消息缓冲区的数据。 xMessageBufferSend() 用于将数据从 RTOS 任务发送到消息缓冲区。 xMessageBufferSendFromISR() 用于从中断服务程序 (ISR) 向消息缓冲区发送数据。

如果在任务使用 xMessageBufferReceive() 从恰好为空的消息缓冲区读取时指定了非零阻塞时间,则该任务将被置于阻塞状态(因此它不消耗任何 CPU 时间并且其他任务可以运行)直到消息缓冲区中的数据可用,或者阻塞时间到期。

如果在任务使用 xMessageBufferSend() 写入恰好已满的消息缓冲区时指定了非零阻塞时间,则该任务将被置于阻塞状态(因此它不消耗任何 CPU 时间并且其他任务可以运行)直到消息缓冲区中有可用空间,或者阻塞时间到期。

发送完成和接收完成宏

由于消息缓冲区建立在流缓冲区上,因此 sbSEND_COMPLETE()sbRECEIVE_COMPLETE() 宏的行为与描述流缓冲区的小节上描述的完全相同 。

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
FreeRTOS 中,串口接收缓冲区通常是由一个队列来实现的。具体实现方式如下: 1. 首先定义一个队列句柄,用于保存接收到的数据: ```c QueueHandle_t uart_queue; ``` 2. 在任务创建或初始化时,创建该队列: ```c uart_queue = xQueueCreate(UART_QUEUE_LENGTH, sizeof(uint8_t)); ``` 其中,UART_QUEUE_LENGTH 是队列长度,sizeof(uint8_t) 是队列元素大小。 3. 在串口接收中断中,将接收到的数据存入队列: ```c void UART_IRQHandler(void) { uint8_t data = UART_ReceiveData(UART); BaseType_t xHigherPriorityTaskWoken = pdFALSE; xQueueSendFromISR(uart_queue, &data, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } ``` 其中,xQueueSendFromISR() 函数用于向队列中发送数据,&data 是数据指针,&xHigherPriorityTaskWoken 是一个指针,用于指示是否有更高优先级的任务需要立即运行。 4. 在读取串口数据的任务中,从队列中读取数据: ```c void uart_task(void *pvParameters) { uint8_t data; while (1) { if (xQueueReceive(uart_queue, &data, portMAX_DELAY)) { // 处理接收到的数据 } } } ``` 其中,xQueueReceive() 函数用于从队列中接收数据,&data 是数据指针,portMAX_DELAY 表示一直等待队列中有数据才返回。 通过以上步骤,就可以实现 FreeRTOS 中的串口接收缓冲区了。在读取数据任务中,只需要不断地从队列中读取数据,就可以获取串口接收缓冲区中的数据了。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值