
一文带你搞懂S19文件(S-Record)
一文带你搞懂S19文件(S-record)
我之前做过UDS诊断上位机的开发,做过 ECU 的 Bootloader 刷写相关的工作。想来把知识积累一下,以便后人翻阅学习。
可以关注我,之后还会分享HEX文件的解析,以及相关的示例代码。
一、概述
S19文件,也常被称为S-Record、SREC或Motorola S-record,是一种由摩托罗拉推出的ASCII文本格式,主要用于表示二进制数据。它最早被应用于嵌入式系统中的固件传输与存储,在微控制器编程、EEPROM烧录以及调试场景中尤为常见。这种格式以可读的文本方式编码二进制信息,并内嵌地址、数据和校验码,从而保证数据传输的可靠性。
Motorola S-record诞生于20世纪70年代中期,最初是为Motorola 6800处理器而设计。配套的开发工具会将可执行代码和数据输出为该格式,随后程序员将其读取并"烧录"到嵌入式系统中的PROM或EPROM里。
此外,还有一种常见的十六进制格式叫Intel HEX,简称Hex,是由英特尔定义的。
二、S19文件是什么
S19文件示例
在正式讲解之前,我们先来看一段真实的S19文件示例。
如下边的文件所示,这是一个非常常见的S19文件片段, 我们在UDS中通过34、 36、 37 服务刷写的就是像这样的数据。
S309000080040000812CC5
S31900008008000000000000000000000008000080080000800846
S3190000801C000001240000812C0000812C0000023C00009000FD
S309000081200000000055
S31900008138D0012C00D101780000757060E0021C63762C00930B
S3190003B51CFFFF0000454452495645000000000000533135306C
S3190003B53030312E3030360000564355303200000000000089
我们使用VSCode打开S19文件,并添加 S19 HighLight 插件,即可像如下图所示,有代码块高亮,可以快速查看每一个部分在哪里。建议每个汽车工程师都下载该插件。

