ESP32 FreeRTOS-消息缓冲区(13)

提示:好记性不如烂笔头。本博客作为学习笔记,有错误的地方希望指正

前言:

  参考资料:FreeRTOS API参考
  streambuffer和Messagebuffer最大的区别主要有两点。

  1. streambuffer一次只能接收一条数据
  2. streambuffer中接收函数xMessageBufferReceive中的接收数组小于发送数据的长度的时候,不能接收数据。

  从名字上就可以知道Messagebuffer的用法大概率就是用来接收消息的,streambuffer的话可以用来接收数据流例如音频数据流等等流媒体数据。

一、xMessageBufferCreate()、xMessageBufferCreateWithCallback()

API原型:

MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );

MessageBufferHandle_t xMessageBufferCreateWithCallback( 
                          size_t xBufferSizeBytes,
                          StreamBufferCallbackFunction_t pxSendCompletedCallback,
                          StreamBufferCallbackFunction_t pxReceiveCompletedCallback );

  使用动态分配的内存新建消息缓冲区。 消息缓冲区会在每次发送和接收操作完成后执行回调。使用 xMessageBufferCreate() API 创建的消息缓冲区共享相同的发送和接收完成回调函数,这些回调函数通过 sbSEND_COMPLETED() 和 sbRECEIVE_COMPLETED() 宏定义。使用 xMessageBufferCreateWithCallback() API 创建的消息缓冲区 可以有自己唯一的发送和接收完成回调函数。请参阅 xMessageBufferCreateStatic() and xMessageBufferCreateStaticWithCallback() 获取使用静态分配内存(在编译时分配的内存)的相应版本。
  configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1 或未定义,xMessageBufferCreate() 才可用。此外,configUSE_SB_COMPLETED_CALLBACK 必须在 FreeRTOSConfig.h 中设置为 1,xMessageBufferCreateWithCallback() 才可用。
  将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中即可启用消息缓冲区功能 (因为消息缓冲区使用流缓冲区)。
参数:
  xBufferSizeBytes 消息缓冲区在任何时候能够容纳的字节(而非消息)总数。当消息写入消息缓冲区时, 同时也会写入额外的 sizeof( size_t ) 字节以存储消息的长度。 sizeof( size_t ) 在 32 位架构上的大小通常是 4 个字节,因此在大多数 32 位架构中,10 字节的消息将占用 14 字节的 消息缓冲区空间。
  pxSendCompletedCallback 消息写入消息缓冲区时调用的回调函数。如果参数为 NULL, 则使用 sbSEND_COMPLETED 宏所提供的默认实现。发送完成回调函数必须 具有 StreamBufferCallbackFunction_t 定义的原型,即

void vSendCallbackFunction( MessageBufferHandle_t xMessageBuffer,
                            BaseType_t xIsInsideISR,
                            BaseType_t * const pxHigherPriorityTaskWoken );

  pxReceiveCompletedCallback 从消息缓冲区读取消息时调用的回调函数。如果参数为 NULL, 则使用 sbRECEIVE_COMPLETED 宏所提供的默认实现。接收完成回调函数必须 具有 StreamBufferCallbackFunction_t 定义的原型,即

void vReceiveCallbackFunction( MessageBufferHandle_t xMessageBuffer,
                               BaseType_t xIsInsideISR,
                               BaseType_t * const pxHigherPriorityTaskWoken );

返回值:
  如果返回 NULL,则说明因为没有足够的堆内存可供 FreeRTOS 分配消息缓冲区的数据结构体和存储区域,所以无法创建消息缓冲区。如果返回非 NULL 值,则表示消息 缓冲区已成功创建,返回值应该作为所创建消息缓冲区的句柄来存储。
用法示例:

void vSendCallbackFunction ( MessageBufferHandle_t xMessageBuffer,
                            BaseType_t xIsInsideISR,
                            BaseType_t * const pxHigherPriorityTaskWoken )
{
    
    
    /* 在这里插入代码,当一条消息被写入到
     * 消息缓冲区。
     * 当消息缓冲区被用来在多核处理器之间传递消息时,这很有用。
     * 这在多核处理器上的核心之间传递消息时非常有用。在这种情况下,这个回调
     * 可以实现在另一个CPU核中产生一个中断。
     * 然后,中断的服务程序可以使用
     * xMessageBufferSendCompletedFromISR() API函数来检查,如果有必要的话
     * 必要时解除对正在等待消息的任务的封锁。*/
}

void vReceiveCallbackFunction( MessageBufferHandle_t xMessageBuffer,
                               BaseType_t xIsInsideISR,
                               BaseType_t * const pxHigherPriorityTaskWoken )
{
    
    
    /* 在此插入代码,当从消息缓冲区中读取消息时被调用。
     * 缓冲区。
     * 当消息缓冲区被用来在多核之间传递消息时,这很有用。
     * 这在多核处理器上的核心之间传递消息时非常有用。在这种情况下,这个回调
     * 可以实现在另一个CPU核中产生一个中断。
     * 然后中断的服务程序可以使用
     * xMessageBufferReceiveCompletedFromISR() API函数来检查,如果有必要的话,可以解除对一个任务的封锁。
     * 必要时解除对一个等待发送消息的任务的封锁。*/
}

