基于CAPL的S19文件解析

  • 🍅 我是蚂蚁小兵,专注于车载诊断领域,尤其擅长于对CANoe工具的使用
  • 🍅 寻找组织 ,答疑解惑,摸鱼聊天,博客源码,点击加入👉【相亲相爱一家人】
  • 🍅 玩转CANoe,博客目录大全,点击跳转👉

📘前言

  • 🍅 车载测试必不可少的是刷写,行业内有很多格式的刷写文件,S19,HEX,BIN,还有一些主机厂自定义的比如Volvo/GeeaVBFCheryCBF

  • 🍅 本章节先了解S19文件

请添加图片描述

目录

  • 📘前言
  • 📙 S19 文件的格式简介
    • 🍅 标准解释
    • 🍅 实例说明
    • 🍅 HEX View 神器
  • 📙CAPL解析S19文件源码
  • 🌎总结

请添加图片描述


📙 S19 文件的格式简介

🍅 标准解释

  • Motorola S-record是由Motorola创建的一种文件格式,它以 ASCII十六进制文本形式传送二进制信息。这种文件格式也可以称为SRECORD、SREC、S19、S28、S37。它通常用于对微控制器、EPROM 和其他类型的可编程逻辑设备进行编程。

  • S-record格式是在1970年代中期为 Motorola 6800处理器创建的。该处理器和其他嵌入式处理器的软件开发工具将生成S-record格式的可执行代码和数据。程序员将读取S-record格式并将数据“刻录”到嵌入式系统中使用的PROM或EPROM中

