FreeRTOS入门(07):流缓冲区 消息缓冲区

news2025/3/1 1:42:18

文章目录

  • 目的
  • 基础说明
  • 流缓冲区
    • 相关函数
    • 使用演示
  • 消息缓冲区
    • 相关函数
    • 使用演示
  • 总结

目的

缓冲区是操作系统中常见的一种用于任务间数据传递的机制。这篇文章将对FreeRTOS中相关内容做个介绍。

本文代码测试环境见前面的文章:《FreeRTOS入门(01):基础说明与使用演示》

基础说明

FreeRTOS中的缓冲区是v10.0.0新加入的功能,可能该功能还在调整中,部分内容官方文档和实际的库中有些出入。

缓冲区相当于一片共享的内存,一方面中断和任务中都可以向里面写输入,另一方面中断和任务也可以从里面读数据。这在一些现有的机制不能满足或者大数据传输时比较有用。不过需要注意的是目前FreeRTOS中的缓冲区并不是完全是线程安全的,使用是需要特别注意。

流缓冲区 有一个叫做 触发等级 的参数,只有当向流缓冲区写入数据后,缓冲区中可读数据大于等于触发等级时,才可以从中读取数据,否则将阻塞。但是只要可读取后便可多次读取直到没有数据可读为止。

消息缓冲区 是建立在流缓冲区之上的,但是提供了另外一种机制。每次向消息缓冲区发送的消息都会成为一个消息包。当消息缓冲区中有消息包便可以读取。每次触发读取都是以包为单位读取的。

使用消息缓冲区需要特别注意两点:1、每次向消息缓冲区发送数据时都会额外占用一定空间用于记录这条消息的长度(对于32架构来说将额外占用4字节);读取时以完整的包形式读取,如果接收缓存的长度不足,则一个字节都不会接受,也不会阻塞,会直接返回接收0字节,并且消息缓冲区这条消息还存在,等待下一次接收。

FreeRTOS的缓冲区有 sbSEND_COMPLETED()sbRECEIVE_COMPLETED() 两个宏用来处理发送和接收完成后的额外操作,你可以自行定义这两个宏具体的功能,也可以不管,FreeRTOS默认已经封装了一些基础的操作。

流缓冲区

相关函数

// 创建流缓冲区
// xBufferSizeBytes表示流缓冲区容量(字节)
// xTriggerLevelBytes读取等待字节数(触发等级),如果写0会被重置为1
StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes );

// 删除流缓冲区
void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer );

// 向流缓冲区写数据,如果空间不足无法写入将阻塞直至超时
// pvTxData为要写入的消息首指针
// xDataLengthBytes为要写入的消息长度,写入时会额外占用sizeof(size_t)字节空间,对于32架构来说将额外占用4字节
// 返回写入字节数
size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait );
// 写数据中断版本,不会阻塞
size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t xDataLengthBytes, BaseType_t *pxHigherPriorityTaskWoken );

// 从流缓冲区读数据,如果没有数据可读取将阻塞直至超时
// pvRxData为要接收数据的数组首指针
// xBufferLengthBytes接收消息的数组长度
// 返回读取字节数
size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait );
// 读数据中断版本,不会阻塞
size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t xBufferLengthBytes, BaseType_t *pxHigherPriorityTaskWoken );

// 返回缓冲区可读字节数
size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer );
// 返回缓冲区可写字节数
size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer );
// 查看缓冲区是否为空
BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer );
// 查看缓冲区是否已满
BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer );

// 设置缓冲区触发等级
BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel );
// 将缓冲区重置为空,只有缓冲区没有在使用时才可设置
BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer );

使用演示

#include "debug.h"
#include "FreeRTOS.h"      // 引入头文件
#include "task.h"          // 引入头文件
#include "stream_buffer.h" // 引入头文件

StreamBufferHandle_t xStreamBuffer; // 流缓冲区句柄

void task1(void *pvParameters) {
    uint8_t data[4] = {1, 2, 3, 4};
    while(1) {
        vTaskDelay(500);
        xStreamBufferSend(xStreamBuffer, data, 4, portMAX_DELAY); // 发送数据
    }
}

void task2(void *pvParameters) {
    while(1) {
        uint8_t data[5] = {0};
        size_t len = xStreamBufferReceive(xStreamBuffer, data, 5, portMAX_DELAY); // 等待数据可接收
        printf("%u task2 len: %u  data: %u %u %u %u %u \r\n", xTaskGetTickCount(), len, data[0], data[1], data[2], data[3], data[4]);
    }
}

int main(void) {
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);

    xStreamBuffer = xStreamBufferCreate(128, 5); // 这里设置触发等级为5

    xTaskCreate(task1, "task1", 256, NULL, 5, NULL);
    xTaskCreate(task2, "task2", 256, NULL, 5, NULL);

    vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环

    while(1) {} // 程序不会运行到这里
}

在这里插入图片描述