void vAFunction( void )
{
    
    
	MessageBufferHandle_t xMessageBuffer, xMessageBufferWithCallback;
	const size_t xMessageBufferSizeBytes = 100;

    /* 创建一个可以容纳100字节的消息缓冲区,并使用
     * 使用sbSEND_COMPLETED()和
     * sbRECEIVE_COMPLETED() 宏定义的函数作为发送和接收完成的
     * 的回调函数。用来保存消息的内存
     * 缓冲器结构和消息缓冲器中的数据是动态分配的。
     * 动态分配。*/
    xMessageBuffer = xMessageBufferCreate( xMessageBufferSizeBytes );
    
    if( xMessageBuffer == NULL )
    {
    
    
        /* 没有足够的堆内存空间可用于创建
         *消息缓冲区。*/
    }
    else
    {
    
    
        /* 消息缓冲区已成功创建,现在可以使用了。*/
    }
    
    /* 创建一个可以容纳100字节的消息缓冲区,并使用
     * 函数vSendCallbackFunction和vReceiveCallbackFunction
     *作为发送和接收完成的回调函数。内存
     * 用来保存消息缓冲区结构和数据的
     *消息缓冲区中的数据是动态分配的。*/
    xMessageBufferWithCallback = xMessageBufferCreateWithCallback( 
                                     xMessageBufferSizeBytes,
                                     vSendCallbackFunction,
                                     vReceiveCallbackFunction);
    if( xMessageBufferWithCallback == NULL )
    {
    
    
        /* 没有足够的堆内存空间可用于创建
         * 消息缓冲区。*/
    }
    else
    {
    
    
        /* 消息缓冲区已成功创建,现在可以使用了。*/
    }
}

二、xMessageBufferCreateStatic()、xMessageBufferCreateStaticWithCallback()

API原型:

MessageBufferHandle_t xMessageBufferCreateStatic(
                          size_t xBufferSizeBytes,
                          uint8_t *pucMessageBufferStorageArea,
                          StaticMessageBuffer_t *pxStaticMessageBuffer );

MessageBufferHandle_t xMessageBufferCreateStaticWithCallback(
                          size_t xBufferSizeBytes,
                          uint8_t *pucMessageBufferStorageArea,
                          StaticMessageBuffer_t *pxStaticMessageBuffer,
                          StreamBufferCallbackFunction_t pxSendCompletedCallback,
                          StreamBufferCallbackFunction_t pxReceiveCompletedCallback );

  使用静态分配的内存创建新的消息缓冲区。消息缓冲区 在完成每个发送和接收操作时执行回调。使用 xMessageBufferCreateStatic()API 创建的消息缓冲区共享相同的发送和接收完成回调函数,这些函数是用 sbSEND_COMPLETED() 和 sbRECEIVE_COMPLETED() 宏定义的。 使用 xMessageBufferCreateStaticWithCallback() API 创建的消息缓冲区可以有各自独特的发送和接收完成 回调函数。请参阅 xMessageBufferCreate() and xMessageBufferCreateWithCallback() 了解使用动态分配内存的对应版本。
  configSUPPORT_STATIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1, 才能使用 xMessageBufferCreateStatic()。此外,configUSE_SB_COMPLETED_CALLBACK 必须在 FreeRTOSConfig.h 中设置为 1, xMessageBufferCreateStaticWithCallback() 才可用。
  将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中即可启用消息缓冲区功能 (因为消息缓冲区使用流缓冲区)。
参数:
  xBufferSizeBytes pucMessageBufferStorageArea 参数所指向的缓冲区的大小(单位:字节)。当消息写入消息缓冲区时, 同时也会写入额外的 sizeof(size_t)字节以存储消息的长度。 sizeof( size_t ) 在 32 位架构上的大小通常是 4 个字节,因此在大多数 32 位架构中,10 字节的消息将占用 14 字节的 消息缓冲区空间。可以存储在消息缓冲区的最大字节数实际上是 (xBufferSizeBytes - 1)。
  pucMessageBufferStorageArea 必须指向一个大小至少为 xBufferSizeBytes + 1 的 uint8_t 数组。将消息写入消息缓冲区时, 实际上是将消息复制到这个数组。
  pxStaticMessageBuffer 必须指向一个 StaticMessageBuffer_t 类型的变量,它将用于保存消息缓冲区的数据结构体。
  pxSendCompletedCallback 当一条消息被写入消息缓冲区时调用的回调函数。如果参数为 NULL,则使用 sbSEND_COMPLETED 宏所提供的默认实现。发送完成回调函数必须具有 StreamBufferCallbackFunction_t 定义的原型,即:

void vSendCallbackFunction( MessageBufferHandle_t xMessageBuffer,
                            BaseType_t xIsInsideISR,
                            BaseType_t * const pxHigherPriorityTaskWoken );

  pxReceiveCompletedCallback 当从消息缓冲区读取消息时 调用的回调函数。如果参数为 NULL,则使用 sbRECEIVE_COMPLETED 宏所提供的默认实现。接收完成回调函数必须具有 StreamBufferCallbackFunction_t 定义的原型,即:

void vReceiveCallbackFunction( MessageBufferHandle_t xMessageBuffer,
                               BaseType_t xIsInsideISR,
                               BaseType_t * const pxHigherPriorityTaskWoken );

返回值:
  如果成功创建了消息缓冲区,那么将返回一个所创建消息缓冲区的句柄。如果 pucMessageBufferStorageArea 或 pxStaticMessageBuffer 为 NULL,则返回 NULL。
用法示例:

/* 用来确定用于保存信息的数组的尺寸。可用的
 * 可用的空间实际上会比这个少一个,所以是999。*/
#define STORAGE_SIZE_BYTES 1000

/* 定义在消息缓冲区内实际保存消息的内存。
 *缓冲区。应该比在xBufferSizeBytes中传递的值多一个。
 *参数中传递的值多一个。*/
static uint8_t ucMessageBufferStorage[ STORAGE_SIZE_BYTES ];
static uint8_t ucMessageBufferWithCallbackStorage[ STORAGE_SIZE_BYTES ];

/*[/code-comment] 用来保存消息缓冲区结构的变量。*/
StaticMessageBuffer_t xMessageBufferStruct;
StaticMessageBuffer_t xMessageBufferWithCallbackStruct;

void vSendCallbackFunction ( MessageBufferHandle_t xMessageBuffer,
                            BaseType_t xIsInsideISR,
                            BaseType_t * const pxHigherPriorityTaskWoken )
{
    
    
    /* 在这里插入代码,当一条消息被写入到
     * 消息缓冲区。
     * 当消息缓冲区被用来在多核处理器之间传递消息时,这很有用。
     * 这在多核处理器上的核心之间传递消息时非常有用。在这种情况下,这个回调
     * 可以实现在另一个CPU核中产生一个中断。
     * 然后,中断的服务程序可以使用
     * xMessageBufferSendCompletedFromISR() API函数来检查,如果有必要的话
     * 必要时解除对正在等待消息的任务的封锁。*/
}

void vReceiveCallbackFunction( MessageBufferHandle_t xMessageBuffer,
                               BaseType_t xIsInsideISR,
                               BaseType_t * const pxHigherPriorityTaskWoken )
{
    
    
    /* 在此插入代码,当从消息缓冲区中读取消息时被调用。
     * 缓冲区。
     * 当消息缓冲区被用来在多核之间传递消息时,这很有用。
     * 这在多核处理器上的核心之间传递消息时非常有用。在这种情况下,这个回调
     * 可以实现在另一个CPU核中产生一个中断。
     * 然后中断的服务程序可以使用
     * xMessageBufferReceiveCompletedFromISR() API函数来检查,如果有必要的话,可以解除对一个任务的封锁。
     * 必要时解除对一个等待发送消息的任务的封锁。*/
}

void MyFunction( void )
{
    
    
	MessageBufferHandle_t xMessageBuffer, xMessageBufferWithCallback;

    /* 创建一个消息缓冲区,使用定义的函数
     * 使用sbSEND_COMPLETED()和sbRECEIVE_COMPLETED()
     *宏作为发送和接收完成的回调函数。*/
	xMessageBuffer = xMessageBufferCreateStatic( sizeof( ucMessageBufferStorage ),
                                                 ucMessageBufferStorage,
                                                 &xMessageBufferStruct );
                                                 
    /* 创建一个消息缓冲区,使用函数
     * vSendCallbackFunction和vReceiveCallbackFunction作为发送
     *和接收完成的回调函数。*/
    xMessageBufferWithCallback = xMessageBufferCreateStaticWithCallback( 
                                     sizeof( ucMessageBufferWithCallbackStorage ),
                                     ucMessageBufferWithCallbackStorage,
                                     &xMessageBufferWithCallbackStruct,
                                     vSendCallbackFunction,
                                     vReceiveCallbackFunction );
    
    /* 由于pucMessageBufferStorageArea或pxStaticMessageBuffer
     * 参数为空,xMessageBuffer和xMessageBufferWithCallback
     * 将不会是NULL,并且可以用来引用创建的消息
     * 在其他消息缓冲区的API调用中引用所创建的消息缓冲区。*/
    /* 其他使用消息缓冲区的代码可以在这里进行。*/
}

三、xMessageBufferSend()

API原型:

size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer,
                           const void *pvTxData,
                           size_t xDataLengthBytes,
                           TickType_t xTicksToWait );

  将离散消息发送到消息缓冲区。 消息可以是 适合缓冲区可用空间的任意长度,并复制到缓冲区中 。
  注意: 在 FreeRTOS 对象中唯一的流缓冲区实现 (消息缓冲区实现也是如此,因为消息缓冲区构建在 假定只有一个任务或中断会写到 缓冲区(写入器),而且只有一个任务或中断会从 缓冲区(读取器)读取。 写入器和读取器为不同的任务或中断是安全的, 或中断,但与其他 FreeRTOS 对象不同, 拥有多个不同的编写器或多个不同的读取器是不安全的。 如果有 多个不同的编写器,则应用程序编写者必须将每次调用 在临界区内写入 API 函数(如 xMessageBufferSend()),并使用发送阻塞时间 0。 同样,如果有多个不同的读取器,则应用程序 编写者必须将每次调用读取 API 函数(如 xMessageBufferRead()) 放在临界区中,并使用接收阻塞时间 0。
  使用 xMessageBufferSend() 从任务写入消息缓冲区。 使用 xMessageBufferSendFromISR() 从中断服务程序 (ISR) 写入 消息缓冲区。
  通过在构建中包含 FreeRTOS/source/stream_buffer.c 源文件 来启用消息缓冲区功能(因为消息缓冲区使用流缓冲区)。
参数:
  xMessageBuffer 消息发送到的消息缓冲区 的句柄。
  pvTxData 指向要复制到 消息缓冲区中的消息的指针。
  xDataLengthBytes 消息长度, 即 从pvTxData复制到消息缓冲区的字节数。 当消息 写入消息缓冲区时,还会额外写入 sizeof( size_t ) 字节 用来存储消息的长度。 sizeof( size_t ) 在 32 位架构上通常为 4 字节, 因此在大多数 32 位架构上, xDataLengthBytes设置为20时,会将消息缓冲区中的可用空间减少24个字节 字节(20 字节的消息数据和 4 字节用来保存消息长度)。
  xTicksToWait 如果调用 xMessageBufferSend() 时消息缓冲区 没有足够的空间,则 xTicksToWait 调用任务应保持 已阻塞状态的最长时间, 以等待消息缓冲区中有足够的空间可用。 如果 xTicksToWait 为零,则调用任务永远不会阻塞。 阻塞时间以滴答周期为单位,因此 它代表的绝对时间取决于滴答频率。 宏 pdMS_TO_TICKS() 可用于将以毫秒为单位的时间转换为 以滴答为单位的时间。 将 xTicksToWait 设置为 portMAX_DELAY 将导致 任务无限期等待(无超时),前提是 INCLUDE_vTaskSuspend 在 FreeRTOSConfig.h 中设置为 1。 任务在处于阻塞状态时 在处于已阻塞状态时不使用任何 CPU 时间。
返回值:
  写入消息缓冲区的字节数。 如果调用 xMessageBufferSend() 在有足够的空间将消息写入消息缓冲区之前超时, 则返回零。 如果调用未 超时,则返回 xDataLength 字节。
用法示例:

void vAFunction ( MessageBufferHandle_t xMessageBuffer )
{
    
    
	size_t xBytesSent;
	uint8_t ucArrayToSend[] = {
    
     0, 1, 2, 3 };
	char *pcStringToSend = "String to send";
	const TickType_t x100ms = pdMS_TO_TICKS( 100 );

    /* 发送一个数组到消息缓冲区,最多阻塞100ms,以等待消息缓冲区有足够的空间。
    等待消息缓冲区有足够的可用空间。*/
    xBytesSent = xMessageBufferSend( xMessageBuffer,
                                     ( void * ) ucArrayToSend,
                                     sizeof( ucArrayToSend ),
                                     x100ms);

    if( xBytesSent != sizeof( ucArrayToSend ))
    {
    
    
        /* 对xMessageBufferSend()的调用在缓冲区有足够的空间容纳数据之前就超时了。
        缓冲区内有足够的空间来写入数据之前,对xMessageBufferSend()的调用就超时了。*/
    }

    /* 发送字符串到消息缓冲区。 如果缓冲区没有足够的空间,立即返回。
    缓冲区中没有足够的空间,立即返回。*/
    xBytesSent = xMessageBufferSend( xMessageBuffer,
                                    ( void * ) pcStringToSend,
                                    strlen( pcStringToSend ), 0 );

    if( xBytesSent != strlen( pcStringToSend ))
    {
    
    
        /* 该字符串不能被添加到消息缓冲区,因为缓冲区没有足够的可用空间。
        缓冲区内没有足够的自由空间。*/
    }
}

四、xMessageBufferSendFromISR()

API原型:

size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer,
                                  const void *pvTxData,
                                  size_t xDataLengthBytes,
                                  BaseType_t *pxHigherPriorityTaskWoken );

  中断安全版本的 API 函数, 用于向消息缓冲区发送离散消息。 消息长度只要满足缓冲区可用空间即可, 消息会被复制到缓冲区中。
  注意: 与其他 FreeRTOS 对象都不同的是,流缓冲区的实现 (消息缓冲区的实现也是如此,因为消息缓冲区是建立在流缓冲区之上的) 流缓冲区的实现假定只有一个任务或中断将写入缓冲区(写入程序), 缓冲区(写入器),只有一个任务或中断会从 (读取程序)。 写入和读取 不同的任务或中断是安全的,但与其他FreeRTOS对象不同, 有多个不同的写入或多个不同的读取是不安全的。 如果 必须有多个不同的写入,那么应用程序编写者必须 将每个调用放置到临界区中的一个写入 API 函数(如 xStreamBufferSend())中, 并将发送阻塞时间设置为0。 同样,如果有多个不同的读取器, 那么应用程序编写者必须将每个调用放置到临界区中的读取API函数(如xStreamBufferRead())中, 并使用阻塞时间 0。
  使用 xMessageBufferSend() 往任务的消息缓冲区写入消息。 xMessageBufferSendFromISR()用于 往中断服务程序 (ISR) 的消息缓冲区写入 数据。
  通过在构建中包含 FreeRTOS/source/stream_buffer.c 源文件 来启用消息缓冲区功能(因为消息缓冲区使用流缓冲区)。
