驱动开发,队列,环形缓冲区:以GD32 CAN 消息处理为例

对环形缓冲区进行进一步的优化和功能扩展,以应对更复杂的实际应用场景,特别是针对 CAN 总线消息处理的场景。

一、优化点

1:动态配置环形缓冲区大小在原始实现中,我们固定了缓冲区大小为 RINGBUFF_LEN = 64。这种方式虽然简单,但在实际应用中缺乏灵活性。例如,在资源受限的嵌入式系统中,我们可能需要根据内存情况和数据流量动态调整缓冲区大小。

我们通过修改初始化函数,引入动态缓冲区容量配置:

void ring_buffer_init(can_ring_buffer_t *rb, can_receive_message_struct *buffer, uint32_t capacity, uint8_t mode) 
{    
rb->buffer = buffer;      
rb->in_index = 0;    
rb->out_index = 0;    
rb->length = 0;    
rb->capacity = capacity;  // 动态配置缓冲区大小    
rb->mode = mode;
}

在 main 函数中,我们可以通过调整传入的 capacity 参数,轻松设置缓冲区大小:

can_receive_message_struct recv_buf[50]; can_ring_buffer_t can_ring_buffer;ring_buffer_init(&can_ring_buffer, recv_buf, 50, RING_BUFFER_MODE_NORMAL);

这种方式使缓冲区大小完全可控,能够适应不同场景下的需求。
二、优化点

2:增强错误处理机制在实际系统运行中,缓冲区溢出或数据丢失是常见的问题。我们对写入函数进行了改进,增加了对缓冲区状态的详细检查和处理:

int ring_buffer_write(can_ring_buffer_t *rb, can_receive_message_struct *msg) 
{    
if (rb->length < rb->capacity) 
{        
rb->buffer[rb->in_index] = *msg;        
rb->in_index = (rb->in_index + 1) % rb->capacity;        
rb->length++;        
return 1;    
} 
else 
{        
if (rb->mode == RING_BUFFER_MODE_OVERWRITE) {            
rb->buffer[rb->in_index] = *msg;            
rb->in_index = (rb->in_index + 1) % rb->capacity;            
rb->out_index = (rb->out_index + 1) % rb->capacity;            
return 1;        
} 
else 
{            
return 0;  // 在正常模式下,丢弃数据        
}    
}}

在缓冲区满的情况下,根据模式决定是否覆盖旧数据。
我们还提供了状态检查函数:

int ring_buffer_is_empty(can_ring_buffer_t *rb) 
{    
return rb->length == 0;
}
int ring_buffer_is_full(can_ring_buffer_t *rb) 
{    
return rb->length == rb->capacity;
}

这些函数可以帮助开发者实时监控缓冲区状态,及时发现潜在问题。

三、优化点
3:提供缓冲区状态监控功能为了更好地理解缓冲区的使用情况,我们增加了缓冲区占用百分比计算功能:

float ring_buffer_occupancy(can_ring_buffer_t *rb) {    
return (float)rb->length / rb->capacity * 100.0f;
}

在 main 函数中,我们可以通过以下方式输出缓冲区占用情况:

printf("Buffer occupancy: %.2f%%\n", ring_buffer_occupancy(&can_ring_buffer));

这个功能对于系统调优和资源分配具有重要意义。

四、优化点
4:改进消息处理流程我们完善了消息读取和处理函数,使其能够批量处理缓冲区中的消息:

int read_messages_from_buffer(can_ring_buffer_t *rb) 
{    
can_receive_message_struct msg;    
int read_count = 0;    
while (!ring_buffer_is_empty(rb)) 
{        
if (ring_buffer_read(rb, &msg)) 
{            
// 处理读取到的 CAN 消息            
printf("Received CAN message:\n");            printf("SFID: 0x%X, EFID: 0x%X, FF: %d, FT: %d, DLEN: %d\n",                    msg.rx_sfid, msg.rx_efid, msg.rx_ff, msg.rx_ft, msg.rx_dlen);            
printf("Data: ");            
for (int i = 0; i < msg.rx_dlen; i++) 
{                
printf("%d ", msg.rx_data[i]);            
}            
printf("\n");            
read_count++;        
}    
}    
return read_count;
}

在 main 函数中调用:

int read_count = read_messages_from_buffer(&can_ring_buffer);
printf("Total messages read: %d\n", read_count);

这种批量处理方式提高了系统效率。