消息缓冲区

相关函数

// 创建消息缓冲区
MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );

// 删除消息缓冲区
void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer );

// 向消息缓冲区写数据,如果空间不足无法写入将阻塞直至超时
// pvTxData为要写入的消息首指针
// xDataLengthBytes为要写入的消息长度,每次写入时会额外占用sizeof(size_t)字节空间,对于32架构来说将额外占用4字节
size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait );
// 写数据中断版本,不会阻塞
size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer, const void *pvTxData, size_t xDataLengthBytes, BaseType_t *pxHigherPriorityTaskWoken );

// 从消息冲区读数据,如果没有数据可读取将阻塞直至超时
// pvRxData为要接收数据的数组首指针
// xBufferLengthBytes接收消息的数组长度
// 返回读取字节数
// 从消息缓冲区中读取数据是以一个个消息为单位进行读取的
size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait );
// 读数据中断版本,不会阻塞
size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer, void *pvRxData, size_t xBufferLengthBytes, BaseType_t *pxHigherPriorityTaskWoken );

// 返回缓冲区可写字节数
size_t xMessageBufferSpacesAvailable( MessageBufferHandle_t xMessageBuffer );
// 查看缓冲区是否为空
BaseType_t xMessageBufferIsEmpty( MessageBufferHandle_t xMessageBuffer );
// 查看缓冲区是否已满
BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer );

// 将缓冲区重置为空,只有缓冲区没有在使用时才可设置
BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );

使用演示

#include "debug.h"
#include "FreeRTOS.h"       // 引入头文件
#include "task.h"           // 引入头文件
#include "message_buffer.h" // 引入头文件

MessageBufferHandle_t xMessageBuffer; // 消息缓冲区句柄

void task1(void *pvParameters) {
    uint8_t data[5] = {1, 2, 3, 4, 5};
    while(1) {
        vTaskDelay(500);
        xMessageBufferSend(xMessageBuffer, data, 5, portMAX_DELAY); // 发送数据,长度为5
    }
}

void task2(void *pvParameters) {
    uint8_t data[3] = {7, 7, 7};
    while(1) {
        vTaskDelay(1000);
        xMessageBufferSend(xMessageBuffer, data, 3, portMAX_DELAY); // 发送数据,长度为3
    }
}

void task3(void *pvParameters) {
    while(1) {
        uint8_t data[5] = {0};
        size_t len = xMessageBufferReceive(xMessageBuffer, data, 5, portMAX_DELAY); // 等待数据可接收
        printf("%u task3 len: %u  data: %u %u %u %u %u \r\n", xTaskGetTickCount(), len, data[0], data[1], data[2], data[3], data[4]);
    }
}

int main(void) {
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);

    xMessageBuffer = xMessageBufferCreate(128); // 创建消息缓冲区

    xTaskCreate(task1, "task1", 256, NULL, 5, NULL);
    xTaskCreate(task2, "task2", 256, NULL, 5, NULL);
    xTaskCreate(task3, "task3", 256, NULL, 5, NULL);

    vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环

    while(1) {} // 程序不会运行到这里
}

在这里插入图片描述

总结

流缓冲区和消息缓冲区的使用都比较简单,其实就有点像是一个公共的先进先出的队列,这种结构在传输数据流或是不定长数据时是比较常用的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/379517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink:The generic type parameters of ‘Collector‘ are missing 类型擦除

类型擦除问题处理报错日志描述问题描述报错解决其他方法方法一:TypeInformation方法二:TypeHint报错日志描述 报错日志: The generic type parameters of Collector are missing. In many cases lambda methods dont provide enough informa…

双边滤波和交叉双边滤波

什么是双边滤波 双边滤波是一种局部的、非线性的、非迭代的滤波技术,它将经典的低通滤波器与边缘停止函数相结合,当像素之间的强度差较大时,边缘停止函数会衰减滤波器的核心。由于同时考虑了相邻像素的灰度相似度和几何贴近度,滤…

使用开源httpworkshop执行http api测试与资源下载

平时我们在涉及HTTP开发时,会使用postman做api测试,优点是界面友好;平时我们写爬虫查找网络资源一般使用python编写脚本,优点是脚本支持的功能灵活;其实网络上做HTTP测试的工具和产品特别的多,但是都是做好…

锐捷(十六)mpls vxn option c 带RR场景

一 实验拓扑二 实验需求如图模拟R1在北京,R10在上海,要求二者之间用loopback口进行通信由于公司机密性质,要求二者需要使用mpls vxn 虚拟专线的方式进行通信本实验采取option c带RR的方式模拟通信左边AS100,右边AS200,…

19万字智慧城市总体规划与设计方案word

【版权声明】本资料来源网络,知识分享,仅供个人学习,请勿商用。【侵删致歉】如有侵权请联系小编,将在收到信息后第一时间删除!完整资料领取见文末,部分资料内容: 1.1.1 感知基础设施 感知基础设…