参数:
  xMessageBuffer 消息发送到的消息缓冲区 的句柄。
  pvTxData 要复制到消息缓冲区中的消息 的指针。
  xDataLengthBytes 消息长度, 即 从pvTxData复制到消息缓冲区的字节数。 当消息 写入消息缓冲区时,还会额外写入 sizeof( size_t ) 字节 用来存储消息的长度。 sizeof( size_t ) 在 32 位架构上通常为 4 字节, 因此在大多数 32 位架构上, xDataLengthBytes设置为20时,会将消息缓冲区中的可用空间减少24个字节 (其中消息数据占20个字节,4个字节用来保存消息长度)。
  pxHigherPriorityTaskWoken (这是一个可选参数,可以设置为 NULL。) 消息缓冲区可能会 阻塞任务等待数据。 调用 xMessageBufferSendFromSISR() 可以使数据可用,从而使 正在等待数据的任务离开阻塞状态。 如果调用 xMessageBufferSendFromSISR() 使任务离开阻止状态,同时 未阻塞任务的优先级高于当前正在执行的任务 (被中断的任务),那么在内部,xMessageBufferSendFromISR() 将* 把pxHigherPriorityTaskWoken设置为pdTRUE。 如果 xMessageBufferSendFromSISR() 将此值设置为 pdTRUE , 那么通常应在退出中断之前执行上下文切换。 这将 确保中断直接返回到最高优先级的“就绪” 状态任务。 * pxHigherPriorityTaskWoken在传递给函数之前 应该设置为pdFALSE。 有关示例,请参阅下面的代码示例。
返回值:
  实际写入消息缓冲区的字节数。 如果 消息缓冲区可用空间不足,无法存储消息, 则返回 0,否则返回 xDataLengthBytes。
用法示例:

/* 一个已经被创建的消息缓冲区。*/
MessageBufferHandle_t xMessageBuffer。

void vAnInterruptServiceRoutine( void )
{
    
    
	size_t xBytesSent;
	char *pcStringToSend = "String to send";
	BaseType_t xHigherPriorityTaskWoken = pdFALSE; /*初始化为pdFALSE。*/

    /* 尝试将字符串发送到消息缓冲区。*/
    xBytesSent = xMessageBufferSendFromISR( xMessageBuffer,
                                            ( void * ) pcStringToSend,
                                            strlen( pcStringToSend ),
                                            &xHigherPriorityTaskWoken );

    if( xBytesSent != strlen( pcStringToSend ) )
    {
    
    
        /* 该字符串不能被添加到消息缓冲区,因为缓冲区没有足够的可用空间。
        缓冲区内没有足够的可用空间。*/
    }

    /* 如果xMessageBufferSendFromISR()中的xHigherPriorityTaskWoken被设置为pdTRUE,那么一个优先级高于TaskWoken的任务就会出现。
    xMessageBufferSendFromISR()中设置为pdTRUE,那么一个优先级高于当前执行任务优先级的任务就会被删除。
    的任务被解锁,那么就应该进行上下文切换,以确保 ISR
    应执行上下文切换,以确保ISR返回到未被阻止的
    任务。 在大多数FreeRTOS端口中,这可以通过简单地传递
    xHigherPriorityTaskWoken到taskYIELD_FROM_ISR()中,它将测试这个
    变量值,并在必要时进行上下文切换。 检查
    文件中关于端口的具体说明。*/
    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

五、xMessageBufferReceive()

API原型:

size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,
                              void *pvRxData,
                              size_t xBufferLengthBytes,
                              TickType_t xTicksToWait );

  从RTOS 消息缓冲区接收离散消息。 消息长度可变, 并且从缓冲区中复制出来。
  注意: 与其他 FreeRTOS 对象都不同的是,流缓冲区的实现 (消息缓冲区的实现也是如此,因为消息缓冲区是建立在流缓冲区之上的) 流缓冲区的实现假定只有一个任务或中断将写入缓冲区(写入程序), 缓冲区(写入器),只有一个任务或中断会从 (读取程序)。 写入和读取 不同的任务或中断是安全的,但与其他FreeRTOS对象不同, 有多个不同的写入或多个不同的读取是不安全的。 如果 必须有多个不同的写入,那么应用程序编写者必须 将每个调用放置到临界区中的一个写入 API 函数(如 xStreamBufferSend())中, 并将发送阻塞时间设置为0。 同样,如果有多个不同的读取器, 那么应用程序编写者必须将每个调用放置到临界区中的读取API函数(如xStreamBufferRead())中, 并使用接收阻塞时间 0。
  使用 xMessageBufferReceive () 从任务中的消息缓冲区读取数据。 使用 xMessageBufferReceiveFromISR() 从 中断服务程序 (ISR) 中的消息缓冲区读取数据。
  消息缓冲区的功能是通过将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中 来启用消息缓冲区功能(因为消息缓冲区使用流缓冲区)。
