LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

ESP-IDF与freeRTOS(九) 数据流缓冲区

ESP-IDF与freeRTOS(九) 数据流缓冲区

流数据缓冲区

运行结果1

由于设置触发长度为10,而每次发送长度为25,所以每次发送都能触发接收

-----------------
Send: str_len = 25, send_byte = 25
-------------
Receive: rec_bytes = 25, data = Hello, I am vampire610 1       

-----------------
Send: str_len = 25, send_byte = 25
-------------
Receive: rec_bytes = 25, data = Hello, I am vampire610 2       

-----------------
Send: str_len = 25, send_byte = 25
-------------
Receive: rec_bytes = 25, data = Hello, I am vampire610 3 

运行结果2

此处可以看到第一次发送后接收成功了,而后续需要满100个数据才会触发接收。

这是因为当buffer中不为空时,接收函数会先接收再进入阻塞,然后等待满100触发。

可以将task1中的delay提到前面,保持buffer为空,让task2先因为不满100而阻塞,再让task1向buffer发送数据。

-----------------
Send: str_len = 24, send_byte = 24
-------------
Receive: rec_bytes = 24, data = Hello, I am vampire610 1       
-----------------
Send: str_len = 24, send_byte = 24
-----------------
Send: str_len = 24, send_byte = 24
-----------------
Send: str_len = 24, send_byte = 24
-----------------
Send: str_len = 24, send_byte = 24
-----------------
Send: str_len = 24, send_byte = 24
-------------
Receive: rec_bytes = 50, data = Hello, I am vampire610 2Hello, I am vampire610 3He�?
-------------
Receive: rec_bytes = 50, data = llo, I am vampire610 4Hello, I am vampire610 5Hell�?
-------------
Receive: rec_bytes = 20, data = o, I am vampire610 6
-----------------
Send: str_len = 24, send_byte = 24
-------------

运行结果3

按上述方法修改delay位置后,可以看到第一次接收消失了,task2一开始就就进入阻塞,需要等待stream buffer满100个才能接收。

-----------------
Send: str_len = 24, send_byte = 24
-----------------
Send: str_len = 24, send_byte = 24
-----------------
Send: str_len = 24, send_byte = 24
-----------------
Send: str_len = 24, send_byte = 24
-----------------
Send: str_len = 24, send_byte = 24
-------------
Receive: rec_bytes = 50, data = Hello, I am vampire610 1Hello, I am vampire610 2He�?
-------------
Receive: rec_bytes = 50, data = llo, I am vampire610 3Hello, I am vampire610 4Hell�?
-------------
Receive: rec_bytes = 20, data = o, I am vampire610 5
-----------------

示例代码

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

#include "freertos/stream_buffer.h"
#include "string.h"
StreamBufferHandle_t StreamBufferHandle = NULL;

void Task1(void *pvParam)
{
    int i = 0;
    int buf_len = 0;
    int send_bytes = 0;
    char tx_buf[50];
    while (1)
    {
        i++;
        // 向tx_buf填值,并返回长度
        buf_len = sprintf(tx_buf, "Hello, I am vampire610 %d\n", i);
        // 发送buf,并返回成功发送长度
        send_bytes = xStreamBufferSend(StreamBufferHandle, // 句柄
                                       (void *)tx_buf,     // 发送的buf,需要强转位void指针
                                       buf_len,            // buf长度
                                       portMAX_DELAY);     // 阻塞时间

        printf("-----------------\n");
        printf("Send: str_len = %d, send_byte = %d\n", buf_len, send_bytes);

        vTaskDelay(pdMS_TO_TICKS(3000));
    }
}

