工作经验总结:S19(SRecord)文件解析

一、SRecord文件简介

        SRecord文件是由Motorola公司定义的一种ASCII文本文件,文件扩展名包括:.s19、.s28、.s37、.s、.s1、.s2、.s3、.sx、.srec、.exo、.mot、.mxt,都是同一种格式,文件内容没有差异,主要用于记录微控制器、EPROM和其他类型的可编程设备的程序记录。

 

二、SRecord解析格式

SRecord文件一般格式如下:

typecountaddressdatachecksum
2 Byte2 Byte4 or 6 or 8 Byte0~64 Byte2 Byte
  • type:表示该行的数据记录类型
类型字段(type)记录内容地址字段数据字段记录描述
S0标题信息0x0000

1. mname is char[20] and is the module name.

2. ver is char[2] and is the version number.

3. rev is char[2] and is the revision number.

4. description is char[0-36] and is a text comment.

S1数据16位地址该记录包含从 16 位地址字段开始的数据。该记录通常用于 8 位微控制器
S2数据24位地址该记录包含从 24 位地址开始的数据
S3数据32位地址该记录包含从 32 位地址开始的数据。该记录通常用于 32 位微控制器
S4ResverdResverd×
S5计数16 位计数×此可选记录包含S1 / S2 / S3记录的 16 位计数。如果记录计数小于或等于 65,535 (0xFFFF),则使用此记录,否则将使用S6记录
S6计数24 位计数×此可选记录包含S1 / S2 / S3记录的 24 位计数。如果记录计数小于或等于 16,777,215 (0xFFFFFF),则使用此记录。如果小于 65,536 (0x010000),则将使用S5记录
S7记录起始地址,并且终止S3记录32位地址×该记录包含 32 位地址的起始执行位置。这用于终止一系列 S3 记录
S8记录起始地址,并且终止S2记录24位地址×该记录包含 24 位地址的起始执行位置。这用于终止一系列 S2 记录
S9记录起始地址,并且终止S1记录16位地址×该记录包含 16 位地址的起始执行位置。这用于终止一系列 S1 记录
  • count:(address + data + checksum)区域的字节总数
  • address:对应记录数据的起始地址信息
  • data:数据内容或者描述信息
  • checksum:(cout + address + data)区域的字节累加和S,保留最低有效字节,计算有效字节的补码checksum = ~(S & 0xFF) & 0xFF

(1)SRecord  S0/S3/S7解析示例

S01200004B4C5F333030395F4150502E733139E0
S30B100693F00000000000005B
S30910080000FECACEFA4E
S7051006818DD6
  • S0 12 0000 4B4C5F333030395F4150502E733139 E0

S0 表示记录标题信息

12 表示 addr + data + checksum 区域的字节数为12个字节

0000 固定为0000

4B4C5F333030395F4150502E733139 记录相关信息

E0 表示count + addr + data 区域的校验和为E0

  • S3 09 10080000 FECACEFA 4E 表示0x10080000地址起始的数据为FECACEFA
0x100800000xFE
0x100800010xCA
0x100800020xCE
0x100800030xFA

 

  • S7 05 1006818D D6 表示main函数的入口地址为0x1006818D,并且结束S3的记录(类似文件末尾)

(2)SRecord  S0/S1/S5/S9解析

S00600004844521B
S1130000285F245F2212226A000424290008237C2A
S11300100002000800082629001853812341001813
S113002041E900084E42234300182342000824A952
S107003000144ED492
S5030004F8
S9030000FC
  • S1 13 0000 285F245F2212226A000424290008237C 2A 表示0x0000起始地址的数据为285F245F2212226A000424290008237C

  • S5 03 0004 F8

S5 表示记录S1/S2/S3的计数

03 表示 addr + data + checksum 区域的字节数为3个字节

0004 表示前面S1记录的数量为4个

F8 表示count + addr + data 区域的校验和为F8

累加和 S = (03 + 00 + 00 + 04)= 0x07

校验和 checksum = ~(S & 0xFF)& 0xFF = 0xF8

  • S9 03 0000 FC 表示main函数的入口地址为0x0000,并且结束S1的记录(类似文件末尾)

​​​​​​​

三、SRecord解析Python代码示例