五、完整代码实现
以下是优化后的完整代码实现:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>  // 用于动态内存分配#define RING_BUFFER_MODE_OVERWRITE 1
#define RING_BUFFER_MODE_NORMAL 0// CAN接收消息结构体
typedef struct 
{    
uint32_t rx_sfid;   /*!< standard format frame identifier */    
uint32_t rx_efid;   /*!< extended format frame identifier */    
uint8_t rx_ff;      /*!< format of frame, standard or extended format */    
uint8_t rx_ft;      /*!< type of frame, data or remote */    
uint8_t rx_dlen;    /*!< data length */    
uint8_t rx_data[8]; /*!< receive data */    
uint8_t rx_fi;      /*!< filtering index */} 
can_receive_message_struct;// 环形缓冲区结构体
typedef struct 
{    
can_receive_message_struct *buffer;  // 指向缓冲区的指针    
uint32_t in_index;    
uint32_t out_index;    
uint32_t length;    
uint32_t capacity;    
uint8_t mode;
} can_ring_buffer_t;// 初始化环形缓冲区void ring_buffer_init(can_ring_buffer_t *rb, can_receive_message_struct *buffer, uint32_t capacity, uint8_t mode) 
{    
rb->buffer = buffer;      
rb->in_index = 0;    
rb->out_index = 0;    
rb->length = 0;    
rb->capacity = capacity;  // 动态配置缓冲区大小    
rb->mode = mode;
}// 写入数据int ring_buffer_write(can_ring_buffer_t *rb, can_receive_message_struct *msg) 
{    
if (rb->length < rb->capacity) 
{        
rb->buffer[rb->in_index] = *msg;        
rb->in_index = (rb->in_index + 1) % rb->capacity;        
rb->length++;        
return 1;    
} 
else 
{        
if (rb->mode == RING_BUFFER_MODE_OVERWRITE) {            
rb->buffer[rb->in_index] = *msg;            
rb->in_index = (rb->in_index + 1) % rb->capacity;            
rb->out_index = (rb->out_index + 1) % rb->capacity;            
return 1;        
} 
else 
{            
return 0;  // 在正常模式下,丢弃数据        
}    
}
}// 读取数据int ring_buffer_read(can_ring_buffer_t *rb, can_receive_message_struct *msg) 
{    
if (rb->length > 0) 
{        
*msg = rb->buffer[rb->out_index];        
rb->out_index = (rb->out_index + 1) % rb->capacity;        
rb->length--;        
return 1;    
} 
else 
{        
return 0;  // 如果没有数据,返回0    
}
}// 检查是否为空int ring_buffer_is_empty(can_ring_buffer_t *rb) 
{    
return rb->length == 0;
}// 检查是否已满int ring_buffer_is_full(can_ring_buffer_t *rb) {    
return rb->length == rb->capacity;}// 获取缓冲区占用的百分比float ring_buffer_occupancy(can_ring_buffer_t *rb) {    
return (float)rb->length / rb->capacity * 100.0f;
}// 读取并处理缓冲区中的消息int read_messages_from_buffer(can_ring_buffer_t *rb) 
{    
can_receive_message_struct msg;    
int read_count = 0;    while (!ring_buffer_is_empty(rb)) 
{        
if (ring_buffer_read(rb, &msg)) 
{            
// 处理读取到的 CAN 消息            
printf("Received CAN message:\n");            
printf("SFID: 0x%X, EFID: 0x%X, FF: %d, FT: %d, DLEN: %d\n",                    msg.rx_sfid, msg.rx_efid, msg.rx_ff, msg.rx_ft, msg.rx_dlen);            
printf("Data: ");            for (int i = 0; i < msg.rx_dlen; i++) 
{                
printf("%d ", msg.rx_data[i]);            
}            
printf("\n");            
read_count++;        
}    
}    return read_count;}// 模拟接收CAN消息的中断处理函数void CAN1_IRQHandler(can_ring_buffer_t *rb) {    
can_receive_message_struct temp;    // 模拟从硬件获取 CAN 消息    
temp.rx_sfid = 0x123;    
temp.rx_efid = 0x1ABC;    
temp.rx_ff = 0;    
temp.rx_ft = 1;    
temp.rx_dlen = 8;    
for (int i = 0; i < 8; i++) 
{        
temp.rx_data[i] = i;    
}    
temp.rx_fi = 1;    
if (rb->length < rb->capacity) 
{        
ring_buffer_write(rb, &temp);    
} 
else 
{        
if (rb->mode == RING_BUFFER_MODE_OVERWRITE) {            
ring_buffer_write(rb, &temp);        
}    
}
}int main(void) 
{    
// 定义接收缓存数组,容量为50    
can_receive_message_struct recv_buf[50];     // 初始化环形缓冲区,设置容量为50,正常模式    
can_ring_buffer_t can_ring_buffer;    
ring_buffer_init(&can_ring_buffer, recv_buf, 50, RING_BUFFER_MODE_NORMAL);    // 模拟接收10条CAN消息    
for (int i = 0; i < 10; i++) 
{        
CAN1_IRQHandler(&can_ring_buffer);      
}    // 输出缓冲区的占用情况    
printf("Buffer occupancy: %.2f%%\n", ring_buffer_occupancy(&can_ring_buffer));    // 读取并处理缓冲区中的消息    
int read_count = read_messages_from_buffer(&can_ring_buffer);    
printf("Total messages read: %d\n", read_count);    return 0;}