S19文件的生成方式
S19文件通常由以下工具生成:
- 编译器/链接器:如
GCC、IAR、Keil等,在编译代码后生成。 - 转换工具:通过
objcopy(GNU工具链)将ELF或BIN文件转换为S19格式。
S19文件的具体生成过程
假设你有这样的C代码:
void AppMain(void);编译后:
AppMain这个函数被放在 Flash 的 0x00008000地址上- S19 文件里有一行: S3 09 00008000 ...... ......
那么以下4种东西,本质上是同一个地址:
| 视角 | 表示 |
|---|---|
| S19 文件 | 00008000 |
| C 语言 | void AppMain(void) |
| CPU | PC = 0x00008000 |
| Flash | 第 0x8000字节开始 |
👉 CPU 并不关心"这是函数还是数据"
它只看到:
- 地址
- 那里的二进制内容
- 如果内容是合法指令,就执行
- 如果内容是数据,就当作数据读
严格上来讲,如果你拿到别人的完整S19文件,你可以将其反编译为汇编代码。(继续反编译为C代码会很困难)
👇 再举一个例子
上述的文件示例中,S19文件实际上是保存的《S15001.006.s19》这个文件,文件名S15001.006就是该编译后的软件的软件版本号。
我们使用UDS的34、36、37服务刷写之后,再使用22服务去读取0xF195这个DID,就可以读取到刚才刷进去这个软件版本号。
我们逆向思考,既然我们刷写之后,可以通过22服务读取到更新后的0xF195的DID值;那么说这个刷写进去的S19文件中一定是完整保存了刚才这个字符串的。
我们将这个字符串格式的S15001.006软件版本号,按照ASCII码表转换为字节数组:5331353030312E303036, 再拿着这个到S19文件中去找,你可以找到刚才这段字节数组。
S3190003B51CFFFF0000454452495645000000000000533135306C
S3190003B53030312E3030360000564355303200000000000089
我们的程序就是以这样的方式躺在S19文件里。
S19 / HEX 里的地址,就是 CPU 指令指针、C 语言指针在 Flash 上的投影;Flash 里直接躺着二进制化的函数和变量;CPU 通过地址总线,把这些二进制"当成指令"或"当成数据"来使用。
S19文件的应用场景
- 固件烧录:通过编程器将S19文件写入MCU的Flash或EEPROM。我们的
Bootloader正是通过CAN总线 +UDS的方式将S19格式的程序烧录进车身的控制器中。 - 调试:调试器通过解析S19文件定位代码和数据地址。
- 数据校验:校验和机制确保数据传输过程中无错误。
三、S19文件格式结构
如第二节所示,一个S19文件由一些相似的行组成,我们称之为记录,每条记录代表一段二进制数据或控制信息。
每一行都分为6个部分,我用不同颜色区分开来了。每条记录的结构如下(以S3190003B51CFFFF0000454452495645000000000000533135306C为例):
- 起始符(Start Code),固定字符 S
- 记录类型(Type),如 S3 中的 3
- 字节数(Byte Count),如 19
- 地址(Address),如 00008120
- 数据(Data),如 FFFF000045445249564500000000000053313530
- 校验和(Checksum),如 6C
下边我会详细展开讲每一个字段。
四、S19行字段说明
起始符(Start Code)
注
固定字符 S ,标识一条记录的开始。
记录类型(Type)
注
1位数字,表示记录类型(0-9),常用类型包括:
- S0:文件头(通常包含文件名或描述信息)。
- S1:16位地址的数据记录(地址范围:0x0000–0xFFFF)。
- S2:24位地址的数据记录(地址范围:0x000000–0xFFFFFF)。
- S3:32位地址的数据记录(地址范围:0x00000000–0xFFFFFFFF)。
- S5:记录计数(可选,表示S1/S2/S3记录的数量)。
- S7/S8/S9:终止记录(表示程序入口地址或文件结束)。
S0记录(文件头)
注
用途:文件标识或描述信息。
示例: S00600004844521B
S0:类型为S0。06:总字节数为6(地址4字节 + 数据0字节 + 校验和1字节)。0000:地址字段(通常为0)。484452:ASCII数据("HDR")。1B:校验和。
S1/S2/S3记录(数据记录)
注
用途:存储实际的二进制数据及其地址。
示例: S3190003B51CFFFF0000454452495645000000000000533135306C
- S3:类型为S3(32位地址)。
- 19:总字节数为 0x19 = 25(地址 4 字节 + 数据 20 字节 + 校验和 1 字节)。
- 0003B51C:地址为
0x0003B51C。 - FFFF000045445249564500000000000053313530:20字节数据。
- 6C:校验和。
S5记录(记录计数)
注
用途:统计S1/S2/S3记录的数量(可选)。
示例:S5030003FA
S5:类型为S5。03:总字节数为3(地址 0 字节 + 数据 2 字节 + 校验和 1 字节)。0003:表示有3条数据记录。FA:校验和。
S7/S8/S9记录(终止记录)
注
用途:标识文件结束,并可能包含程序入口地址。
示例: S7050000812455
S7:类型为S7(终止记录)。05:总字节数为5(地址 0 字节 + 数据5字节 + 校验和1字节)。00008124:程序入口地址(通常由系统忽略)。55:校验和。
字节数(Byte Count)
注
2位十六进制数,表示后续字段(地址 + 数据 + 校验和)的总字节数。
以 S3190003B51CFFFF0000454452495645000000000000533135306C 为例:
对应我们的程序文件, S319 就表示后边跟的是4字节(一个int)的地址,并且有 0x19 = 25 个字节的数据。
0003B51C 就是这个4字节的地址,
后边跟了20个数据:FFFF000045445249564500000000000053313530,
然后最后是一个校验位:6C
总共就是 4 + 20 + 1 = 25,计算正确。
地址(Address)
注
根据记录类型确定长度:
- S1:2字节(16位地址)
- S2:3字节(24位地址)
- S3:4字节(32位地址)
以 S3190003B51CFFFF0000454452495645000000000000533135306C 为例:
S3 就表示这是一个32位的地址(也就是一个int),而 0003B51C 就是这个地址。
数据(Data)
注
可变长度的二进制数据,以十六进制ASCII编码表示。
以下边两段记录为例
S309000080040000812CC5
S31900008008000000000000000000000008000080080000800846
S3190000801C000001240000812C0000812C0000023C00009000FD
例如 0x00008008 - 0x00008004 等于 0x4,下一行的地址减去上一行的地址,得到4。就是说,上一行一共传输了 4 个字节的数据,数一下确实是 4 字节数据: 0000812CC5,其中 C5 是校验位。通常一次最多传输 20 个字节的数据。
0x0000801C - 0x00008008 = 28 - 8 = 20 ,表示上一行有 20 个数据。一行27个字节,前2个表示数据头,后4个表示数据地址,最后一个是校验位,所以 数据段的长度 = 27 - 2 - 4 - 1 = 20个。数一下确实是20字节数据。
校验和(Checksum)
1字节,用于验证记录的完整性。计算方法为:
注
校验和 = 0xFF - (字节数 + 地址高位到低位 + 数据所有字节) 的低8位。
即计数值、地址场和数据场的若干字符以两个字符为一对, 将它们相加求和,和的溢出部分不计(掩码),只保留最低两位字符 NN (补码),checksum = 0xFF - 0xNN。有关详细的校验和示例,请参见示例部分。
校验和计算示例: S10FCAC00C0C0C0C0C0C0C007F40000053
0F 表示后边的 地址 + 数据 + 校验 一共15个字节。 CAC0 表示地址。
被解码以显示校验和值是如何计算的,如下所示:
- 累加: 0F + CA + C0 + 0C + 0C + 0C + 0C + 0C + 0C + 0C + 00 + 7F + 40 + 00 + 00 =
2AC(hex) - 掩码:保留最低有效字节 =
AC(hex) =1010 1100(bin) - 补码:计算最低有效字节的补码; checksum = 0xFF - 0xNN ,即 0xFF - 0xAC = 0x53 (和预期值相比,正确)
总结
本文介绍了 S19(S-Record)文件的基本结构:记录类型、字节数、地址、数据和校验和五大字段,以及 S0/S1/S2/S3/S5/S7/S8/S9 各类型的具体含义。掌握了这些,看懂 S19 文件、编写解析器或刷写工具就不再是问题。
后续我会继续更新 HEX 文件的解析,以及相关的 Python / C# 示例代码,方便大家直接拿来用。如果本文对你有帮助,欢迎关注,一起学习交流。
