ESP32 FreeRTOS-流缓冲区(12)

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

前言:

  参考资料:FreeRTOS API参考
  这里主要数据流,我们可以实现接收一些流媒体数据,例如音频数据流,视频数据流,只要达到我们接收长度即可触发接收,之后一值会将结束数据区塞满。

一、xStreamBufferCreate()、xStreamBufferCreateWithCallback()

API原型:

StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes,
                                           size_t xTriggerLevelBytes );
StreamBufferHandle_t xStreamBufferCreateWithCallback( 
                         size_t xBufferSizeBytes,
                         size_t xTriggerLevelBytes
                         StreamBufferCallbackFunction_t pxSendCompletedCallback,
                         StreamBufferCallbackFunction_t pxReceiveCompletedCallback );

  创建一个使用动态分配内存的新流缓冲区。流缓冲区 在完成每个发送和接收操作时执行回调。使用 xStreamBufferCreate()API 创建的流缓冲区 共享相同的发送和接收完成回调函数,这些函数是用 sbSEND_COMPLETED() 和 sbRECEIVE_COMPLETED() 宏定义的 。使用 xStreamBufferCreateWithCallback() API 创建的流缓冲区可以有各自独特的发送和接收完成 回调函数。请参阅 xStreamBufferCreateStatic() 和 xStreamBufferCreateStaticWithCallback() 了解使用动态分配内存(在编译时分配的内存)的对应版本 。
  configSUPPORT_DYNAMIC_ALLOCATION 必须 在 FreeRTOSConfig.h 中设置为 1 或未定义, xStreamBufferCreate () 才可用。此外,configUSE_SB_COMPLETED_CALLBACK 必须在 FreeRTOSConfig.h 中设置为 1,xStreamBufferCreateWithCallback() 才可用。
  将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中即可启用流缓冲区功能。
参数:
  xBufferSizeBytes 流缓冲区在任何时候能够容纳的总字节数。
  xTriggerLevelBytes 在流缓冲区中被阻塞以等待数据的任务离开阻塞状态之前, 流缓冲区中必须包含的字节数。例如,如果一个任务在读取触发等级为 1 的空流缓冲区时被阻塞, 那么当单个字节写入缓冲区或该任务的阻塞时间结束时,该任务将被解除阻塞。 另一个例子是,如果一个任务在读取触发等级为 10 的空流缓冲区时被阻塞, 那么直到流缓冲区至少包含 10 个字节或该任务的阻塞时间结束之前,该任务将不会被解除阻塞。如果 读任务的阻塞时间在达到触发等级之前过期,那么该任务仍将接收实际可用的字节数 。将触发等级设置为 0 将导致使用触发等级 1。指定 一个大于缓冲区大小的触发等级是无效的。
  pxSendCompletedCallback 当对流缓冲区的数据写入导致缓冲区的字节数超过触发等级时调用的回调函数 。如果参数为 NULL,则使用 sbSEND_COMPLETED 宏所提供的默认实现 。发送完成的回调函数必须具有 StreamBufferCallbackFunction_t 定义的原型,即:

void vSendCallbackFunction( StreamBufferHandle_t xStreamBuffer,
                            BaseType_t xIsInsideISR,
                            BaseType_t * const pxHigherPriorityTaskWoken );

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

void vReceiveCallbackFunction( StreamBufferHandle_t xStreamBuffer,
                               BaseType_t xIsInsideISR,
                               BaseType_t * const pxHigherPriorityTaskWoken );

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

void vSendCallbackFunction( StreamBufferHandle_t xStreamBuffer,
                            BaseType_t xIsInsideISR,
                            BaseType_t * const pxHigherPriorityTaskWoken )
{
    
    
    /* 在此插入代码,当对流缓冲区进行数据写入操作时,该代码被调用。
     * 在此插入代码,当对流缓冲区的数据写入操作导致缓冲区内的字节数
     * 导致缓冲区内的字节数超过了触发水平。
     * 当流缓冲区被用来在多核处理器之间传递数据时,这很有用。
     * 在一个多核处理器上的核心之间传递数据时,这一点非常有用。在这种情况下,这个回调
     * 可以实现在另一个CPU核中产生一个中断。
     * 然后,中断的服务程序可以使用
     * xStreamBufferSendCompletedFromISR() API函数来检查,如果有必要的话
     * 必要时解除对一个正在等待数据的任务的封锁。*/
}

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