六、未来扩展方向

  1. 支持多线程环境:在多线程系统中,我们可以在写入和读取操作时添加互斥锁(mutex),防止数据竞争。例如:
// 在写入函数中添加锁保护
pthread_mutex_lock(&rb->mutex);// 写入操作
pthread_mutex_unlock(&rb->mutex);
  1. 进一步优化消息处理:可以根据 CAN 消息的内容添加过滤和优先级处理。例如,对特定 ID 的消息进行优先处理。
  2. 错误日志和统计:可以添加日志记录功能,记录缓冲区溢出、数据丢失等事件,便于系统调试和优化。通过这些优化和扩展,我们的环形缓冲区实现变得更加健壮和实用,能够更好地适应实际嵌入式系统中的 CAN 消息处理需求。

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

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

相关文章

SQL基础知识,MySQL学习(长期更新)

1、基本操作&#xff0c;增删查改 INSERT INTO 表名 (字段1, 字段2, ...) VALUES (值1, 值2, ...); DELETE FROM 表名 WHERE 条件 SELECT * FROM 表名 WHERE 条件 UPDATE 表名 SET 字段1 值, 字段2 值, ... WHERE 条件; SELECT * INTO 新表 FROM 旧表 WHERE… INSERT INTO 语…

Git(一):初识Git

文章目录 Git(一)&#xff1a;初识GitGit简介核心功能分布式特性结构与操作优势与适用场景 创建本地仓库git init配置name与email--global 工作区、暂存区与版本库git addgit commitcommit后.git的变化 Git(一)&#xff1a;初识Git Git简介 Git 是一个分布式版本控制系统&…

第19天:初级数据库学习笔记3

分组函数&#xff08;多行处理函数&#xff09; 即多个输入对应一个输出。前面讲的数据处理函数是单行处理函数。&#xff08;在公司中常说单&#xff0c;多行处理函数&#xff09; 分组函数包括五个&#xff1a; max&#xff1a;最大值min&#xff1a;最小值avg&#xff1a…

Windows11下搭建Raspberry Pi Pico编译环境

1. 系统与工具要求 PC平台&#xff1a; Windows 11 专业版 Windows GCC: gcc-15.1.0-64.exe GNU Make: 4.3 Git: 2.49.0 cmake: 4.0.2 python:3.12.11 Arm GNU Toolchain Downloads – Arm Developer 2. 工具安装与验证 2.1 工具安装 winget安装依赖工具&#xff08;Windows …

【C语言极简自学笔记】重讲运算符

一、算术操作符 算术操作符描述把两个操作数相加-第一个操作数减去第二个操作数*把两个操作数相乘/分子除以分母%取模运算符&#xff0c;整除后的余数 注意&#xff1a;1.除号的两端都是整数的时候执行的是整数的除法&#xff0c;两端只要有一个浮点数&#xff0c;就执行浮点…

持续集成 CI/CD-Jenkins持续集成GitLab项目打包docker镜像推送k8s集群并部署至rancher

Jenkins持续集成GitLab项目 GitLab提交分支后触发Jenkis任务 之前是通过jar包在shell服务器上进行手动部署&#xff0c;麻烦且耗时。现通过Jenkins进行持续集成实现CI/CD。以test分支为例 提交即部署。 由于是根据自己实际使用过程 具体使用到了 gitlabjenkinsdockerharborra…

Apache Iceberg与Hive集成:非分区表篇

引言 在大数据处理领域&#xff0c;Apache Iceberg凭借其先进的表格式设计&#xff0c;为大规模数据分析带来了新的可能。当Iceberg与Hive集成时&#xff0c;这种强强联合为数据管理与分析流程提供了更高的灵活性和效率。本文将聚焦于Iceberg与Hive集成中的非分区表场景&#…

webpack 如何区分开发环境和生产环境

第一种方法: 方法出处&#xff1a;命令行接口&#xff08;CLI&#xff09; | webpack 中文文档 1.利用webpack.config.js 返回的是个函数&#xff0c;利用函数的参数&#xff0c;来区分环境 具体步骤 1&#xff09; package.json文件&#xff1a;在npm scripts 命令后面追加 …

React组件通信——context(提供者/消费者)

Context 是 React 提供的一种组件间通信方式&#xff0c;主要用于解决跨层级组件 props 传递的问题。它允许数据在组件树中"跨级"传递&#xff0c;无需显式地通过每一层 props 向下传递。 一、Context 核心概念 1. 基本组成 React.createContext&#xff1a;创建 C…