参数:
  xMessageBuffer 正在接收消息来自的消息缓冲区 的句柄。
  pvRxData 指向缓冲区的指针,收到的信息 将被复制到该缓冲区。
  xBufferLengthBytes 由 pvRxData 参数指向的缓冲区的 长度。 这将设置可以接收的消息的最大长度。 如果 xBufferLengthBytes 空间不足,无法保存下一条消息,那么消息 将保留在消息缓冲区中,并且将返回 0。
  xTicksToWait 调用xMessageBufferReceive()时, 如果消息缓冲区为空, 则调用任务保持在阻塞状态等待消息的最长时间。 如果 xTicksToWait 为零并且消息缓冲区为空,将立即返回。 阻塞时间在滴答周期中指定,因此 它所表示的绝对时间取决于滴答频率。 宏pdMS_TO_TICKS()可以用来将以毫秒为单位的时间转换成以刻度为单位的时间。 宏pdMS_TO_TICKS()可以用来将以毫秒为单位的时间 转换为以 tick 为单位的时间。 将xTicksToWait设置为portMAX_DELAY会 导致任务无限期地等待(没有超时),前提是 在FreeRTOSConfig.h中将INCLUDE_vTaskSuspend设置为1。 任务在处于阻塞状态时 不使用任何CPU时间。
返回值:
  从消息缓冲区读取的消息的长度(以字节为单位) (如有)。 如果在消息写入之前,xMessageBufferSend() 调用超时, 则返回零。 如果消息长度大于 xBufferLengthBytes,则消息将保留在消息缓冲区中, 同时返回零。
用法示例:

void vAFunction ( MessageBuffer_t xMessageBuffer )
{
    
    
	uint8_t ucRxData[ 20 ];
	size_t xReceivedBytes;
	const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );

    /* 从消息缓冲区接收下一条消息。 在阻塞状态下等待
    状态下等待(所以不使用任何CPU处理时间),最多100ms,以等待一个消息的出现。
    的消息。*/
    xReceivedBytes = xMessageBufferReceive( xMessageBuffer,
                                            ( void * ) ucRxData,
                                            sizeof( ucRxData ),
                                            xBlockTime);

    if( xReceivedBytes > 0 )
    {
    
    
        /* 一个ucRxData包含一个长为xReceivedBytes的信息。 处理
        这里的消息.... */
    }
}

六、xMessageBufferReceiveFromISR()

API原型:

size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer,
                                     void *pvRxData,
                                     size_t xBufferLengthBytes,
                                     BaseType_t *pxHigherPriorityTaskWoken );

  中断安全版本的 API 函数,用于 从消息缓冲区接收离散消息。 消息长度可变, 从缓冲区中复制出来。
  注意: 与其他 FreeRTOS 对象都不同的是,流缓冲区的实现 (消息缓冲区的实现也是如此,因为消息缓冲区是建立在流缓冲区之上的) 流缓冲区的实现假定只有一个任务或中断将写入缓冲区(写入程序), 缓冲区(写入器),只有一个任务或中断会从 (读取程序)。 写入和读取 不同的任务或中断是安全的,但与其他FreeRTOS对象不同, 有多个不同的写入或多个不同的读取是不安全的。 如果 必须有多个不同的写入,那么应用程序编写者必须 将每个调用放置到临界区中的一个写入 API 函数(如 xStreamBufferSend())中, 并将发送阻塞时间设置为0。 同样,如果有多个不同的读取器, 那么应用程序编写者必须将每个调用放置到临界区中的读取API函数(如xStreamBufferRead())中, 并将接收阻塞时间设置为0。
  使用 xMessageBufferReceive() 从任务的消息缓冲区读取数据。 使用 xMessageBufferReceiveFromISR () 从中断服务程序 (ISR) 的消息缓冲区 读取数据。
  消息缓冲区的功能是通过将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中 来启用消息缓冲区功能(因为消息缓冲区使用流缓冲区)。