void vAFunction( void )
{
    
    
	StreamBufferHandle_t xStreamBuffer, xStreamBufferWithCallback;
	const size_t xStreamBufferSizeBytes = 100, xTriggerLevel = 10;

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

二、xStreamBufferCreateStatic()、xStreamBufferCreateStaticWithCallback()

API原型:

StreamBufferHandle_t xStreamBufferCreateStatic(
                                    size_t xBufferSizeBytes,
                                    size_t xTriggerLevelBytes,
                                    uint8_t *pucStreamBufferStorageArea,
                                    StaticStreamBuffer_t *pxStaticStreamBuffer );

StreamBufferHandle_t xStreamBufferCreateStaticWithCallback(
                                    size_t xBufferSizeBytes,
                                    size_t xTriggerLevelBytes,
                                    uint8_t *pucStreamBufferStorageArea,
                                    StaticStreamBuffer_t *pxStaticStreamBuffer,
                                    StreamBufferCallbackFunction_t pxSendCompletedCallback,
                                    StreamBufferCallbackFunction_t pxReceiveCompletedCallback );

  使用静态分配的内存创建一个新的流缓冲区。流缓冲区 在完成每个发送和接收操作时执行回调。使用 xStreamBufferCreateStatic() API 创建的流缓冲区 共享相同的发送和接收完成回调函数,这些函数是用 sbSEND_COMPLETED() 和 sbRECEIVE_COMPLETED() 宏定义的 。使用 xStreamBufferCreateStaticWithCallback() API 创建的流缓冲区可以有各自独特的发送和接收完成 回调函数。请参阅 xStreamBufferCreate() and xStreamBufferCreateWithCallback() 了解使用动态分配内存的对应版本。
  configSUPPORT_STATIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1, xStreamBufferCreateStatic() 才可用。此外,configUSE_SB_COMPLETED_CALLBACK 必须在 FreeRTOSConfig.h 中设置为 1,xStreamBufferCreateStaticWithCallback() 才可用。
  将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中即可启用流缓冲区功能。
参数:
  xBufferSizeBytes pucStreamBufferStorageArea 参数所指向的缓冲区的大小(单位:字节)。
  xTriggerLevelBytes 在流缓冲区中被阻塞以等待数据的任务离开阻塞状态之前, 流缓冲区中必须存在的字节数。例如,如果一个任务在读取触发等级为 1 的空流缓冲区时被阻塞, 那么当单个字节写入缓冲区或该任务的阻塞时间结束时,该任务将被解除阻塞。 另一个例子是,如果一个任务在读取触发等级为 10 的空流缓冲区时被阻塞, 那么直到流缓冲区至少包含 10 个字节或该任务的阻塞时间结束之前,该任务将不会被解除阻塞。如果 读取任务的阻塞时间在达到触发等级之前过期,那么该任务仍将接收实际可用的字节数 。将触发等级设置为 0 将导致使用触发等级 1。指定 一个大于缓冲区大小的触发等级是无效的。
  pucStreamBufferStorageArea 必须指向一个大小至少为 xBufferSizeBytes + 1 的 uint8_t 数组。这是一个数组,当将流写入流缓冲区时, 流会被复制到这个数组中。
  pxStaticStreamBuffer 必须指向一个 StaticStreamBuffer_t 类型的变量,它将用于保存流缓冲区的数据结构体。
  pxSendCompletedCallback 当对流缓冲区的数据写入导致缓冲区的字节数超过触发等级时调用的回调函数 。如果参数为 NULL,则使用 sbSEND_COMPLETED 宏所提供的默认实现 。发送完成的回调函数必须具有 StreamBufferCallbackFunction_t 定义的原型,即:

void vSendCallbackFunction( StreamBufferHandle_t xStreamBuffer,
                            BaseType_t xIsInsideISR,
                            BaseType_t * const pxHigherPriorityTaskWoken );

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

void vReceiveCallbackFunction( StreamBufferHandle_t xStreamBuffer,
                               BaseType_t xIsInsideISR,
                               BaseType_t * const pxHigherPriorityTaskWoken );

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

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

/* 定义在流缓冲区内实际容纳流的内存。
 *流缓冲区的内存。*/
static uint8_t ucStreamBufferStorage[ STORAGE_SIZE_BYTES ]static uint8_t ucStreamBufferWithCallbackStorage[ STORAGE_SIZE_BYTES ];

/* 用于保存流缓冲区结构的变量。*/
StaticStreamBuffer_t xStreamBufferStruct;
StaticStreamBuffer_t xStreamBufferWithCallbackStruct;

void vSendCallbackFunction( StreamBufferHandle_t xStreamBuffer,
                            BaseType_t xIsInsideISR,
                            BaseType_t * const pxHigherPriorityTaskWoken )
{
    
    
    /* 在此插入代码,当对流缓冲区进行数据写入操作时,该代码被调用。
     * 在此插入代码,当对流缓冲区的数据写入操作导致缓冲区内的字节数
     * 导致缓冲区内的字节数超过了触发水平。
     * 当流缓冲区被用来在多核处理器之间传递数据时,这很有用。
     * 在一个多核处理器上的核心之间传递数据时,这一点非常有用。在这种情况下,这个回调
     * 可以实现在另一个CPU核中产生一个中断。
     * 然后,中断的服务程序可以使用
     * xStreamBufferSendCompletedFromISR() API函数来检查,如果有必要的话
     * 必要时解除对一个正在等待数据的任务的封锁。*/
}

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

void MyFunction( void )
{
    
    
	StreamBufferHandle_t xStreamBuffer, xStreamBufferWithCallback;
	const size_t xTriggerLevel = 1;

    /* 创建一个流缓冲区,使用定义的函数
     * 使用sbSEND_COMPLETED()和sbRECEIVE_COMPLETED()
     * 的宏作为发送和接收完成的回调函数。*/
    xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucStreamBufferStorage ),
                                               xTriggerLevel,
                                               ucStreamBufferStorage,
                                               &xStreamBufferStruct );

    /* 创建一个流缓冲区,使用函数
     * vSendCallbackFunction和vReceiveCallbackFunction作为发送
     *和接收完成的回调函数。*/
    xStreamBufferWithCallback = xStreamBufferCreateStaticWithCallback(
                                    sizeof( ucStreamBufferWithCallbackStorage )。
                                    xTriggerLevel。
                                    ucStreamBufferWithCallbackStorage。
                                    &xStreamBufferWithCallbackStruct,
                                    vSendCallbackFunction。
                                    vReceiveCallbackFunction );

    /* 由于pucStreamBufferStorageArea或pxStaticStreamBuffer
     * 参数为NULL,所以xStreamBuffer和xStreamBufferWithCallback
     * 将不会是NULL,并且可以用来引用创建的流
     * 在其他流缓冲区的API调用中引用创建的流缓冲区。*/
    
    /* 其他使用流缓冲区的代码可以到这里。*/
}

