下一个流缓冲区。
简单来说流缓冲区就是用来传输数据的。
我们之前介绍的队列也可以传输数据,但是有个小问题,那就是队列中每个元素都是固定长度的,如果我每次传输的数据长度都不一致呢?要么队列元素大小设置小了塞不进去,要么队列元素大小设置大了造成资源浪费。
所以流缓冲区的出现了。
我们可以理解成流缓冲区就是个池子,我们可以往池子里塞任意数量的东西,同时也可以从池子中取任意数量的东西,这样对于我们要传输的数据大小就没有限制了。
首先包含头文件。
#include "freertos/stream_buffer.h"
接着使用下面这个宏创建一个流缓冲区。
xStreamBufferCreate( xBufferSizeBytes, xTriggerLevelBytes )
两个参数,一个是流缓冲区的大小,另一个是流缓冲区里有多少字节的时候可以读取。
下面这个函数给流缓冲区塞数据。
size_t xStreamBufferSend(StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait)
参数一流缓冲区句柄。
参数二是要发送的数据的地址。
参数三是要发送的数据的长度。
参数四是最长的等待时间,因为流缓冲区有可能满了写不进去。
接下来是从流缓冲区中取数据。
size_t xStreamBufferReceive(StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait)
参数和发送的类似,句柄,地址,长度,时间。
有了上面三个函数基本上就能用了,接下来来个小例子看看我们怎么通过流缓冲区来进行任务间的数据传输。
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/stream_buffer.h"
StreamBufferHandle_t sbhandle;
void test(void*){
char data = 'a';
while(1){
xStreamBufferSend(sbhandle,(void*)&data,1,pdMS_TO_TICKS(100));
if(data == 'z') data = 'a';
else data++;
vTaskDelay(100/portTICK_PERIOD_MS);
}
}
void app_main(void) {
sbhandle = xStreamBufferCreate(1024,5);
char receiveBuff[1024];
size_t receiveSize = 0;
xTaskCreate(test,"test",1024,NULL,configMAX_PRIORITIES/2,NULL);
while (1) {
receiveSize = xStreamBufferReceive(sbhandle,(void*)receiveBuff,1024,pdMS_TO_TICKS(1000));
if(receiveSize != 0){
printf("size is %d ",receiveSize);
receiveBuff[receiveSize] = '\0';
printf("receive data is %s\r\n",receiveBuff);
}
}
}
这边有个小问题我没找到原因,那就是流缓冲区设置了5个字节才能接收,但是第一次接收的时候只接收了一个,修改了接收字节的设置之后,第一次接收也还是只接收一个。知道为什么的小伙伴可以在评论区说一下。
接下来再介绍一个相关函数。
void vStreamBufferDelete(StreamBufferHandle_t xStreamBuffer)
删除流缓冲区。
BaseType_t xStreamBufferIsFull(StreamBufferHandle_t xStreamBuffer)
查询流缓冲区是否满了。
BaseType_t xStreamBufferIsEmpty(StreamBufferHandle_t xStreamBuffer)
查询流缓冲区是否为空。
BaseType_t xStreamBufferReset(StreamBufferHandle_t xStreamBuffer)
重置流缓冲区,就是清空。
虽然数据的大小没有限制了,但是如果两个数据同时(不会真的同时,而是时间非常接近)塞进了流缓冲区,我们就很难从一堆数据中区分成两个数据了(同一个数据不会被拆开,假设一个数据是“aaa”,另一个数据是“bbb”,那么“同时”被塞进流缓冲区之后,流缓冲区中的数据会是“aaabbb”或者是“bbbaaa”,而不会是“ababab”之类的)。
我们还有一个办法,那就是消息缓冲区。
消息缓冲区是基于流缓冲区的,但是它做了改进,那就是会帮我们把不同批次发出的数据拆分出来。
操作消息缓冲区的API只有宏,因为它都是封装的流缓冲区的函数。
创建消息缓冲区。
xMessageBufferCreate(xBufferSizeBytes)
只需要传入消息缓冲区的总大小。
发送数据进消息缓冲区。
从消息缓冲区读取数据。
我们只需要把流缓冲区的函数中的StreamBuffer改成MessageBuffer就变成了消息缓冲区的API了。甚至参数都不需要改,除了上面创建句柄的那个。
下面来个小例子直接来个小例子大家就能看懂,我们往消息缓冲区中塞进随机长度的数据,然后再读取出来。
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/message_buffer.h"
#include "string.h"
#include "esp_random.h"
MessageBufferHandle_t mbhandle;
void getRandomString(char* s){ //获取长度在10以内的随机字符串
uint32_t len = esp_random() % 10 + 1;
for(int i = 0; i < len; ++i){
s[i] = 'a' + (esp_random() % 26);
}
s[len] = '\0';
}
void test(void*){
char senddata[1024] = {0};
while(1){
getRandomString(senddata);
printf("send data size is %d data is %s\r\n",strlen(senddata),senddata);
xMessageBufferSend(mbhandle,(void*)senddata,strlen(senddata),pdMS_TO_TICKS(500));
vTaskDelay(500/portTICK_PERIOD_MS);
}
}
void app_main(void) {
char receiveBuff[1024];
size_t receiveSize = 0;
mbhandle = xMessageBufferCreate(1024);
xTaskCreate(test,"test",1024*2,NULL,configMAX_PRIORITIES/2,NULL);
while (1) {
receiveSize = xMessageBufferReceive(mbhandle,(void*)receiveBuff,1024,pdMS_TO_TICKS(1000));
if(receiveSize !=0){
receiveBuff[receiveSize] = '\0';
printf("size is %d receive data is %s\r\n",receiveSize,receiveBuff);
}
}
}