“微信短剧小程序开发指南:从架构设计到上线“

1. 引言&#xff1a;短剧市场的机遇与挑战 近年来&#xff0c;短视频和微短剧市场呈现爆发式增长&#xff0c;用户碎片化娱乐需求激增。短剧小程序凭借轻量化、社交传播快、变现能力强等特点&#xff0c;成为内容创业的新风口。然而&#xff0c;开发一个稳定、流畅且具备商业价…

RPC与RESTful对比:两种API设计风格的核心差异与实践选择

# RPC与RESTful对比&#xff1a;两种API设计风格的核心差异与实践选择 ## 一、架构哲学与设计目标差异 1. **RPC&#xff08;Remote Procedure Call&#xff09;** - **核心思想**&#xff1a;将远程服务调用伪装成本地方法调用&#xff08;方法导向&#xff09; - 典型行为…

【pytest进阶】pytest之钩子函数

什么是 hook (钩子)函数 经常会听到钩子函数(hook function)这个概念,最近在看目标检测开源框架mmdetection,里面也出现大量Hook的编程方式,那到底什么是hook?hook的作用是什么? what is hook ?钩子hook,顾名思义,可以理解是一个挂钩,作用是有需要的时候挂一个东西…

深度学习计算——动手学深度学习5

环境&#xff1a;PyCharm python3.8 1. 层和块 块&#xff08;block&#xff09;可以描述 单个层、由多个层组成的组件或整个模型本身。 使用块进行抽象的好处&#xff1a; 可将块组合成更大的组件(这一过程通常是递归) 如 图5.1.1所示。通过定义代码来按需生成任意复杂度…

NodeJS的fs模块的readFile和createReadStream区别以及常见方法

Node.js 本身没有像 Java 那样严格区分字符流和字节流&#xff0c;区别主要靠编码&#xff08;encoding&#xff09;来控制数据是以 Buffer&#xff08;二进制字节&#xff09;形式还是字符串&#xff08;字符&#xff09;形式处理。 详细解释&#xff1a; 方面JavaNode.js字节…

基于二进制XOR运算的机器人运动轨迹与对称图像自动生成算法

原创&#xff1a;项道德&#xff08;daode3056,daode1212&#xff09; 新的算法出现&#xff0c;往往能给某些行业与产业带来革命与突破。为探索机器人运动轨迹与对称图像自动生成算法&#xff0c;本人已经通过18种算法的测试&#xff0c;最终&#xff0c;以二进制的XOR运算为…

Spring AI 项目实战(七):Spring Boot + Spring AI Tools + DeepSeek 智能工具平台(附完整源码)

系列文章 序号文章名称1Spring AI 项目实战(一):Spring AI 核心模块入门2Spring AI 项目实战(二):Spring Boot + AI + DeepSeek 深度实战(附完整源码)3Spring AI 项目实战(三):Spring Boot + AI + DeepSeek 打造智能客服系统(附完整源码)4Spring AI 项目实战(四…

spring-webmvc @RequestHeader 典型用法

典型用法 基础用法&#xff1a;获取指定请求头值 GetMapping("/info") public String getInfo(RequestHeader("User-Agent") String userAgent) {return "User-Agent: " userAgent; }如果请求中包含 User-Agent 请求头&#xff0c;则其值将被…

Ubuntu:20.04中安装docker

是的&#xff0c;您列出的命令是完整的安装步骤&#xff0c;但为了确保100%可靠性和处理可能的问题&#xff0c;我建议进行以下优化和补充&#xff1a; 完整优化的安装脚本&#xff08;包含错误处理和验证&#xff09; #!/bin/bash# 1. 彻底清理旧版本和配置 sudo apt remove…

大数据实时风控引擎:Spark Streaming、Kafka、Flink与Doris的融合实践

大数据实时风控引擎&#xff1a;Spark Streaming、Kafka、Flink与Doris的融合实践 在数字金融、电商交易与在线服务的核心战场&#xff0c;风险控制能力已成为业务的生命线。传统批量风控模式在应对瞬息万变的欺诈攻击、信用风险时捉襟见肘。本文将深入探讨如何利用**Spark St…

【创龙瑞芯微 RK3576 全国产 ARM 八核 2.2GHz 工业开发板-硬件说明书】

前 言 本文主要介绍TL3576-EVM评估板硬件接口资源以及设计注意事项等内容。 RK3576J/RK3576处理器的IO电平标准一般为1.8V、3.3V,上拉电源一般不超过3.3V或1.8V,当外接信号电平与IO电平不匹配时,中间需增加电平转换芯片或信号隔离芯片。按键或接口需考虑ESD设计,ESD器件…