三、xStreamBufferSend()

API原型:

size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
                          const void *pvTxData,
                          size_t xDataLengthBytes,
                          TickType_t xTicksToWait );

  将字节发送到流缓冲区。 字节复制到流缓冲区中。
  注意: 在 FreeRTOS 对象中唯一的流缓冲区实现 (消息缓冲区实现也是如此,因为消息缓冲区构建在 假定只有一个任务或中断会写到 缓冲区(写入器),而且只有一个任务或中断会从 缓冲区(读取器)读取。 写入器和读取器为不同的任务或中断是安全的, 或中断,但与其他 FreeRTOS 对象不同, 拥有多个不同的编写器或多个不同的读取器是不安全的。 如果有 多个不同的写入器,那么应用程序写入器必须把对写入 API 函数(如 xStreamBufferSend())的每个调用放在一个临界区内, 并使用发送阻塞时间 0。 同样,如果有多个不同的读取器, 那么应用程序必须把对读取 API 函数(如 xStreamBufferReceive())的每个调用放在一个临界区内, 并使用接收阻塞时间 0。
  使用 xStreamBufferSend() 从任务写入流缓冲区。 使用 xStreamBufferSendFromSISR () 从 中断服务程序 (ISR) 写入流缓冲区。
  通过将 FreeRTOS/source/stream_buffer.c 源文件纳入构建中, 即可启用流缓冲区功能。
参数:
  xStreamBuffer 作为流发送目标缓冲区的流缓冲区的句柄 。
  pvTxData 一个指向缓冲区的指针, 该缓冲区用于保存要复制到流缓冲区的字节。
  xDataLengthBytes 从 pvTxData 复制到流缓冲区的最大字节数 。
  xTicksToWait 当流缓冲区的空间太小, 无法 容纳 另一个 xDataLengthBytes 的字节时,任务应保持在阻塞状态,以等待流缓冲区中出现足够空间的最长时间。 阻塞时间的单位为 tick 周期, 因此,它代表的绝对时间取决于 tick 频率。 宏 宏pdMS_TO_TICKS()可以用来将以毫秒为单位的时间 转换为以 tick 为单位的时间。 将xTicksToWait设置为portMAX_DELAY会 导致任务无限期地等待(没有超时),前提是 INCLUDE_vTaskSuspend 在 FreeRTOSConfig.h 中设置为 1。 如果一个任务 在向缓冲区写入所有 xDataLengthBytes 之前就超时, 它仍然会写入尽可能多的字节数。 处于阻塞状态的任务不会使用任何 CPU 时间 。
返回值:
  写入流缓冲区的字节数。 如果一个任务 在向缓冲区写入所有 xDataLengthBytes 之前就超时, 它仍然会写入尽可能多的字节数。
用法示例:

void vAFunction( StreamBufferHandle_t xStreamBuffer )
{
    
    
	size_t xBytesSent;
	uint8_t ucArrayToSend[] = {
    
     0, 1, 2, 3 };
	char *pcStringToSend = "要发送的字符串";
	const TickType_t x100ms = pdMS_TO_TICKS( 100 );

    /* 发送一个数组到流缓冲区,最多阻塞100ms以等待流缓冲区有足够的空间。
    等待流缓冲区内有足够的空间。*/
    xBytesSent = xStreamBufferSend( xStreamBuffer,
                                   ( void * ) ucArrayToSend,
                                   sizeof( ucArrayToSend ),
                                   x100ms);

    if( xBytesSent != sizeof( ucArrayToSend ))
    {
    
    
        /* 对xStreamBufferSend()的调用在缓冲区有足够的空间容纳数据之前就超时了。
        缓冲区内有足够的空间来写入数据之前,调用xStreamBufferSend()就已经超时了,但它确实成功地写入了xBytesSent字节。
        成功地写入了xBytesSent字节。*/
    }

    /* 发送字符串到流缓冲区。 如果缓冲区没有
    缓冲区内没有足够的空间,立即返回。*/
    xBytesSent = xStreamBufferSend( xStreamBuffer,
                                    ( void * ) pcStringToSend,
                                    strlen( pcStringToSend ), 0 );

    if( xBytesSent != strlen( pcStringToSend ))
    {
    
    
        /* 整个字符串不能被添加到流缓冲区,因为
        缓冲区内没有足够的空闲空间,但xBytesSent字节已经被发送。
        已被发送。 可以再次尝试发送剩余的字节。*/
    }
}

四、xStreamBufferSendFromISR()

API原型:

size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer,
                                 const void *pvTxData,
                                 size_t xDataLengthBytes,
                                 BaseType_t *pxHigherPriorityTaskWoken );

  向 流缓冲区 发送字节流的中断安全版本 API 函数。
  注意: 与其他 FreeRTOS 对象都不同的是,流缓冲区的实现 (消息缓冲区的实现也是如此,因为消息缓冲区是建立在流缓冲区之上的) 流缓冲区的实现假定只有一个任务或中断将写入缓冲区(写入程序), 缓冲区(写入器),只有一个任务或中断会从 (读取程序)。 写入和读取 不同的任务或中断是安全的,但与其他FreeRTOS对象不同, 有多个不同的写入或多个不同的读取是不安全的。 如果 多个不同的写入器, 那么应用程序写入器必须把对写入 API 函数(如 xStreamBufferSend())的每个调用放在一个临界区内, 并使用发送阻塞时间 0。 同样,如果有多个不同的读取器, 那么应用程序必须把对读取 API 函数(如 xStreamBufferReceive())的每个调用放在一个临界区内, 并使用接收阻塞时间 0。
  使用 xStreamBufferSend() 从任务写入流缓冲区。 使用 xStreamBufferSendFromSISR () 从 中断服务程序 (ISR) 写入流缓冲区。
  通过将 FreeRTOS/source/stream_buffer.c 源文件纳入构建中, 即可启用流缓冲区功能。
