【研发日记】C/C++开发避坑秘籍(一)——CAN接收Buffer溢出Bug

文章目录

背景介绍

问题描述

分析排查

解决方案

总结归纳


背景介绍

        在一个嵌入式软件项目中,有一段使用C语言写的嵌入式代码,功能是把CAN总线上的几帧报文接收进来,并解析出数据。示例如下:

        乍一看感觉挺简单,想着直接用一个while循环,周期提取CAN buffer中缓存的数据就行了。但是就这么一个小应用让我栽了跟头,在整个工程中排查了几个小时才找到问题,下面就来分享一下这一小段个人经历。

        一开始代码调试的时候很顺利,一共接收5帧报文,总共40个信号,相关代码大概是下面这个样子:

void main(void)
{
//省略若干行if(Timer10ms == 1){do{ret = GetCANFrame(1, &Ext, &ID, &Length, &DataBuf);if(ret){if(ID == 0x100){memcpy(&SignalArray[0], DataBuf, 8)}if(ID == 0x101){memcpy(&SignalArray[8], DataBuf, 8)}if(ID == 0x102){memcpy(&SignalArray[16], DataBuf, 8)}if(ID == 0x103){memcpy(&SignalArray[24], DataBuf, 8)}if(ID == 0x104){memcpy(&SignalArray[32], DataBuf, 8)}}}while(ret && (i <= 100))}//省略若干行	
}

问题描述

        按照上述示例开发的软件,烧录到ECU中在实验室桌面上调试,使用Vector的CAN接口卡模拟残余总线发送0x100、0x101、0x102、0x103、0x104这五帧报文,ECU都能正常接收。但是把ECU集成到实车之后,却收到反馈ECU的CAN接收有丢帧现象。

分析排查

        把实车上的CAN报文Log打开之后,看到总线上除了0x100、0x101、0x102、0x103、0x104这五帧报文之外,还有几十个其他ID的报文,所以推测是这些残余总线报文干扰了ECU的CAN接收。所以在实验室桌面上把这些残余总线模拟出来,复现了实车上的CAN接收丢帧现象,这就锁定了是残余总线的问题。

        为了更具体的分析Bug机理,在代码中加入了一些调试信息,每个ID接收到之后都把相应的计数加1,示例如下:

void main(void)
{
//省略若干行if(Timer10ms == 1){do{ret = GetCANFrame(1, &Ext, &ID, &Length, &DataBuf);if(ret){if(ID == 0x100){memcpy(&SignalArray[0], DataBuf, 8)FrameCount[0]++;}if(ID == 0x101){memcpy(&SignalArray[8], DataBuf, 8)FrameCount[1]++;}if(ID == 0x102){memcpy(&SignalArray[16], DataBuf, 8)FrameCount[2]++;}if(ID == 0x103){memcpy(&SignalArray[24], DataBuf, 8)FrameCount[3]++;}if(ID == 0x104){memcpy(&SignalArray[32], DataBuf, 8)FrameCount[4]++;}}}while(ret && (i <= 100))}//省略若干行	
}

        烧录上述软件后可以看到,发生CAN接收丢帧时都伴随着残余总线报文的集中突增,总线瞬时100%负载率会持续十多ms,所以推测可能是残余总线报文挤占了CAN接收buffer。翻了一下CAN接收buffer的配置,是40帧的size。根据CAN总线500k的波特率计算100%负载率持续十多ms总线上会有50帧左右的报文。而我的代码是10ms从CAN接收buffer提取一次数据,这样必然会导致CAN接收buffer塞满后无法继续接收,也就是丢帧的现象。

解决方案

        针对上述CAN接收buffer溢出的问题,解决方案可以从如下公式的每一个因子入手:


        Buffer占用空间 = 单位时间最多的报文数量 * 提取Buffer的周期                    公式(1)


        方案一:扩大buffer size配置其他条件不变,这里只要把原本40帧的size扩大到45帧左右即可。因为500k波特率的CAN总线,10ms时间内最多产生的报文不会超过45帧。这个方案需要MCU硬件资源的支持。

        方案二:避免报文突增其他条件不变,这里只要在CAN协议中约定好,各个ECU节点都按既定节拍发送报文,避免瞬时负载率拉升到100%,或者控制100%负载率持续时间要小于10ms。这样任何一个10ms周期内,总线上的报文最多也不会超过40帧,现有的40帧的Buffer size就够用了。这个方案需要总线上各个节点ECU的配合。

        方案三:降低波特率其他条件不变,这里只要把原本500k的波特率降下来,也就降低了单位时间最多的报文数量,实现的原理跟方案二一样。这个方案也需要总线上各个节点ECU配合变更波特率。

        方案四:CAN接收ID过滤其他条件不变,这里只要在底层CAN接收模块中做一个ID过滤,只允许0x100、0x101、0x102、0x103、0x104这五帧报文进入CAN接收buffer,实现的原理也跟方案二一样。这个方案只需要自己的ECU修改底层软件即可,不需要其他方面的支持和配合。

        方案五:缩短提取Buffer的周期其他条件不变,这里只要把原本10ms的提取周期改成5ms或者其他小于10ms的数值即可。因为因为500k波特率的CAN总线,5ms时间内最多产生的报文在20帧左右,远远低于40帧的buffer空间。这个方案只需要自己的ECU修改应用软件即可,不需要其他方面的支持和配合。

        上述五种解决方案各有优劣,适用于不同的项目开发情景。方案一适用于ECU开发的早期,在MCU选项时,或者在软件架构定型时,就根据CAN总线的应用需求为buffer size留出足够的资源空间。方案二和方案三适用于系统集成阶段,在制定整车的网络协议时,约定好各个ECU节点需要遵循的公共基础和公用策略。方案四和方案五适用于ECU的应用开发阶段,在开发具体功能时,更具其他已经设定的条件,决定如何设计ID过滤和报文提取周期。