在这里插入图片描述


  • S19文件的每一行数据由下列五个部分组成:
    在这里插入图片描述

  • type(记录类型):用来描述记录的类型 (S0,S1,S2,S3,S5,S7,S8,S9)。

  • count(字节计数):2个字符,表示记录的其余部分(address + data + checksum)的字节数,该字段的最小值为 3,最大值为 255 。通常记录有 0x20 个数据字节。

  • address(地址):4或6或8个字符。由记录类型 type(记录类型)` 决定。对于S1和S9类型(S19),地址字段为 4 个十六进制数字(2 个字节)。对于S2和S8 record(S28),地址字段为 6 个十六进制数字(3个字节),对于S3和S7 record(S37),地址字段为 8 个十六进制数字(4 个字节)。地址首先以 MSB 发送。地址字节以大端格式排列。

  • data(数据):0—64字符。用来组成和说明一个代表了内存载入数据或者描述信息的16进制的值。

  • checksum(校验和):一个字节。Checksum = 取补码( (uint8_t)(Byte count + Address + Data) )。


🍅 实例说明

  • Notepad++ 打开选择语言,自动识别,根据颜色可以看出S19的记录格式
  • 看到的是16进制显示的ASCII文本格式

在这里插入图片描述
在这里插入图片描述


  • 重点说下S19的Type类型
  • S0是文件的第一行,头信息
  • 如果数据是S1开头,则地址占2个字节(文本字符串用4个字节表示),则文件最后一行的尾信息的开头就是用S9表示
  • 如果数据是S2开头,则地址占3个字节(文本字符串用6个字节表示),则文件最后一行的尾信息的开头就是用S8表示
  • 如果数据是S3开头,则地址占4个字节(文本字符串用8个字节表示),则文件最后一行的尾信息的开头就是用S7表示

再看下面的一个type 是S2的小实例 :地址占6个字节(注意这是ASCII文本表示的,实际地址是0x708000 )
S2 24 708000 18A718A718A718A718A718A718A718A718A718A718A718A718A718A718A718A7 FB

再看下面的一个type 是S1的小实例 ,地址占4个字节(注意这是ASCII文本表示的,实际地址是0x0020)
S1 23 0020 1A6416AE01DA6426AE024A6E01B6AE01EEDE029A6416AE01FA6406AE025A6436 21


🍅 HEX View 神器

  • HEX View 是一款专业的解析S19文件,HEX文件的工具,可以很方便的看出打开文件的Block块,起始地址和地址块的长度等信息
    在这里插入图片描述

📙CAPL解析S19文件源码


  • 核心变量解释下:
  • F_SegmentInfor[10]:用来存放解析S19文件的Block的信息,一般情况下,刷写文件的地址是不连续的,那么就会分成几个Block块,每个Block块我们要记录下该Block起止地址数据大小,因为这是我们UDS 34服务下载的必须数据,这里定义数组大小为10,足够大了,具体有多少个Block块,由变量SegCounter记录
  • FlsData_BufferArr[0x1FFFFFF]: 这个数组是记录是s19文件的所有字节,数组大小可由刷写文件具体大小设置,因为这个数组存放的是所有Block块的数据,那么当我下载的时候我怎么区分,去取数据呢,那就是 上面说的结构体中的 dword data_offset;这个变量来控制的,
  • AllDataBytes:该文件所有的数据字节数。
  • 我为什么没有这样定义 结构体呢?这样分段信息不是更加明确,刷写时取数据也更加方便直观吗?想一想为什么? struct FlsData_Segment
    {
    byte seg_index;
    dword start_address;
    dword data_size;
    byte FlsData_BufferArr[0x1FFFFFF];
    } F_SegmentInfor[10];//暂时定数组为10
/*@!Encoding:936*/variables
{ dword fileHandle;const dword  text_module = 0;const dword  binary_module = 1;enum File_Type { file_header, file_data,file_tail };struct  FlsData_Segment {byte  seg_index;dword start_address;dword data_size;dword data_offset;     } F_SegmentInfor[10];//暂时定数组为10long   SegCounter;       //记录文件被分成多少个段long   AllDataBytes;     // 文件所有的数据字节数byte  FlsData_BufferArr[0x1FFFFFF]; //文件中的所有字节被解析后放在该数组  
}on key 'a'
{Flash_Parse_S19("E:\\demo.s19");}long Flash_Parse_S19(char f_path[])
{long i;char tmpBuffer[5000];char temStr[255];byte AddressSize;          // s19文件的地址占多少个字节,S1是2个;S2是3个;S3是4个dword DataSize_PerLine_0;  // 两个地址之间的插值,是个固定值,比如0x20dword DataSize_PerLine_1;  // 每行数据的第2-3,两个字节,是改行的数据长度(包含地址,数据,和校验)dword DataSize_PerLine_2;  // 每行纯数据段的长度byte  RawData_Line[255];   // 解析后每行数据内容dword FlsData_CurrentAddress;dword FlsData_PreviousAddress;  long   file_line_num;    // 文件有多少行long   SegmentDataSize;  //记录当前段的字节数,临时变量/*初始化变量*/file_line_num = 0;SegCounter = 0;AllDataBytes = 0; SegmentDataSize = 0;  FlsData_CurrentAddress = 0x00000000;fileHandle = OpenFileRead(f_path,text_module);if (fileHandle == 0 ) {write("Failed to open File %s !",f_path);return 0 ;}write("File:%s Opened",f_path);while ( fileGetString(tmpBuffer,elcount(tmpBuffer),fileHandle) != 0){file_line_num++; //数据行计数器if(S19_Record_Type(tmpBuffer[1],AddressSize) == file_header){write("文件头数据:%s",tmpBuffer);}else if(S19_Record_Type(tmpBuffer[1],AddressSize) == file_data){FlsData_PreviousAddress = FlsData_CurrentAddress;substr_cpy_off(temStr, 0, tmpBuffer, 2, 2, elcount(tmpBuffer));        // 截取字符串,取出数据长度snprintf(temStr, elcount(temStr), "0x%s",temStr);       strtoul(temStr,DataSize_PerLine_1);  //将数据长度 字符串转为数值substr_cpy_off(temStr, 0, tmpBuffer, 4, AddressSize, elcount(tmpBuffer)); // 截取字符串,取出当前行的地址snprintf(temStr, elcount(temStr), "0x%s",temStr);       strtoul(temStr,FlsData_CurrentAddress);  //将当前行的地址 字符串转为数值if (file_line_num == 2) //只执行一次,获取S19文件 第一段的刷写地址,和数据长度{DataSize_PerLine_0 = DataSize_PerLine_1; //DataSize_PerLine_0  是标准数据长度,DataSize_PerLine_1最后一行数据可能不等于标准数据长度F_SegmentInfor[SegCounter].start_address = FlsData_CurrentAddress;F_SegmentInfor[SegCounter].data_offset = 0; }//分段处理if(((FlsData_CurrentAddress - FlsData_PreviousAddress) > DataSize_PerLine_0) && (FlsData_PreviousAddress != 0x00000000)) {   write("Previous Address:0x%2X,Current Addresses:0x%2X ",FlsData_PreviousAddress,FlsData_CurrentAddress);F_SegmentInfor[SegCounter].data_size   = SegmentDataSize;  //上一段结束,保存上一段的字节数                                          SegCounter++;            F_SegmentInfor[SegCounter].start_address = FlsData_CurrentAddress; //保存下一段的首地址  F_SegmentInfor[SegCounter].data_offset += SegmentDataSize; //相对数组FlsData_BufferArr的开始索引地址 SegmentDataSize = 0;     // 重置计数变量,为这一段字节计数准备}// DataSize_PerLine_0 - AddressSize -1  : Byte count: 一个字节,表示后面其余部分(地址+数据+校验和)的字节数DataSize_PerLine_2 =  DataSize_PerLine_1*2 - AddressSize - 2 ;substr_cpy_off(temStr, 0, tmpBuffer, 4+AddressSize,DataSize_PerLine_2 , elcount(tmpBuffer)); // 截取字符串,取出当前行的数据    HexStrToByteArr(temStr,RawData_Line);for(i=0;i<DataSize_PerLine_2/2;i++){FlsData_BufferArr[AllDataBytes] = RawData_Line[i];AllDataBytes ++ ; SegmentDataSize ++ ;}  }else if(S19_Record_Type(tmpBuffer[1],AddressSize) == file_tail){write("文件尾数据:%s\n",tmpBuffer);//最后一段的数据长度F_SegmentInfor[SegCounter].data_size = SegmentDataSize;F_SegmentInfor[SegCounter].data_offset = AllDataBytes - SegmentDataSize; //相对数组FlsData_BufferArr的开始索引地址   fileClose(fileHandle);SegCounter++;write("文件总行数:%d",file_line_num);write("文件总字节数:%d",AllDataBytes);write("文件总分段数:%d",SegCounter);for(i = 0; i < SegCounter; i++){write("数据块:%d,  起始地址:0x%X, 结束地址:0x%X, 数据长度:%6d字节\r\n", i+1, F_SegmentInfor[i].start_address, F_SegmentInfor[i].start_address + F_SegmentInfor[i].data_size - 1, F_SegmentInfor[i].data_size);}}else{write("S19为定义格式!");fileClose(fileHandle);return 0 ;}}return 1 ;
}long S19_Record_Type(byte type ,byte &address_size)   
{if(type == '0') //s19文件的头部{return file_header; }else if((type == '7') || (type == '8') || (type == '9') ) //s19文件的结束{return file_tail;  }else if((type == '1') || (type == '2') || (type == '3') ) //s19文件的数据{if(type== '1'){address_size = 4 ;  //地址占 4个字节      }else if(type == '2'){address_size = 6 ; //地址占6个字节    }else if(type == '3'){address_size = 8 ; //地址占 8个字节   }return file_data;  }else{write("Not Defined!"); }return -1;  
}byte HexStrToByteArr(char hexRawData[], byte outByteArr[])
{word i;word offset;word hexLength;word byteIndex;byte tmpVal;byte retVal;  for (i = 0; i < elcount(outByteArr); i++){outByteArr[i] = 0;}// get the hex lengthhexLength = elcount(hexRawData);if( hexRawData[0] == '0' && hexRawData[1] == 'x' )offset = 2;   elseoffset = 0; if ( elcount(outByteArr) <  (hexLength - offset)/2 ){write("Out Arrary too Small.");return 0;}else {  //All checks went fine, convert datafor (i = offset; i < hexLength; i++){// The byte index to use for accessing output arraybyteIndex = (i - offset) / 2 ;         // convert the Hex data and validity check ittmpVal = (byte)hexRawData[i];if (tmpVal >= 0x30 && tmpVal <= 0x39)tmpVal = tmpVal - 0x30;else if(tmpVal >= 'A' && tmpVal <= 'F')tmpVal = tmpVal - 0x37;else if (tmpVal >= 'a' && tmpVal <= 'f')tmpVal = tmpVal - 0x57;else{return 0;}       if (0 == (i % 2)){outByteArr[byteIndex] = tmpVal << 4;}else{outByteArr[byteIndex] = outByteArr[byteIndex] | tmpVal;}}}return 1;
}

  • CAPL脚本跑出来的结果和HexView对比,结果一致,说明我们解析没问题

在这里插入图片描述

在这里插入图片描述

🌎总结

23

  • 🍅 有需要演示中所用demo工程的,可以关注下方公众号网盘自取啦,感谢阅读。

7

  • 🚩要有最朴素的生活,最遥远的梦想,即使明天天寒地冻,路遥马亡!

  • 🚩如果这篇博客对你有帮助,请 “点赞” “评论”“收藏”一键三连 哦!码字不易,大家的支持就是我坚持下去的动力。
    18

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

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

相关文章

UDS报文传输的四种帧

ISO14229-1规定了26个诊断服务细节&#xff0c;也就是UDS诊断报文的细节。它只规定了各个服务每个字节的含义&#xff0c;它不关心底层到底是怎么传输的。 ISO15765-2规定了基于CAN总线进行UDS报文传输的细节&#xff08;包括四种帧&#xff09;。是在CAN总线传输的情况下&…

掉落回弹问题(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;float b 100;float sum 0;int i 0;//运算&#xff1b;for (i 1; i < 10; i){//运算&…

力扣HOT100 - 101. 对称二叉树

解题思路&#xff1a; class Solution {public boolean isSymmetric(TreeNode root) {if(root null) return true;return recur(root.left, root.right);}boolean recur(TreeNode L, TreeNode R) {if (L null && R null) return true;if (L null || R null || L.…

前端开发攻略---实现发送手机验证码60s倒计时效果(手机号验证+按钮文字自定义显示+Vue2写法+Vue3写法)

1、演示 2、说明 1、为了便于演示&#xff0c;本示例将在3秒后就再次发送。您可以根据需要自定义此时间间隔。 2、采用最少的变量以满足需求&#xff0c;以减少内存占用。 3、不仅仅局限于按钮情况&#xff0c;也可应用于不禁用按钮的情况&#xff0c;以实现更多的扩展性。 4、…

XiaodiSec day037 Learn Note 小迪安全学习笔记

XiaodiSec day037 Learn Note 小迪安全学习笔记 记录得比较凌乱&#xff0c;不尽详细 day 37 XSS 跨站 前置 利用跨站做一些其他攻击 配合跨站能达到意想不到的效果 开始 权限维持 表单劫持(后台加密的密码不方便解密&#xff0c;考虑结合权限维持来劫持表单获得明文密码…

zkVM选型要点

1. 引言 当选择ZK工具&#xff0c;来做可验证链下计算来扩容区块链时&#xff0c;需考虑&#xff1a; 1&#xff09;为何应选择zkVM&#xff1f;2&#xff09;zkVM有哪些基本功能&#xff1f;3&#xff09;哪些zkVM可提供这些基本功能&#xff1f; 2. 为何应选择zkVM&#x…

大模型培训老师叶梓:通过微调提升小型语言模型的复杂推理能力

在人工智能的快速发展中&#xff0c;复杂推理能力的提升一直是研究者们追求的目标。最近&#xff0c;一项发表在arXiv上的研究成果【1】&#xff0c;提出了一种创新的方法&#xff0c;即通过微调小型语言模型&#xff08;LMs&#xff09;&#xff0c;并将其与大型语言模型&…

贪吃蛇游戏C语言破解:成为编程高手的必修课!

​ 个人主页&#xff1a;秋风起&#xff0c;再归来~ 文章专栏&#xff1a;C语言实战项目 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c;律己则安&#xff01; 1、游戏效果演示 贪吃蛇游戏效果演示 2、win32 A…

20240423给飞凌的OK3588-C开发板适配OV13855【绿屏】linux

20240423给飞凌的OK3588-C开发板适配OV13855【绿屏】 2024/4/22 20:29 开发板&#xff1a;飞凌的OK3588-C OS操作系统&#xff1a;linux R4/Buildroot 【OV13855接到CAM1上&#xff0c;如果要接到CAM2上请修改相关的DTS即可】 https://item.taobao.com/item.htm?_unju3ku2f4…

Beego框架学习

Beego框架学习 在当今快速发展的互联网时代&#xff0c;Web开发技术日新月异&#xff0c;各种开发框架层出不穷。其中&#xff0c;Go语言因其高效的并发处理能力和简洁的语法&#xff0c;逐渐受到开发者的青睐。而基于Go语言的Web框架中&#xff0c;Beego因其易用性和丰富的功…

slam相关观点,欢迎补充

多模态slam 多模态包括&#xff1a;视觉、激光、文本、声音等等 深度学习与slam融合&#xff1a;特征提取、重定位、神经网络辐射场等。 神经辐射场引入了importance Sampling&#xff08;重要性采样&#xff09;和Positional Encoding&#xff08;位置编码&#xff09;&#…

kerberos:适配华为FI

文章目录 一、hive1、hive thrift连接方式 一、hive 1、hive thrift连接方式 kerberos认证失败信息 缺少配置&#xff1a;{“hadoop.rpc.protection”:“privacy”}&#xff0c;具体可参考&#xff1a;kerbros认证相关问题 华为FI参考资料&#xff1a; https://github.com…

【MySQL 数据宝典】【磁盘结构】- 004 redolog 重做日志

一、背景介绍 持久性要求&#xff1a; 对于已提交的事务&#xff0c;即使系统发生崩溃&#xff0c;其对数据库的更改也不能丢失。问题&#xff1a; 在事务提交前将所有修改的页面刷新到磁盘浪费资源。随机IO导致刷新速度慢。 解决方案&#xff1a; 【数据副本】记录事务执行过…

XiaodiSec day013 Learn Note 小迪安全学习笔记

XiaodiSec day013 Learn Note 小迪安全学习笔记 记录得比较凌乱&#xff0c;不尽详细 day13 文件上传 代码自主写 编辑器引用 ueditor 文件下载 直连下载 传参下载 直连下载 中间件决定下载文件类型 直连一般没有问题 传参下载可能存在安全问题 文件删除 文件删除目录…

linux信号机制分析

概念 信号递达&#xff1a;实际执行信号的处理动作就是信号递达 信号未决&#xff1a;信号从产生到递达之间的状态就是信号未决&#xff08;未决就是没有解决&#xff09; 收到某信号后&#xff0c;把未决信号集中的此信号置为1&#xff08;1表示未解决的信号&#xff09;&a…

若依框架升级SpringBoot3

文章目录 前言一、修改父项目pom.xml二、修改ruoyi-admin/pom.xml文件mysql依赖三、修改ruoyi-framework/pom.xml文件kaptcha依赖四、修改ruoyi-common/pom.xml文件servlet依赖为jakarta五、Java EE转Jakarta EE总结 前言 若依官网给出的修改步骤&#xff0c;自己在实际操作过…

【Camera Sensor Driver笔记】四、点亮指南之EEPROM配置

很久之前写的一版&#xff1a; 【Qcom Camera】微距eeprom调试_cam_vio-supply <&l7p>-CSDN博客 <slaveInfo> EEPROMName cat24c64_imx585 eeprom型_sensor名字 slaveAddress 0xa0 i2c write address regAddrType …

国产PLC有哪些,哪个牌子比较好用?

你知道国产PLC有哪些吗,哪个牌子更好用吗&#xff1f; 今天拿出国产先锋的汇川与台达对比&#xff0c;注&#xff1a;视频后方有各品牌学习资料免费送&#xff0c;需要的移步自取。话说回来&#xff0c;只要基于Codesys开发的都比较好用&#xff0c;只是使用底层芯片不同&…

国产软件不背黑锅:4款功能强大的黑科技软件,且用且珍惜

国内软件常被冠以“流氓软件、需要额外付费、广告繁多”等负面标签&#xff0c;但实际上&#xff0c;其中不乏一些小众却功能强大、用户体验极佳的软件。 布丁扫描——免费专业的扫描APP&#xff08;安卓、ios&#xff09; 布丁扫描&#xff0c;无疑是我今年的最爱&#xff0…

服务器还在长期泄密,保护数据IPSSL证书必不可少

IP SSL&#xff0c;或称为安全套接层协议&#xff08;Secure Sockets Layer&#xff09;&#xff0c;是一种用于在互联网上进行通信加密的技术标准&#xff0c;它通过为数据提供加密服务&#xff0c;确保了数据在传输过程中的安全与完整。其工作方式是在客户端和服务器之间建立…