参数:
  xStreamBuffer 作为流发送目标缓冲区的流缓冲区的句柄 。
  pvTxData 一个指向缓冲区的指针, 该缓冲区用于保存要复制到流缓冲区的字节。
  xDataLengthBytes 从 pvTxData 复制到流缓冲区的最大字节数 。
  pxHigherPriorityTaskWoken (这是一个可选参数,可以设置为 NULL。) 一个流缓冲区有可能会 有一个在其上阻塞的任务,以等待数据。 调用 xStreamBufferSendFromISR() 可以使数据可用,进而导致正在等待数据的任务 离开阻塞状态。 如果调用 xStreamBufferSendFromSISR() 导致任务离开阻塞状态,并且 未阻塞任务的优先级高于当前执行的任务( 被中断的任务),那么 xStreamBufferSendFromISR() 将在内部 把 *pxHigherPriorityTaskWoken 设置为 pdTRUE。 如果 xStreamBufferSendFromSISR() 将此值设置为 pdTRUE,那么通常应在 退出中断之前执行上下文切换。 这将 确保中断直接返回到最高优先级的“就绪” 状态任务。 * pxHigherPriorityTaskWoken在传递给函数之前 应将其设置为 pdFALSE。 有关示例,请参阅下面的示例代码。
返回值:
  写入流缓冲区的字节数。 如果一个任务 在向缓冲区写入所有 xDataLengthBytes 之前就超时, 它仍然会写入尽可能多的字节数。