参数:
  xMessageBuffer 消息缓冲区的句柄,正在接收的信息来自该消息缓存区。
  pvRxData 一个指向缓冲区的指针,收到的信息将 被复制到该缓冲区。
  xBufferLengthBytes 由 pvRxData 参数指向的缓冲区的 长度。 这将设置可以接收的消息的最大长度。 如果 xBufferLengthBytes 空间不足,无法保存下一条消息,那么消息 将保留在消息缓冲区中,并且将返回 0。
  pxHigherPriorityTaskWoken (此为可选参数,可以设置为 NULL。) 消息缓冲区可能会 阻塞任务,等待可用空间。 调用 xMessageBufferReceiveFromISR () 可以使空间可用,从而使等待空间的任务 结束阻塞状态。 如果调用 xMessageBufferReceiveFromISR () 导致任务结束阻塞状态,并且 未阻塞任务的优先级高于当前正在执行的任务 (被中断的任务),那么在内部, xMessageBufferReceiveFromISR() 将* pxHigherPriorityTaskWoken 设置为 pdTRUE。 如果 xMessageBufferReceiveFromISR() 将此值设置为 pdTRUE, 那么通常应在退出中断之前执行上下文切换。 这将 确保中断直接返回到最高优先级的就绪状态 任务。 * pxHigherPriorityTaskWoken在传递给函数之前 应该设置为pdFALSE。 有关示例,请参阅下面的代码示例。
返回值:
  从消息缓冲区读取的消息的长度(以字节为单位) (如有)。
用法示例:

/* 一个已经被创建的消息缓冲区。*/
MessageBuffer_t  xMessageBuffer;

void vAnInterruptServiceRoutine( void )
{
    
    
	uint8_t ucRxData[ 20 ];
	size_t xReceivedBytes;
	BaseType_t xHigherPriorityTaskWoken = pdFALSE; /*初始化为pdFALSE。*/

    /* 从消息缓冲区接收下一条消息。*/
    xReceivedBytes = xMessageBufferReceiveFromISR( xMessageBuffer,
                                                  ( void * ) ucRxData,
                                                  sizeof( ucRxData )&xHigherPriorityTaskWoken);

    if( xReceivedBytes > 0 )
    {
    
    
        /* 一个ucRxData包含一个长为xReceivedBytes的信息。 处理
        这里的消息.... */
    }

    /* 如果xMessageBufferReceiveFromISR()中的xHigherPriorityTaskWoken被设置为pdTRUE,那么一个有优先权的任务就会在这里进行处理。
    xMessageBufferReceiveFromISR()中设置为pdTRUE,那么一个优先级高于当前执行任务优先级的任务就会被解锁,并且上下文也被解锁。
    的任务被解锁,那么就应该进行上下文切换,以确保 ISR
    应执行上下文切换,以确保ISR返回到未被阻止的
    任务。 在大多数FreeRTOS端口中,这可以通过简单地传递
    xHigherPriorityTaskWoken到taskYIELD_FROM_ISR()中,它将测试这个
    变量值,并在必要时进行上下文切换。 检查
    文件中关于端口的具体说明。*/
    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

七、vMessageBufferDelete()

API原型:

void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer );

  删除之前创建的消息缓冲区 (通过调用 xMessageBufferCreate() 或 xMessageBufferCreateStatic() 创建)。 如果使用动态内存(即由 xMessageBufferCreate())创建消息缓冲区, 则会释放分配的内存。
  删除消息缓冲区后,不得使用消息缓冲区 句柄。
  通过在构建中包含 FreeRTOS/source/stream_buffer.c 源文件, 来启用消息缓冲区功能(因为消息缓冲区使用流缓冲区)。
参数:
  xMessageBuffer 要删除的消息缓冲区的句柄。

八、xMessageBufferSpacesAvailable()

API原型:

size_t xMessageBufferSpacesAvailable( MessageBufferHandle_t xMessageBuffer );

  查询一个消息缓冲区,以查看它包含多少空闲空间, 这等于在消息缓冲区满之前可以 向它发送的数据量。 返回的值比可发送到消息缓冲区的 最大消息大小多了 4 个字节。
  通过将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中 来启用消息缓冲区功能(因为消息缓冲区使用流缓冲区)。
参数:
  xMessageBuffer 正在查询的消息缓冲区的句柄。
返回值:
  在消息缓冲区满之前,可以写入消息缓冲区的 字节数。 当消息被 写入消息缓冲区时,还会额外写入 sizeof( size_t ) 字节 用来存储消息的长度。 在 32 位的架构上,sizeof( size_t ) 通常为 4 个字节, 因此如果 xMessageBufferSpacesAvailable() 返回 10, 则可以写入消息缓冲区的最大消息的大小 为 6 字节。
用法示例:

九、xMessageBufferReset()

API原型:

BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );

  将消息缓冲区重置为其初始空状态。 任何在消息缓冲区的数据 都被丢弃。 只有没有任务处于阻塞态以等待向消息缓冲区发送或从消息缓冲区接收时, 消息缓冲区才能被重置 。
  通过在构建中包含 FreeRTOS/source/stream_buffer.c 源文件 来启用消息缓冲区功能(因为消息缓冲区使用流缓冲区)。
参数:
  xMessageBuffer 正在重置的消息缓冲区的句柄。
返回值:
  如果重置消息缓冲区,则返回 pdPASS。 如果有 一个任务处于阻塞态,等待向消息缓冲区发送或从消息缓冲区读取, 则消息缓冲区将不会被重置,并返回 pdFAIL。

十、xMessageBufferReset()

API原型:

BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );

  查询消息缓冲区是否为空。 如果消息缓冲区不包含任何消息,则为空。
  将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中即可 来启用消息缓冲区功能(因为消息缓冲区使用流缓冲区)。
参数:
  xMessageBuffer 正在查询的消息缓冲区的句柄。
返回值:
  若消息缓冲区为空,则返回 pdTRUE。 否则 返回 pdFALSE。

十一、xMessageBufferIsFull()

API原型:

BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer );

  查询消息缓冲区以查看其是否已满。 如果消息缓冲区无法再接受任何大小的消息,则消息缓冲区已满, 直到通过从消息缓冲区中删除消息来提供空间为止。
  通过将 FreeRTOS/source/stream_buffer.c 源文件包含在构建(消息缓冲区使用流缓冲区)中 来启用消息缓冲区功能(因为消息缓冲区使用流缓冲区)。
参数:
  xMessageBuffer 正在查询的消息缓冲区的句柄。
返回值:
  如果消息缓冲区已满,则返回 pdTRUE。 否则 返回 pdFALSE。

十二、用法示例

streambuffer示例

/**
 * @file 21_MessageBuffer.c
 * @author WSP
 * @brief 消息流 和 streambuffer不同有
 * 1、MessageBuffer只能一次接收一条数据
 * 2、MessageBuffer当接收buffer小于发送数据长度的时候不能接收到数据
 * @version 0.1
 * @date 2022-10-23
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_StreamBuffer";

/**
 * @brief   Task_Send_Buffer
 * @param   arg
 * @return  NULL
*/
void Task_Send_Buffer(void *arg)
{
    
    
    char SendBuffer[50];
    char count = 0,str_len = 0,send_len = 0;
    MessageBufferHandle_t MessageBufferHandle = (MessageBufferHandle_t)arg;
    vTaskDelay(1000/portTICK_PERIOD_MS);
    while (1) {
    
    
        str_len = sprintf(SendBuffer,"My name is wsp counter value:%d",count);
        count ++;
        send_len = xMessageBufferSend(MessageBufferHandle,(void *)SendBuffer,str_len,portMAX_DELAY);
        if(send_len == str_len) {
    
    
            ESP_LOGI(TAG, "Task_Send_Buffer OK:%d",send_len);
        } else {
    
    
            ESP_LOGI(TAG, "Task_One Notify fail:%d",send_len);
        }
        vTaskDelay(3000/portTICK_PERIOD_MS);
    }
}
/**
 * @brief   Task_Receive_Buffer
 * @param   arg
 * @return  NULL
*/
void Task_Receive_Buffer(void *arg)
{
    
    
    MessageBufferHandle_t MessageBufferHandle = (MessageBufferHandle_t)arg;
    char receive_data[50];
    char receive_len = 0;
    while (1) {
    
    
        memset(receive_data,0,sizeof(receive_data));
        receive_len = xMessageBufferReceive(MessageBufferHandle, (void *)receive_data,sizeof(receive_data),portMAX_DELAY);
        if(receive_len) {
    
    
            int message_receive_len = xMessageBufferSpacesAvailable(MessageBufferHandle);
            ESP_LOGI(TAG, "Task_Receive_Buffer receive len:%d message_receive_len:%d data:%s",receive_len,message_receive_len,receive_data);
        }
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void MessageBuffer_Init(void)
{
    
    
    MessageBufferHandle_t MessageBufferHandle;
    MessageBufferHandle = xMessageBufferCreate(500);
    
    if(MessageBufferHandle != NULL) {
    
    
        // 创建任务一
        xTaskCreate(Task_Send_Buffer,
                    "Task_Send",
                    1024 * 5,
                    (void *)MessageBufferHandle,
                    1,
                    NULL);
        // 创建任务二
        xTaskCreate(Task_Receive_Buffer,
                    "Task_Receive",
                    1024 * 5,
                    (void *)MessageBufferHandle,
                    2,
                    NULL);
        ESP_LOGI(TAG,"task Create ok");
    }
    vTaskDelay(2000/portTICK_PERIOD_MS);        // 延时等待
}

猜你喜欢

转载自blog.csdn.net/believe666/article/details/128709814
今日推荐