s19(SRecord)​​​​​​​文件的内容行解析python代码示例:

 def _line_analyse(self, line:str):
        # string fliter
        line = line.strip()  # 去除开头与结尾的空格或换行符
        
        # S19 file format
        type        = line[0:2]
        count       = int(line[2:4], 16)    # addr + data + checksum
        checksum    = int(line[-2:], 16)
        
        # S19 file info string dispose
        # 标题类型
        if (type == "S0"):
            pass
        
        # 数据类型
        # ( S1: 地址段 2个字节; S2: 地址段 3个字节; S3: 地址段 4个字节)
        elif (type == "S1" or\
              type == "S2" or\
              type == "S3"):
            
            # 地址索引和数据索引
            ADDRESS_LINE_INDEX = 4
            ADDRESS_LEN = DATA_TYPE_ADDR_LEN_ENUM.get(type)
            DATA_LINE_INDEX = ADDRESS_LINE_INDEX + ADDRESS_LEN
            DATA_LEN = (count * 2) - ADDRESS_LEN - 2    # data = cout - addr - checksum
            
            # 地址数据
            address = int(line[ADDRESS_LINE_INDEX : ADDRESS_LINE_INDEX + ADDRESS_LEN], 16)
            
            # 地址对应的data数据
            data_hex_list = []
            for i in range(DATA_LINE_INDEX, DATA_LINE_INDEX + DATA_LEN, 2):
                data_hex = int(line[i : i+2], 16)
                data_hex_list.append(data_hex)
            
            # checksum 校验
            # 该行所有16进制累加和S,保留最低有效字节,计算有效字节补码
            checksum_temp = 0
            for i in range(2, len(line) - 2, 2):
                checksum_temp += int(line[i:i+2], 16)
            checksum_temp = ~(checksum_temp & 0xFF) & 0xFF
            
            # 检验checksum
            if checksum_temp == checksum:
                self.data_dict[address] = data_hex_list
            else:
                print("checksum_temp:0x{:2x}, checksum:0x{:2x}, line:{:s}".format(checksum_temp, checksum, line))
        
        # Resverd
        elif (type == "S4"):
            pass
        
        # 数据计数类型(包含S1/S2/S3的计数)
        elif (type == "S5" or\
              type == "S6"):
            pass
        
        # 起始地址类型
        # ( S9: 终止S1记录; S8: 终止S2记录; S7: 终止S3记录)
        elif (type == "S7" or\
              type == "S8" or\
              type == "S9"):
            self.is_finished_analyse = True
            
            # 起始地址索引与长度
            MAIN_ADDRESS_INDEX = 4
            MAIN_ADDRESS_ADDRESS_LEN = (count * 2) - 2   # 减去checksum长度
            
            self.main_address = int(line[MAIN_ADDRESS_INDEX: MAIN_ADDRESS_INDEX + MAIN_ADDRESS_ADDRESS_LEN], 16) 

### S19 文件格式 S19文件是一种文本格式的文件,通常用于嵌入式系统的开发过程中。这种文件包含了二进制代码及其对应的内存地址信息,便于在目标设备上加载和执行程序[^1]。S19文件的主要特点是以ASCII字符表示数据,并按照特定的记录格式组织。 #### 记录结构 S19文件由多条记录组成,每条记录都遵循固定的格式: - **起始符**:每条记录以`S`开头。 - **记录长度**:紧跟其后的两位十六进制数表示该记录中剩余字节的数量(不包括校验和)。 - **地址字段**:指定数据存储的目标地址。 - **数据字段**:包含实际的数据内容。 - **校验和**:用于检测传输错误。 常见的S19记录类型有三种: - `S0`:标识记录,常用来标注文件名或其他元信息。 - `S1`:定义了16位地址空间中的数据位置。 - `S2`:支持更宽广的24位地址范围。 - `S3`:适用于完整的32位地址环境。 例如,一条典型的S1记录可能看起来像这样: ``` S11F00807EFEFFD2C5AFAFBFC6BDC7CDC8CDFCECFDDEEFFAFBECDCCFFFFFFFFFF ``` 这条记录表明,在地址`0x0080`处应该写入一系列字节序列,最后附带了一个校验和以确保数据完整性。 ### 用途 S19文件广泛应用于嵌入式系统领域,特别是在固件更新场景下扮演重要角色。以下是几个典型的应用案例: - **远程编程**:通过网络接口将新版本软件下载至远端设备并完成刷新操作。 - **调试工具链集成**:某些IDE或烧录工具能够直接处理S19文件形式输入,简化了从编译链接到最后部署之间的过渡步骤[^3]。 - **自动化测试框架构建**:结合虚拟仿真平台如CANoe,可以创建复杂的测试用例来验证ECU的行为特性以及诊断协议兼容性等问题[^2]。 另外值得注意的是,在一些高级应用场合里,比如汽车电子控制系统当中,还会配合专门设计好的引导加载程序(Bootloader),从而实现更加灵活高效的空中升级服务(OTA)。 ### 示例代码展示 下面给出一段简单的Python脚本片段,演示如何初步解析一个标准的S19文件: ```python def parse_srec(file_path): with open(file_path, 'r') as f: lines = f.readlines() records = [] for line in lines: if not line.startswith('S'): continue type_ = line[1:2] length = int(line[2:4], base=16) addr_len = { '1': 4, '2': 6, '3': 8 }[type_] // 2 address = int(line[4:(addr_len * 2 + 4)], base=16) data = bytes.fromhex(line[(addr_len * 2 + 4):-2]) record = { "Type": type_, "Address": address, "Data": data } records.append(record) return records if __name__ == "__main__": srecords = parse_srec('./example.s19') for rec in srecords[:5]: print(f"Record Type:{rec['Type']}, Address:{hex(rec['Address'])}, Data:[{', '.join([f'0x{x:02X}' for x in rec['Data']])}]") ``` 此函数接受路径参数指向待分析的目标S19文档,逐行读取后提取关键属性存放到列表容器之中返回给调用方进一步加工使用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃鱼的猫丿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值