用法示例:

/* 一个已经被创建的流缓冲区。*/
StreamBufferHandle_t xStreamBuffer;

void vAnInterruptServiceRoutine( void )
{
    
    
	size_t xBytesSent。
	char *pcStringToSend = "要发送的字符串";
	BaseType_t xHigherPriorityTaskWoken = pdFALSE; /*初始化为pdFALSE。*/

    /* 尝试将字符串发送到流缓冲区。*/
    xBytesSent = xStreamBufferSendFromISR( xStreamBuffer,
                                           ( void * ) pcStringToSend,
                                           strlen( pcStringToSend ),
                                           &xHigherPriorityTaskWoken );

    if( xBytesSent != strlen( pcStringToSend ) )
    {
    
    
        /* 在流缓冲区中没有足够的自由空间来写入整个字符串。
        字符串,所以写了xBytesSent字节。*/
    }

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

五、xStreamBufferReceive()

API原型:

size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
                             void *pvRxData,
                             size_t xBufferLengthBytes,
                             TickType_t xTicksToWait );

  注意: 与其他 FreeRTOS 对象都不同的是,流缓冲区的实现 (消息缓冲区的实现也是如此,因为消息缓冲区是建立在流缓冲区之上的) 流缓冲区的实现假定只有一个任务或中断将写入缓冲区(写入程序), 缓冲区(写入器),只有一个任务或中断会从 (读取程序)。 写入和读取 不同的任务或中断是安全的,但与其他FreeRTOS对象不同, 有多个不同的写入或多个不同的读取是不安全的。 如果 多个不同的写入器, 那么应用程序写入器必须把对写入 API 函数(如 xStreamBufferSend())的每个调用放在一个临界区内, 并使用发送阻塞时间 0。 同样,如果有多个不同的读取器, 那么应用程序必须把对读取 API 函数(如 xStreamBufferReceive())的每个调用放在一个临界区内, 并使用接收阻塞时间 0。
  使用 xStreamBufferReceive() 从任务的流缓冲区读取数据。 使用 xStreamBufferReceiveFromISR() 从 中断服务程序 (ISR) 的流缓冲区读取数据。
  通过将 FreeRTOS/source/stream_buffer.c 源文件纳入构建中, 即可启用流缓冲区功能。