总结归纳

        那么这个代码调试过程,发现的问题可以积累下来这么几条小经验以供自己将来使用,也给广大网友参考:

        1、CAN网络相关的模块设计与开发时,不光要考虑自身ECU节点的发送和接收,还要考虑其他节点不相关报文的实际存在。对于不能确定确定参数时,就要按照最严酷的情况来估算,比如残余总线瞬时负载率达到100%,而且还持续挺长一段时间。

        2、方案设计用的是等式(解决方案中的哪个等式),但是软件开发中用的是不等式,因为实际情况中要面对的有各种误差,所有在留下余量时,等式中的“=”就要变成“>”或者“<”。比如理论计算的buffer size是40帧,这里就应该使用>40帧。

        以上就是本人在解决CAN接收Buffer溢出Bug时,一些个人理解和分析的总结,首先介绍了基本的项目背景,然后描述了问题的想象,最后分析排查了Bug原因,并给出了问题解决方案。

        后续还会分享其他的,使用C/C++研发时遇到的Bug,欢迎评论区留言、点赞、收藏和关注,这些鼓励和支持都将成文本人持续分享的动力。

        述例程使用的Demo工程,可以到笔者的主页查找和下载。


        版权声明:原创文章,转载请注明出处与链接,违者必究!

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

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

相关文章

【算法】回溯与深搜

方法论 1.构建决策树 2.设计代码&#xff1a;全局变量、dfs函数 3.剪枝&#xff0c;回溯 全排列 给定一个不含重复数字的整数数组 nums &#xff0c;返回其 所有可能的全排列 。可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff…

微软Microsoft Surface Go 2

1个小玩具 Microsoft Surface Go 2的评测结果出炉&#xff01;它是目前最好的中端Windows 二合一笔记本平板。 外形简洁小巧&#xff0c;工作娱乐两不误。 它有多个版本。 我们测试的是配备8GB Ram和128GB SSD的Pentium 4425Y处理器&#xff08;第8代&#xff09;的型号。 S…

elementary OS7 Ubuntu 22.04中硬盘挂载报错

elementary OS7 Ubuntu 22.04中硬盘挂载报错 背景目标思路解决方法 背景 上周末安装elementaryos7的过程中将windows10的引导文件搞丢了&#xff0c;这两天准备修复一下&#xff0c;保险期间将固态硬盘上的文件备份到移动硬盘上&#xff0c;备份过程中出现报错的问题&#xff…

Axure RP 9 for Mac中文激活版:原型设计工具

Axure RP 9 for Mac是一款值得设计师信赖的原型设计工具。它以其卓越的性能和稳定的运行赢得了广大用户的赞誉。 软件下载&#xff1a;Axure RP 9 for Mac中文激活版下载 在Axure RP 9中&#xff0c;您可以尽情发挥自己的设计才华&#xff0c;创造出独一无二的原型作品。无论是…

电子电器架构 —— 诊断数据DTC起始篇(下)