void Task2(void *pvParam)
{
    char rx_buf[50];
    int rec_bytes = 0;
    while (1)
    {
        // 清空接收数组
        memset(rx_buf, 0, 50);
        // 接收streambuffer
        rec_bytes = xStreamBufferReceive(StreamBufferHandle, // 句柄
                                         (void *)rx_buf,     // 接收缓冲区,强转void指针
                                         sizeof(rx_buf),     // sizeof取得缓冲区大小
                                         portMAX_DELAY);     // 阻塞时间

        printf("-------------\n");
        printf("Receive: rec_bytes = %d, data = %s\n", rec_bytes, rx_buf);

        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void app_main(void)
{

    // 创建streambuffer,两个参数,buffer大小,触发大小
    StreamBufferHandle = xStreamBufferCreate(1000, 10);
    if (StreamBufferHandle != NULL)
    {
        vTaskSuspendAll();

        // 创建Task1时需要传出Task1的句柄
        xTaskCreatePinnedToCore(Task1, "Task1", 1024 * 5, NULL, 1, NULL, 0);
        xTaskCreatePinnedToCore(Task2, "Task2", 1024 * 5, NULL, 1, NULL, 0);

        // 任务创建结束恢复任务调度器
        xTaskResumeAll();
    }
    else
    {
        printf("Fail to creat stream buffer\n");
    }
}

如何确定流数据缓冲区大小

太大浪费资源,太小不能正常工作。引入task3检测缓冲区空闲空间大小变化情况。

初始设置为比较大的值,再通过检测情况进行调整,这与之前确定任务的堆栈大小思路一致。

流数据实际使用场景一般产生数据的速率不固定,所以需要稍长的时间来检测空闲空间。

运行示例

运行一段时间后,就可以看到当前缓冲区剩余空间大概是多少,最小剩余多少,即可根据该值修改缓冲区容量,来避免过分浪费。

-------------
buf_space = 1000,min_space = 1000
-------------
buf_space = 1000,min_space = 1000
-----------------
Send: str_len = 24, send_byte = 24        
-------------
buf_space = 976,min_space = 976
-----------------
Send: str_len = 24, send_byte = 24        
-------------
buf_space = 952,min_space = 952
-------------
buf_space = 952,min_space = 952
-----------------
Send: str_len = 24, send_byte = 24        
-------------
buf_space = 928,min_space = 928
-----------------
Send: str_len = 24, send_byte = 24        
-------------
buf_space = 904,min_space = 904
-------------
buf_space = 904,min_space = 904
-----------------
Send: str_len = 24, send_byte = 24        
-------------
Receive: rec_bytes = 50, data = Hello, I am vampire610 1Hello, I am vampire610 2He�?
-------------
Receive: rec_bytes = 50, data = llo, I am vampire610 3Hello, I am vampire610 4Hell�?
-------------
buf_space = 980,min_space = 904
-------------
Receive: rec_bytes = 20, data = o, I am vampire610 5
-----------------
Send: str_len = 24, send_byte = 24        
-------------
Receive: rec_bytes = 24, data = Hello, I am vampire610 6
-------------
buf_space = 1000,min_space = 904
-------------
buf_space = 1000,min_space = 904
-----------------

示例代码

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

#include "freertos/stream_buffer.h"
#include "string.h"
StreamBufferHandle_t StreamBufferHandle = NULL;

void Task1(void *pvParam)
{
    int i = 0;
    int buf_len = 0;
    int send_bytes = 0;
    char tx_buf[50];
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(3000));
        i++;
        // 向tx_buf填值,并返回长度
        buf_len = sprintf(tx_buf, "Hello, I am vampire610 %d", i);
        // 发送buf,并返回成功发送长度
        send_bytes = xStreamBufferSend(StreamBufferHandle, // 句柄
                                       (void *)tx_buf,     // 发送的buf,需要强转位void指针
                                       buf_len,            // buf长度
                                       portMAX_DELAY);     // 阻塞时间

        printf("-----------------\n");
        printf("Send: str_len = %d, send_byte = %d\n", buf_len, send_bytes);
    }
}

void Task2(void *pvParam)
{
    char rx_buf[50];
    int rec_bytes = 0;
    while (1)
    {
        // 清空接收数组
        memset(rx_buf, 0, 50);
        // 接收streambuffer
        rec_bytes = xStreamBufferReceive(StreamBufferHandle, // 句柄
                                         (void *)rx_buf,     // 接收缓冲区,强转void指针
                                         sizeof(rx_buf),     // sizeof取得缓冲区大小
                                         portMAX_DELAY);     // 阻塞时间

        printf("-------------\n");
        printf("Receive: rec_bytes = %d, data = %s\n", rec_bytes, rx_buf);

        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void Task3(void *pvParam)
{
    int buf_space = 0;
    int min_space = 1000;
    while (1)
    {
        buf_space = xStreamBufferSpacesAvailable(StreamBufferHandle);
        if(buf_space<min_space)
        {
            //取得最小值
            min_space = buf_space;
        }
        printf("-------------\n");
        printf("buf_space = %d,min_space = %d\n", buf_space,min_space);
        //探查最小剩余空间时,这段代码运行频率要高于数据放入buffer的频率
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

void app_main(void)
{

    // 创建streambuffer,两个参数,buffer大小,触发大小
    StreamBufferHandle = xStreamBufferCreate(1000, 100);
    if (StreamBufferHandle != NULL)
    {
        vTaskSuspendAll();

        // 创建Task1时需要传出Task1的句柄
        xTaskCreatePinnedToCore(Task1, "Task1", 1024 * 5, NULL, 1, NULL, 0);
        xTaskCreatePinnedToCore(Task2, "Task2", 1024 * 5, NULL, 1, NULL, 0);
        xTaskCreatePinnedToCore(Task3, "Task3", 1024 * 5, NULL, 1, NULL, 0);

        // 任务创建结束恢复任务调度器
        xTaskResumeAll();
    }
    else
    {
        printf("Fail to creat stream buffer\n");
    }
}

x

showimg