参数:
  xStreamBuffer 要接收字节来自的流缓冲区的句柄。
  pvRxData 指向缓冲区的指针,接收的字节将被复制到该缓冲区 。
  xBufferLengthBytes pvRxData 参数 所指向的缓冲区的长度。 这会设置一次调用中 接收的最大字节数。 xStreamBufferReceive 将返回尽可能多的字节数, 直到达到由   xBufferLengthBytes 设置的最大字节数为止。
  xTicksToWait 当流缓冲区为空时, 任务应保持在阻塞状态以等待数据的最长时间 。 如果 xTicksToWait 为 0,xStreamBufferReceive() 将立即返回。 阻塞时间的单位为 tick 周期, 因此,它代表的绝对时间取决于 tick 频率。 宏 pdMS_TO_TICKS() 可以 用来将以毫秒为单位的时间转换成以 tick 为单位的时间 。 将 xTicksToWait 设置为 portMAX_DELAY 将 将导致任务无限期等待(不超时),前提是 INCLUDE_vTaskSuspend 在 FreeRTOSConfig.h 中设置为 1。 处于阻塞状态的任务不会使用任何 CPU 时间 。
返回值:
  从流缓冲区读取的字节数。 这将为最多等于 xBufferLengthBytes 的可用字节数。 例如:
如果触发等级为 1 (触发等级是在创建流缓冲区时设置的)-

  • 如果 xBufferLengthBytes 为 10,且在调用 xStreamBufferReceive() 时,流缓冲区中包含 5 个字节,则 xStreamBufferReceive() 不会阻塞,从缓冲区中读取 5 个字节,并返回 5
      如果 xBufferLengthBytes 为 10,且在调用 xStreamBufferReceive() 时,流缓冲区中包含 50 个字节,则 xStreamBufferReceive() 不会阻塞,从缓冲区中读取 10 个字节,并返回 10。
      如果 xBufferLengthBytes 为 10,且在调用 xStreamBufferReceive() 时,流缓冲区中包含 0 个字节,xTicksToWait 为 100,并且在 50 个 tick 后缓冲区中收到 5 个字节,则 xStreamBufferReceive() 将进入为期 50 个 tick 的阻塞状态(直到数据到达缓冲区为止),之后它将从缓冲区中读取 5 个字节,并返回 5。
      如果 xBufferLengthBytes 为 10,且在调用 xStreamBufferReceive() 时,流缓冲区中包含 0 个字节,xTicksToWait 为 100,并且在 100 个 tick 内缓冲区中未收到字节,则 xStreamBufferReceive() 将进入为期 100 个 tick (完整阻塞时间)的阻塞状态,之后它将返回 0。
  • 如果触发等级为 6 -
      如果 xBufferLengthBytes 为 10,且在调用 xStreamBufferReceive() 时,流缓冲区中包含 0 个字节,xTicksToWait 为 100,并且在 50 个 tick 后缓冲区中收到 10 个字节,则 xStreamBufferReceive() 将进入为期 50 个 tick 的阻塞状态(直到至少有等于触发等级的字节数到达缓冲区为止),之后它将从缓冲区中读取 10 个字节,并返回 10。
      如果 xBufferLengthBytes 为 10,且在调用 xStreamBufferReceive() 时,流缓冲区中包含 0 个字节,xTicksToWait 为 100,并且在 50 个 tick 后缓冲区中收到 5 个字节,则 xStreamBufferReceive() 将在完整的 100 个 tick 阻塞周期内保持阻塞状态(因为缓冲区中的数据量永远无法达到触发等级),之后它将从缓冲区中读取 5 个字节,并返回 5。
    用法示例:
void vAFunction( StreamBuffer_t xStreamBuffer )
{
    
    
	uint8_t ucRxData[ 20 ];
	size_t xReceivedBytes;
	const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
    /*从流缓冲区接收最多一个sizeof( ucRxData ) 字节。
    在阻塞状态下等待(所以不使用任何CPU的处理时间),最多100ms,以接收完整的sheet。
    在阻塞状态下最多等待100ms,以获得全部的sizeof( ucRxData ) 字节数。
    可用。*/
    xReceivedBytes = xStreamBufferReceive( xStreamBuffer,
                                           ( void * ) ucRxData,
                                           sizeof( ucRxData ),
                                           xBlockTime );

    if( xReceivedBytes > 0 )
    {
    
    
        /* 一个ucRxData包含另一个xRecievedBytes字节的数据,可以在这里处理。
        在此处理.... */
    }
}

六、xStreamBufferReceiveFromISR()

xTaskCreate API原型:

size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer,
                                    void *pvRxData,
                                    size_t xBufferLengthBytes,
                                    BaseType_t *pxHigherPriorityTaskWoken );

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

/* 一个已经被创建的流缓冲区。*/
StreamBuffer_t xStreamBuffer;

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

    /* 从流缓冲器中接收下一个流。*/
    xReceivedBytes = xStreamBufferReceiveFromISR( xStreamBuffer,
                                                  ( void * ) ucRxData,
                                                  sizeof( ucRxData ),
                                                  &xHigherPriorityTaskWoken);

    if( xReceivedBytes > 0 )
    {
    
    
        /* ucRxData包含从流缓冲区读取的xReceivedBytes。
        在这里处理流.... */
    }

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

七、vStreamBufferDelete()

API原型:

void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer );

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

八、xStreamBufferBytesAvailable()

API原型:

size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer );

  查询流缓冲区以查看它包含多少数据, 该值等于在流缓冲区为空之前 可以从流缓冲区读取的字节数。
  将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中 即可启用流缓冲区功能。
参数:
  xStreamBuffer 正在查询的流缓冲区的句柄。
返回值:
  在流缓冲区为空之前可从流缓冲区读取的 字节数。

九、xStreamBufferSpacesAvailable()

API原型:

size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer );

  查询流缓冲区以查看有多少可用空间—— 等于在流缓冲区满之前可以向它发送的数据量 。
  通过将 FreeRTOS/source/stream_buffer.c 源文件纳入构建中, 即可启用流缓冲区功能。
参数:
  xStreamBuffer 正在被查询的流缓冲区的句柄。
返回值:
  在流缓冲区满之前, 可以写入流缓冲区的字节数。

十、xStreamBufferSetTriggerLevel()

API原型:

BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer,size_t xTriggerLevel );

  流缓冲区的触发等级是指在流缓冲区上被阻塞以等待数据的任务离开阻塞状态之前,流缓冲区中必须包含的字节数。例如,如果一个任务在读取触发等级为 1 的空流缓冲区时被阻塞,那么当单个字节写入缓冲区或该任务的阻塞时间到期时,该任务将被解除阻塞。另一个示例是,如果一个任务在读取触发等级为 10 的空流缓冲区时被阻塞,那么直到流缓冲区至少包含 10 个字节或该任务的阻塞时间结束,该任务才会被解除阻塞。如果读任务的阻塞时间在达到触发等级之前过期,那么该任务仍将接收实际可用的字节数。将触发等级设置为 0 将导致使用触发等级 1。指定大于缓冲区大小的触发等级是无效的。
  触发等级在创建流缓冲区时设置,可使用 xStreamBufferSetTriggerLevel() 进行修改。
  将 FreeRTOS/source/stream_buffer.c 源文件包含在构建中即可启用流缓冲区功能。
参数:
  xStreamBuffer 正在更新的流缓冲区的句柄。
  xTriggerLevel 流缓冲区的新触发等级。
返回值:
  如果 xTriggerLevel 小于或等于流缓冲区的长度,将更新触发等级并返回 pdTRUE。否则,返回 pdFALSE。

十 一、xStreamBufferReset()

API原型:

BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer );

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

十二、xStreamBufferIsEmpty()

API原型:

BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer );

  查询流缓冲区以查看其是否为空。 如果流缓冲区不包含任何数据,则为空。
  通过将源文件 FreeRTOS/source/stream_buffer.c包括在内,启用流缓冲区功能 即可启用流缓冲区功能。