数据库三大范式和个人看法

三大范式图解概括 第一范式(1NF) 确保数据库表字段的原子性 会存在数据冗余过大,插入异常,删除异常,修改异常的问题 举例: 某个字段name:‘西瓜 1566666‘ 依照第一范式就需要拆分成 name:‘西瓜’ ,phone:1566666’ …

房产营销、地产中介如何高效低成本获客?

数字化对企业而言,机遇和挑战并存。房产企业可借助数字化加强日益扩大的业务规模和业务领域管理,以提升管理效率,降低管理难度;基于数字化技术加强客户的服务体验,进而收集多业态客户和场景数据,拓展创新业…

计算机组成原理(2.1)--系统总线

目录 一、总线基本知识 1.总线 2.总线的信息传送 3.分散连接图 4.注 二、总线结构的计算机举例 1.面向 CPU 的双总线结构框图 2.单总线结构框图 3.以存储器为中心的双总线结构框图 三、总线的分类 1.片内总线 2.系统总线 (板级总线或板间总线&#…

【leetcode】寻找重复数

题目链接:寻找重复数https://leetcode.cn/problems/find-the-duplicate-number/ 方法一:快慢指针 因为只有一个数字是重复的,且一个数字正好对应一个唯一的下标,所以可以将数组抽象为一个链表,假定数组为{1,2,3,4,5,…

Java内置队列和高性能队列Disruptor

一、队列简介 队列是一种特殊的线性表,遵循先入先出、后入后出(FIFO)的基本原则,一般来说,它只允许在表的前端进行删除操作,而在表的后端进行插入操作,但是java的某些队列运行在任何地方插入删…

拆解钉钉流程审批功能:卡片式配置的审批流是怎样设计的?

有个客户要求我们做个定制开发,客户觉得钉钉的操作体验比较不错(用户使用的惯性),要求我们要实现钉钉的自动配置审批流程,不过幸不辱命,对比个钉钉的流程审批、表单配置的相关功能,做了个Java私…

世界上最流行的编程语言,用户数超过Python,Java,JavaScript,C的总和!

世界上最流行的编程语言是什么? Python? Java? JavaScript? C?都不是,是Excel!外媒估计,全球有12亿人使用微软的Office套件,其中估计有7.5亿人使用Excel!可是Excel不就是能写点儿公式&#x…

华为OD机试题【合并数组】用 C++ 进行编码 (2023.Q1)

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明合并数…

计算机组成原理4小时速成:五大组成部分:运算器,控制器,存储器,输入设备输出设备

计算机组成原理4小时速成:五大组成部分:运算器,控制器,存储器,输入设备输出设备 2022找工作是学历、能力和运气的超强结合体,遇到寒冬,大厂不招人,可能很多算法学生都得去找开发&am…

BOSHIDA AC-DC电源模块基本原理及常见问题

BOSHIDA三河博电科技 AC-DC电源模块基本原理及常见问题 AC/DC电源模块的基本原理是从交流源中获取电压,然后由整流器转换为直流电压,且输出能量比输入的电压高,从而达到电路输出直流电压、电流的目的。 交流交换器模块由负责控制的设备,整流器,电容器和变压器组成。负责控制的设…

恢复谷歌翻译的究极方法

谷歌翻译为什么会失效,我想各位在去年11月的时候就知道了。可是要怎么解决失效的问题呢?之前我们是通过手动Ping可以连接的ip各位可能觉得麻烦,心里觉得什么档次还要我手动ping就没有可以自动扫描的吗?还别说真的有我最近发现一个…

GVINS论文中关于GNSS的基础知识

1.GNSS发射信号的组成 卫星所发射的信号可分为三个层次:载波(Carrier signal)、伪码(PRN code伪码中的一种)和数据码(Navigation message)。在这三个层次中,伪码和数据码一起先通过调制依附在正弦波形式上的载波,然后卫星将调制后的载波信号…

外贸建站,为什么别人的询盘更多更精准?

大多企业进行外贸建站的目的就是想要获得更多的精准询盘,但是具体该如何做,大多企业都没有方向,要么就是在网上看各种不系统的文章学着操作,要么就找个建站公司做好网站就不管了,而最终结果都不甚理想。那么怎样才能让…

The KINGDOM Software 32-bit导入segy数据

说明 1、软件:The KINGDOM Software 32-bit 2、电脑系统:win10 3、操作:导入segy数据(如果读者没有数据,可使用下面会用到的示例数据) 步骤 1、打开kingdom软件,找到最上面菜单栏的“surve…

php学习笔记 安装XAMPP hbuilder配置

PHP7.0编程 基础配置 Download XAMPP (apachefriends.org) 下载XAMPP,修改端口如图 网站根目录的默认路径:xampp\htdocs。MySQL数据库默认路径:xampp\mysql\data。 XAMPP的默认密码如下。 ●MySQL。User:root;Pas…