电子电器架构 —— 诊断数据DTC起始篇(下) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师(Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,喝完再…

系统架构设计-构建系统应用

1. 系统架构目标与设计原则 在设计系统架构时&#xff0c;我们的目标是确保系统具有以下特点&#xff1a; 可靠性&#xff1a;系统能够持续稳定运行&#xff0c;保证业务可用性。可伸缩性&#xff1a;系统能够根据负载变化自动扩展或收缩&#xff0c;以应对不同的流量需求。容…

STM32通过串口发送指令控制LED灯亮灭OLED并显示命令

先来看看程序运行的结果吧&#xff1a; 接下来就不说废话了&#xff0c;自己看源代码吧&#xff01;每一行我都做了注释&#xff1a; 首先是主函数main.c文件&#xff1a; #include "stm32f10x.h" // Device header #include "OLED.h" …

Python工具-清理Unity(批量深度)清理U3D项目工程保留关键工程文件

前沿 1. Unity工程越来越多&#xff0c;很久不用的工程里存在了很多无用的大文件夹&#xff0c;极大的影响电脑容量。 2. 我电脑里面U3D工程只有17个&#xff0c;但容量就高达60GB&#xff0c;使用自己编写的工具清理后&#xff0c;减到了30GB多。清理了不是很重要的文件和文件…

【CTA动画】制作全记录 笔记

3Dxchange的使用 让图片跳舞 导入&#xff1a;I:\安装包\#动画开发\test\跳舞 model(includeTPose).fbx 转成非标准角色 手动点击骨骼&#xff0c;然后点击人物骨骼&#xff0c;选择00_t-pose 绿灯了就可以转换了&#xff0c;记得启用。 上面的自定义可以先选择3DS 转换后…

Java数据结构-顺序表

目录 1. 顺序表的相关概念1.1 线性表1.2 顺序表2. 功能实现2.1 整体框架2.2 乱七八糟的功能(bushi)2.2.1 判断容量是否满2.2.2 返回顺序表当前长度2.2.3 扩容2.2.4 清空整个顺序表 2.3 插入数据2.3.1 头插数据2.3.2 尾插数据2.3.3 指定位置插入 2.4 删除数据2.4.1 删除第一次出…

微服务之Nacos配置管理

文章目录 前言一、统一配置管理Nacos操作二、统一配置管理java操作1.引入依赖2.创建配置文件3.测试4.总结 三、Nacos配置自动更新1.添加注解RefreshScope2.使用ConfigurationProperties注解3.总结 四、Nacos多环境配置共享1.配置文件2.多种配置的优先级3.总结 总结 前言 一、统…

leetcode 150.逆波兰表达式求值

题目 思路 逆波兰表达式也是经典的栈的应用问题。 先说什么是逆波兰表达式&#xff08;也叫后缀表达式&#xff09; 我们习惯的是这样的表达式&#xff1a;1 2 / 3 ,这也叫中缀表达式。 但是对于计算机来说不好理解&#xff0c;当从左扫描到 2 的时候还需要再判断2后面是什…

STM32 | Systick定时器(第四天源码解析)

STM32 | Systick定时器(第四天)STM32 | STM32F407ZE中断、按键、灯(续第三天)1、参考delay_us代码,完成delay_ms的程序 定时器频率换算单位:1GHZ=1000MHZ=1000 000KHZ = 1000 000 000HZ 定时器定时时间:计数个数/f(频率) 或者 (1/f(频率))*计数的个数 500/1MHZ = 500/1…

C++面向对象三大特征-----继承(详细版)

目录 继承 一、继承的基础介绍 普通版网页和继承版网页的区别 语法 二、继承方式 三种继承方式 三、继承中的对象模型 四、继承中构造和析构函数 五、继承同名成员的处理方式 访问同名成员&#xff1a; 作用域写法&#xff1a; 六、继承同名静态成员的处理方式 访问…

Qt播放音乐代码示例

主界面 点击play按钮播放或暂停音乐&#xff0c;拖动进度条&#xff0c;音乐对应播放。 QWidget window;QPushButton* playButton new QPushButton("Play");// Qt 播放音乐// 创建 QMediaPlayer 对象QMediaPlayer* player new QMediaPlayer;// 指定音频文件的路径…

MySQL B+树索引 和 Redis 中跳表索引的区别

一、MySQL B树索引 和 Redis 中跳表索引 在 MySQL 中常用的索引是 B树索引&#xff0c;而 Redis 中&#xff0c;例如 zset 使用的的是跳表索引&#xff0c;两者有什么区别呢&#xff0c;MySQL 为什么不使用 跳表 呢&#xff1f;或者说 Redis 中为什么不使用 B树 呢&#xff1f…

章节10实验--Ubuntu18.04 Qt MySQL libqsqlmysql.so

前言: 内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容&#xff0c;所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。 引用: 《操作系统实践-基于Linux应用与内核编程》 作者&#xff1a;房胜、李旭健、黄…

golang 对接第三方接口 RSA 做签(加密) 验签(解密)

一、过程 1.调用第三方接口前&#xff0c;一般需要按规则将参数按key1value1&key2value2 阿斯克码排序,sign参数不参与加密 2.将排序并连接好的参数字符串通过我方的私钥证书&#xff08;.pem&#xff09;进行加密得到加密串&#xff0c;当然加密得到的是 []byte 字节流&…

看完就等于拿捏浮点数在内存中的储存了

诸君又该学习了&#xff0c;今天我们继续来一睹浮点数的奥妙真容。 经过前面文章对整形提升相关的解释&#xff0c;我们都对整形和字符在内存空间上的储存已经有了大概的认知&#xff0c;那么现在我们就来好好讲讲浮点数在内存中的储存规则。 目录 浮点数与整形储存的不同 …

41-Vue-webpack基础

webpack基础 前言什么是webpackwebpack的基本使用指定webpack的entry和output 前言 本篇开始来学习下webpack的使用 什么是webpack webpack: 是前端项目工程化的具体解决方案。 主要功能&#xff1a;它提供了友好的前端模块化开发支持&#xff0c;以及代码压缩混淆、处理浏览…