参数:
  xStreamBuffer 正在查询的流缓冲区的句柄。
返回值:
  如果流缓冲区为空,则返回 pdTRUE。 否则 返回 pdFALSE。

十三、xStreamBufferIsFull()

API原型:

BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer );

  查询流缓冲区以查看其是否已满。 如果一个流缓冲区没有任何可用空间,则该流缓冲区已满,因此无法 接受任何更多数据。
  通过将 FreeRTOS/source/stream_buffer.c 源文件纳入构建中, 即可启用流缓冲区功能。
参数:
  xStreamBuffer 正在被查询的流缓冲区的句柄。
返回值:
  如果流缓冲区已满,则返回 pdTRUE。 否则 返回 pdFALSE。

十四、用法示例

数据流

/**
 * @file 19_StreamBuffer.c
 * @author WSP
 * @brief 数据流
 * @version 0.1
 * @date 2022-10-23
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_StreamBuffer";

#define USER_XSTREAMBUFFERSPACESAVAILABLE
/**
 * @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;
    StreamBufferHandle_t StreamBufferHandle = (StreamBufferHandle_t)arg;
    vTaskDelay(1000/portTICK_PERIOD_MS);
    while (1) {
    
    
        str_len = sprintf(SendBuffer,"My name is wsp counter value:%d",count);
        count ++;
        send_len = xStreamBufferSend(StreamBufferHandle,(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)
{
    
    
    StreamBufferHandle_t StreamBufferHandle = (StreamBufferHandle_t)arg;
    char receive_data[50];
    char receive_len = 0;
    while (1) {
    
    
        memset(receive_data,0,sizeof(receive_data));
        receive_len = xStreamBufferReceive(StreamBufferHandle, (void *)receive_data,sizeof(receive_data),portMAX_DELAY);
        if(receive_len) {
    
    
            ESP_LOGI(TAG, "Task_Receive_Buffer receive len:%d data:%s",receive_len,receive_data);
        }
    }
}
/**
 * @brief   Task_Monitoring
 * @param   arg
 * @return  NULL
*/
void Task_Monitoring(void *arg)
{
    
    
    StreamBufferHandle_t StreamBufferHandle = (StreamBufferHandle_t)arg;
    int receive_len = 0;
    while (1) {
    
    
    #ifdef USER_XSTREAMBUFFERBYTESAVAILABLE
        receive_len = xStreamBufferBytesAvailable(StreamBufferHandle);
    #else
        receive_len = xStreamBufferSpacesAvailable(StreamBufferHandle);
    #endif
        if(receive_len) {
    
    
            #ifdef USER_XSTREAMBUFFERBYTESAVAILABLE
                ESP_LOGI(TAG, "Task_Monitoring USER_XSTREAMBUFFERBYTESAVAILABLE  receive len:%d",receive_len);
            #else
                ESP_LOGI(TAG, "Task_Monitoring USER_XSTREAMBUFFERSPACESAVAILABLE receive len:%d",receive_len);
            #endif
        }
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void StreamBuffer_Init(void)
{
    
    
    StreamBufferHandle_t StreamBufferHandle;
    StreamBufferHandle = xStreamBufferCreate(500,100);
    
    if(StreamBufferHandle != NULL) {
    
    
        // 创建任务一
        xTaskCreate(Task_Send_Buffer,
                    "Task_Send",
                    1024 * 5,
                    (void *)StreamBufferHandle,
                    1,
                    NULL);
        // 创建任务二
        xTaskCreate(Task_Receive_Buffer,
                    "Task_Receive",
                    1024 * 5,
                    (void *)StreamBufferHandle,
                    2,
                    NULL);
        // 创建任务三
        xTaskCreate(Task_Monitoring,
                    "Task_Monitoring",
                    1024 * 5,
                    (void *)StreamBufferHandle,
                    2,
                    NULL);
        ESP_LOGI(TAG,"task Create ok");
    }
    vTaskDelay(2000/portTICK_PERIOD_MS);        // 延时等待
}

猜你喜欢

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