s19.h
源文件路径:
common/s19.h所属模块:common(通用工具库) 代码行数:44
1. 文件职责
本文件是 common/s19.c 的对外接口头文件,定义 Motorola S-Record(.s19)固件文本的解析状态枚举 S19_FILE_PASRE_STATUS_E、解析结果结构体 S19_FILE_PASRE_PCK,并声明单行解析函数 parsing_upgrade_file()。它是 BMS 电池固件升级链路的解析层:EMS 从云端下载 .s19 固件文本、存入 SDRAM 后,由 midware/bms/alpha/battery_upgrade.c 的状态机逐行调用本接口,把每行拆成「数据地址 + 数据长度 + 数据内容」,再按 BMS 的 CAN 升级协议(send_addr_code/send_data_code)逐 8 字节下发。文件本身不含实现与状态,只提供类型与原型。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
__S19_H__ | 宏 | include guard |
S19_DEBUG | 宏(= 0) | 调试打印总开关。置 1 后 s19.c 内所有 printf 才参与编译;生产版本为 0,printf 语句被条件编译整体移除 |
S19_FILE_PASRE_STATUS_E | 枚举(typedef 匿名枚举) | 解析结果状态集,从 0 起编号,共 9 个成员(名称里 PASRE 是 PARSE 的拼写错误,已固化到接口) |
BUF_ADDR_ERR | 枚举成员(= 0) | 入参为空:file_addr == NULL 或 file_pack->u8_burn_data == NULL |
S_ADDR_ERR | 枚举成员(= 1) | 在文本中找不到 'S',或记录类型不被支持(既不是 S0/S1/S2/S3,也不是结束记录 S7/S8/S9) |
END_ADDR_ERR | 枚举成员(= 2) | 在文本中找不到换行符 '\n'(即这一行没有正常结束) |
LEN_ERR | 枚举成员(= 3) | sscanf 读长度失败,或长度字段与实际字符数不符 |
DATA_ERR | 枚举成员(= 4) | 数据域某个字节的 sscanf 解析失败 |
CHECK_SUM_ERR | 枚举成员(= 5) | 校验和字段读取失败或与计算值不匹配 |
HEAD_OK | 枚举成员(= 6) | S0 头记录(公司/文件名等元信息)校验通过,不含烧写数据 |
PARSE_OK | 枚举成员(= 7) | S1/S2/S3 数据记录校验通过,file_pack 已被填好 |
PARSE_END | 枚举成员(= 8) | 遇到 S7/S8/S9 结束记录,文件传输完毕 |
S19_FILE_PASRE_PCK | 结构体 | 单行解析结果,字段见下(名称同样含 PASRE 拼写错误) |
S19_FILE_PASRE_PCK.u8_s_addr | u8 * | 指向本行 'S' 字符的指针(指向 SDRAM 固件映像内部,不是拷贝) |
S19_FILE_PASRE_PCK.u8_end_addr | u8 * | 指向本行结尾 '\n' 的指针,调用方用它算出下一行的偏移 |
S19_FILE_PASRE_PCK.u8_burn_data | u8 * | 输入字段:调用方预先指向自己的接收缓冲(battery_upgrade.c 传的是 s_bat_upgrade_tab.u8_burn_data),解析成功时被写入本行数据 |
S19_FILE_PASRE_PCK.u8_burn_len | u8 | 输出字段:本行有效数据字节数(S1 为 count-3,S2 为 count-4,S3 为 count-5) |
S19_FILE_PASRE_PCK.u32_burn_addr | u32 | 输出字段:本行的装载地址,按记录类型分别由 2/3/4 个地址字节拼成 |
本文件不定义位域,除 include guard 与 S19_DEBUG 外没有其它宏常量。注意 u8_burn_data 是唯一的「入参字段」,其余是出参。
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
parsing_upgrade_file | 全局(原型声明,写为 extern u8) | 解析一行 S-Record 到 S19_FILE_PASRE_PCK,返回 S19_FILE_PASRE_STATUS_E 中的取值;定义于 s19.c:35 |
共 1 个原型声明(s19.h:41),对应 s19.c 中的唯一全局定义。本头文件无 static 函数、无函数指针、无回调。
4. 核心逻辑解析
本文件是纯声明文件,不含可执行逻辑。接口契约要点(实现细节见 s19.c.md):
4.1 返回值不是「状态」而是「本行类型 + 是否成功」的混合体
S19_FILE_PASRE_STATUS_E 把两类语义塞进同一个枚举:
| 类别 | 成员 | 值的分布 |
|---|---|---|
| 错误码(0~5) | BUF_ADDR_ERR、S_ADDR_ERR、END_ADDR_ERR、LEN_ERR、DATA_ERR、CHECK_SUM_ERR | 0 ~ 5 |
| 成功码(6~8) | HEAD_OK、PARSE_OK、PARSE_END | 6 ~ 8 |
函数返回 u8(而非枚举类型),调用方必须同时判断「是否 ≥ 6」与「具体是哪一种成功」。工程内 midware/bms/alpha/battery_upgrade.c 正是这么用的:l_u8_pasre_status == HEAD_OK(:641)→ 只推进偏移不烧写;== PARSE_OK(:646)→ 真正烧写本行;== PARSE_END(:711)→ 进入升级结束流程;其余(:716 的 else)→ u8_file_parse_cnt++,连续 3 次失败就 UPGRADE_INFO_INIT 重来。
4.2 指针字段与缓冲的所有权约定
u8_s_addr/u8_end_addr是指向源映像的借用指针,不是副本。调用方用(u8_end_addr - g_u8_fw_sdram1) + 1计算下一行的文件偏移(battery_upgrade.c:644、:699),因此这两个字段失效的唯一条件是g_u8_fw_sdram1被重新加载——升级任务在升级期间必须保证该 SDRAM 区域不被覆盖。u8_burn_data必须在调用前由调用方设置(battery_upgrade.c:633),否则函数第一句检查就会返回BUF_ADDR_ERR。这是「调用方必须给的入参」,但结构体定义里没有任何注释说明,只有读实现才知道。u8_burn_data的容量不由本接口约束:battery_upgrade.c:46分配的是u8 u8_burn_data[256],而一行 S1 记录最多可携带 252 字节数据并额外做一次 4 字节memset(见s19.c.md备注 3),接口层对此没有任何长度校验或约定。
4.3 记录类型与地址宽度的对应
| 记录类型 | 地址字节数 | 有效数据长度 | 返回码 |
|---|---|---|---|
S0 | 无(元信息) | 无 | HEAD_OK |
S1 | 2 | count - 3 | PARSE_OK |
S2 | 3 | count - 4 | PARSE_OK |
S3 | 4 | count - 5 | PARSE_OK |
S7/S8/S9 | — | — | PARSE_END |
S4/S5/S6 或其它 | — | — | S_ADDR_ERR |
(count 即记录里紧跟在 S+类型字符之后的那个长度字节,覆盖「地址 + 数据 + 校验和」三部分。)
4.4 通信协议 / 定时 / uCOS-II 配合
- 协议:本文件描述的是 Motorola S-Record 文件格式,不是空口协议;真正的 BMS 升级帧由调用方(
midware/bms/alpha/battery_upgrade.c的send_addr_code/send_data_code/send_over_updata_code)按 8 字节一包重新封装。 - 超时与重试:本文件不含超时。调用方持有超时(
OSTimeGet()差值比较,如battery_upgrade.c:588的 60 秒)与「连续失败 3 次就重启升级」的重试策略;本函数只提供失败码供其计数。 - uCOS-II:不引用任何 OS API,不声明信号量/消息邮箱。函数内部使用
static u8 s_u8_buf[257]作为行缓冲(s19.c:43),因此不可重入——在多任务环境下若被两个任务同时调用会互相踩踏;当前工程内只有 BMS 升级任务一个调用者,故未暴露问题。
5. 对外接口
导出(供外部使用):
| 名称 | 使用方 |
|---|---|
parsing_upgrade_file() | midware/bms/alpha/battery_upgrade.c:635(在 SEND_UPGRADE_DATA 分支中每包调用一次) |
S19_FILE_PASRE_PCK | midware/bms/alpha/battery_upgrade.c:555(S19_FILE_PASRE_PCK l_pasre_pck = {0};) |
S19_FILE_PASRE_STATUS_E 成员(HEAD_OK/PARSE_OK/PARSE_END) | midware/bms/alpha/battery_upgrade.c:641、:646、:711 |
被调用(本文件引用的外部符号):
| 名称 | 来源 |
|---|---|
chip.h | system/sys/chip.h(s19.h:13),提供 u8 / u16 / u32 |
stdio.h | C 标准库(s19.h:14),供 s19.c 里被 #if S19_DEBUG 包住的 printf 使用 |
string.h | C 标准库(s19.h:15),供 strstr / memset / memcpy 使用 |
本文件不导出任何全局变量。
6. 依赖关系
依赖:
system/sys/chip.h(s19.h:13)——类型定义来源;<stdio.h>、<string.h>(s19.h:14~:15)——由头文件代替实现文件包含标准库,这一点比较少见(其它common/头文件多让.c自己 include);副作用是任何 includes19.h的文件都会连带拿到printf/memcpy等声明。
被依赖(include 本头文件的文件):
common/s19.c:16midware/bms/alpha/battery_upgrade.c(唯一真实使用方)
7. 备注与风险点
- 枚举成员名与
common/hex_file_parse.h冲突(高危):两个头文件的枚举都定义了PARSE_OK、PARSE_END、DATA_ERR,且取值完全不同:
| 名称 | s19.h | hex_file_parse.h |
|---|---|---|
PARSE_OK | 7 | 0 |
PARSE_END | 8 | 2 |
DATA_ERR | 4 | 3 |
任何同时 include 两者的编译单元都会因枚举常量重定义而编译失败(或在不严格的编译器下产生静默的数值比较错误)。当前工程恰好没有这样的文件(s19.h 只被 battery_upgrade.c 使用),但两套「固件行解析器」的接口命名撞车是明确的隐患。
PASRE拼写错误已污染整个接口:S19_FILE_PASRE_STATUS_E与S19_FILE_PASRE_PCK两个类型名把PARSE拼成了PASRE,调用方battery_upgrade.c:555的变量名l_pasre_pck又跟着错了一遍。修正名称需要跨文件同步修改。extern u8返回值与枚举不匹配:parsing_upgrade_file()逻辑上返回S19_FILE_PASRE_STATUS_E,但声明与定义都用u8。调用方若把返回值赋给有符号变量或直接参与比较,编译器无法帮忙检查;同时extern写在头文件里是冗余的(函数声明默认就是外部链接)。- 结构体三个「地址指针」字段类型与命名不一致:字段名用
u8 *前缀却指向字符缓冲(u8_s_addr/u8_end_addr实际是char *转换而来,见s19.c:37~:38与s19.c:145~:146),命名暗示「8 位地址」而不是「8 位宽的类型别名 + 指针」,容易误读;u32_burn_addr才是真正的地址值。 u8_burn_len是u8:一行最多 252 字节数据,恰好放得下;但这也意味着无法表达超过 255 字节的行,接口没有注释说明这个上限,一旦将来改用长行 S19(例如 250+ 字节载荷)会静默截断。S19_DEBUG是二进制开关:置 1 时会启用十余处printf(s19.c:47~:409之间的#if S19_DEBUG块),其中包含打印整行数据与每个字节的循环(s19.c:75~:80),在实时任务里会造成明显阻塞,只适合离线排查。stdio.h/string.h由头文件包含:与工程内其它common/头文件(crc.h只 includechip.h、convert.h只 includeincludes.h)风格不一致,且会扩大包含者的编译依赖面。- 头文件说明不足:
S19_FILE_PASRE_PCK里哪个字段是入参(u8_burn_data)、哪些是出参、指针指向谁的内存、缓冲容量要求,都没有注释;结构体成员的注释(“开始地址/结束地址/数据/数据长度/数据地址”,s19.h:34~:38)只复述了字段名,没有提供上述关键约定。