EMS 知识库
首页 / knowledge/代码解析/midware/inverter/ginlong

ginlong_upgrade.c

源文件路径:midware/inverter/ginlong/ginlong_upgrade.c 所属模块:midware / inverter / ginlong(锦浪逆变器固件升级) 代码行数:884

1. 文件职责

本文件实现锦浪(Ginlong/Solis)储能逆变器的在线固件升级流程,覆盖三个升级对象:ARM 控制单元(UPDT_INV3)、DSP 从机(UPDT_INV2)、DSP 主机(UPDT_INV1)。升级数据由云端经 net/upd_download.c 下载到 SDRAM 的两个固件区(g_u8_fw_sdram1 / g_u8_fw_sdram2)并解析为二进制镜像,本文件负责把镜像按私有协议分帧,经 USART2 写入逆变器并逐包确认。它包含两套独立的状态机(ginlong_upgrade_arm_handle() 与 ginlong_upgrade_dsp_handle()),以及组帧/校验、应答接收解析和进度查询接口。

驱动方是 midware/inverter/ginlong/ginlong_comm_usart.c:当检测到固件下载完成或服务器置位升级请求时,常规 Modbus 通信被挂起,改为在每次任务轮询里推进一次升级状态机(ginlong_comm_usart.c:4875、4885)。进度百分比由 inverter.c:3816-3999 的厂商分发函数读取并上报给 HMI/云端。

2. 关键数据结构与宏

名称类型说明
GINLONG_UPGRADE_DEBUG宏调试打印总开关,默认 0(ginlong_upgrade.h:15)
DAT_PCK_UPGRADE_INV宏300,连续升级包之间的最小间隔毫秒数(流控)(ginlong_upgrade.h:16)
INV_RECV_UPGRADE_BUFFER_SIZE宏200,应答接收缓冲区字节数(ginlong_upgrade.h:17)
SEND_UPGRADE_DATA_SIZE宏1024,每包固件负载字节数(ginlong_upgrade.h:18)
GINLONG_UPGRADE_OBJECT_E枚举升级对象:GINLONG_NONE / GINLONG_MASTER / GINLONG_SLAVE / GINLONG_ARM(ginlong_upgrade.h:20-26)
GINLONG_UPGRADE_ARM_STATE_E枚举ARM 升级状态机 6 态(ginlong_upgrade.h:46-55)
GINLONG_UPGRADE_DSP_STATE_E枚举DSP 升级状态机 8 态(ginlong_upgrade.h:57-68)
GINLONG_UPGRADE_TABLE_T结构体升级过程表(ginlong_upgrade.h:71-81),静态实例 s_t_upgrade_tab
s_t_upgrade_tabstatic 全局变量唯一的升级过程表实例,本文件第 24 行定义,生命周期贯穿整个升级
g_u8_receive_upgrade_buf[200]全局数组升级应答接收缓冲区,本文件第 25 行定义(非 static,但头文件未 extern)
g_u8_jump_boot_command[25]static 数组ARM 跳转 BOOT 指令(命令字 0xC3),第 28-29 行
g_u8_query_erase_instruction[16]static 数组ARM 查询 Flash 擦除状态指令(0xC4),第 31-32 行
g_u8_off_grid_command[55]static 数组离网指令(命令字 0x03,长度 0xDE),第 35-38 行
g_u8_dsp_jump_boot_command[15]static 数组DSP 跳转 BOOT 指令(0xC5),第 40-41 行
g_u8_query_dsp_erase_flash_instruction[15]static 数组查询 DSP 擦写状态指令(0xC7),第 43-44 行
s_u8_query_dsp_erase_flash[27]static 数组27 字节变体查询指令(前 15 字节与上者相同,紧跟 0xA7 后再拼接一帧),第 46-48 行
g_u8_exit_dsp_upgrade[15]static 数组退出 DSP 升级指令(0xC5,数据域全 0),第 51-52 行
g_u8_fw_sdram1 / g_u8_fw_sdram2全局数组SDRAM 固件缓存区,来自 net/server.h:258-259
ENUpdateType / UPDT_INV1 / UPDT_INV2 / UPDT_INV3枚举/枚举成员升级任务类型:1=DSP 主机、2=DSP 从机、3=ARM(net/server.h:190-192)

3. 函数清单

函数名作用域说明
ginlong_send_upgrade_data全局带 300 ms 流控的 USART2 发送封装:不足间隔先延时补齐,然后清空接收缓冲并发送
inv_recv_upgrade_data全局升级应答接收与解析:轮询等待 → 按协议剥头/校验长度 → 逐字节读入缓冲区 → 校验和比对,返回有效长度
fill_arm_upgrade_data_accl_code全局填充 ARM 升级帧(0xC4)的负载与累加校验码到 u8_upgrade_buf
fill_dsp_upgrade_data_accl_code全局填充 DSP 升级帧(0xC6)的帧头、长度、行号、负载与累加校验码
ginlong_read_upgrade_flag_master全局返回当前升级对象是否为逆变器主机(DSP 主机)
ginlong_read_upgrade_percent_master全局返回主机升级百分比(offset*100/file_len),非主机时返回 0
ginlong_read_upgrade_flag_slave全局返回当前升级对象是否为逆变器从机(DSP 从机)
ginlong_read_upgrade_percent_slave全局返回从机升级百分比,非从机时返回 0
ginlong_read_upgrade_flag_arm全局返回当前升级对象是否为 ARM 单元
ginlong_read_upgrade_percent_arm全局返回 ARM 升级百分比,非 ARM 时返回 0
ginlong_upgrade_arm_handle全局ARM 升级状态机主函数(6 态),单次调用推进一步
ginlong_upgrade_dsp_handle全局DSP 升级状态机主函数(8 态),单次调用推进一步

共 12 个函数,其中只有前 4 个被声明在头文件里(ginlong_upgrade.h:88-91 声明了 ginlong_upgrade_arm_handle、ginlong_upgrade_dsp_handle、ginlong_upgrade_handle 与 ginlong_send_upgrade_data);inv_recv_upgrade_data、fill_*_accl_code 是全局函数但无任何声明,属于隐式外部链接。

4. 核心逻辑解析

4.1 私有升级协议帧格式

协议与 Modbus 无关,是锦浪专用的"7E 开头 + 命令字 + 长度 + 负载 + 累加和"格式。

帧结构通式:

7E | 01 | CMD | [LEN_L [LEN_H]] | DATA[...] | CHECK
 |    |     |        长度域           数据域      1 字节累加校验
固定头  固定值为 0x01

已用静态数组验证的自洽性(示例):

4.2 ARM 写固件帧(0xC4)与 DSP 写固件帧(0xC6)的组帧

ARM 帧(ginlong_upgrade.c:502-533 + fill_arm_upgrade_data_accl_code):

偏移  0    1    2    3      4      5      6      7      8     9 ...        9+len
值   7E   01   C4   LEN_L  LEN_H  LINE_L LINE_H FLAG  00    payload...    CHECK

DSP 帧(ginlong_upgrade.c:770-782 + fill_dsp_upgrade_data_accl_code):

偏移  0    1    2    3      4      5      6      7 ...        7+len
值   7E   01   C6   LEN_L  LEN_H  LINE_L LINE_H payload...    CHECK

两帧的分包地址计算方式一致:addr = u8_upgrade_addr + (u16_send_line_num - 1) * SEND_UPGRADE_DATA_SIZE(ginlong_upgrade.c:205、256)。

4.3 应答接收解析(inv_recv_upgrade_data,行 83-191)

这是全文件最关键的底层函数,执行五步:

第 1 步 — 轮询等待(:98-107)

do {
    OSTimeDlyHMSM(0, 0, 0, 50);          // 每次睡 50 ms
    l_u16_len = usart2_get_data_len();
    l_u32_timeout += 50;
    if (16 == l_u16_len || 15 == l_u16_len) break;
} while (l_u32_timeout < timeout);

只有收到 15 或 16 字节才提前退出;其他长度一律等到 timeout 耗尽。这与协议中 15 字节(0xC5/0xC6/0xC7 应答)和 16 字节(0xC4 应答)两种固定应答长度对应。

第 2 步 — 长度钳位与头部解析(:112-153)

第 3 步 — 按长度读负载(:156-161):读 l_u16_recv_len 个字节。

第 4 步 — 读校验字节并比对(:164-180):累加 buf[1] 到负载末字节(不含 0x7E),取低 8 位与末字节比较;相等则 *len = l_u16_len,否则 *len = 0。

第 5 步 — 无数据分支(:182-190):l_u16_len == 0 时走 else if(buf[l_u16_len-1] == 0x14 || 0xD0 == ...),命中则 *len = 0,否则 *len = 0——该分支无论走哪个结果都一样(见第 7 节风险点)。

4.4 ARM 升级状态机(ginlong_upgrade_arm_handle,行 367-589)

状态变量:static GINLONG_UPGRADE_ARM_STATE_E s_u8_upgrade_sate = UPGRADE_ARM_IDLE,三个静态错误计数器 s_u8_upgrade_send_err_cnt、s_u8_erase_failed_cnt、s_u8_upgrade_data_err_cnt。

                 UPDT_INV3==TRUE                    ┌──────────────────┐
UPGRADE_ARM_IDLE ──────────────────────► ENTER_ARM_UPGRADE_BOOT ──┐
        │  UPDT_INV3==FALSE                                         │ 收到 C3,50/70
        ▼                                                          ▼
 UPGRADE_ARM_FINISH ◄──── err>20 ────────────────────  WAIT_ARM_ERASE_RESPONSE
        ▲                                                          │ 收到 C4 + buf[9]==01
        │ err>30                                                   ▼
        └──────────────────────  WRITE_ARM_FIRMWARE_CONTENT ◄───────┘
                                        │ 末包(FLAG=0x01)写入成功
                                        ▼
                                 UPGRADE_ARM_FINISH
状态动作迁移条件
UPGRADE_ARM_IDLE(:379)检查 Get_Status_Is_Updated_From_Server(UPDT_INV3);成立则设 u8_upgrade_object=GINLONG_ARM、u8_upgrade_addr=g_u8_fw_sdram1、u32_upgrade_file_len=get_length_file_updating(UPDT_INV3)有升级任务 → ENTER_ARM_UPGRADE_BOOT;无 → UPGRADE_ARM_FINISH
ENTER_ARM_UPGRADE_BOOT(:425)若 send_err_cnt == 10 先 usart2_init(57600) 切波特率;发 25 字节 0xC3 跳 BOOT;收 300 ms`buf[2]==0xC3 && (buf[3]==0x50 \\buf[3]==0x70) → WAIT_ARM_ERASE_RESPONSE(并再次 usart2_init(57600)、清计数器);否则 err_cnt++,>20 → UPGRADE_ARM_FINISH`
WAIT_ARM_ERASE_RESPONSE(:461)先 OSTimeDlyHMSM(0,0,1,0) 等 1 s;发 16 字节 0xC4 擦除查询;收 2500 msrecv_num != 0 && buf[2]==0xC4 && buf[9]==0x01 → WRITE_ARM_FIRMWARE_CONTENT(erase_failed_cnt=0);否则 erase_failed_cnt++,>30 → UPGRADE_ARM_FINISH
WRITE_ARM_FIRMWARE_CONTENT(:493)若 send_line_num == upgrade_line_num(上包已确认)则 send_line_num++;按末包/中间包分别填 buf[3..8];fill_arm_upgrade_data_accl_code();发送;收 300 ms应答 buf[2]==0xC4 && buf[3]==0x0A && buf[5]==发送行号低字节 && buf[9]==0x01:offset += LEN-4,upgrade_line_num++,err=0;若发送帧 buf[7]==0x01(末包)→ UPGRADE_ARM_FINISH。任一步不匹配则 err++,>10 → UPGRADE_ARM_FINISH
SEND_ARM_OFF_GRID_COMMAND(:394)内层 for(i=0;i<3;i++) 发送 55 字节离网指令,收 300 ms,判断 buf[1]==0x01 && buf[2]==0x03命中 → ENTER_ARM_UPGRADE_BOOT;err_cnt > 10 → ENTER_ARM_UPGRADE_BOOT(无实现入口,死状态)
UPGRADE_ARM_FINISH(:577)状态复位为 IDLE,三个计数器清零,memset(&s_t_upgrade_tab,0,sizeof),usart2_init(9600),usart2_clear_buffer(),Set_Status_Is_Updated_From_Server(UPDT_INV3, FALSE)无条件回到 UPGRADE_ARM_IDLE

关键细节:

4.5 DSP 升级状态机(ginlong_upgrade_dsp_handle,行 598-883)

DSP 升级比 ARM 多两个阶段:先整体上传"API 数据区",再擦除、再逐包写固件,最后显式退出 BOOT。

UPGRADE_DSP_IDLE ──(UPDT_INV1 && UPDT_INV2)──► SEND_OFF_GRID_COMMAND
      ▲                                              │ 收到 01/03
      │                                             ▼
UPGRADE_DSP_FINISH ◄──────────────  ENTER_DSP_UPGRADE_BOOT
      ▲  ▲                              │ 收到 C5 / buf[4]==0xFF
      │  │  err>20                      ▼
      │  └──── EXIT_DSP_UPGRADE ◄── WRITE_DSP_FIRMWARE_DATA
      │            │ 收到 C7/03 → 发 C5 退出 → 收到 C5/00    ▲
      │            └────────────────────► UPGRADE_DSP_FINISH │ 擦除确认 C7/02
      │                                                     │
      │                     WAIT_DSP_ERASE_RESPONSE ────────┘
      │                          │ err_cnt==10 回退重传 API 数据
      └──────────────────────────┴──────► WRITE_DSP_API_DATA
状态动作迁移条件
UPGRADE_DSP_IDLE(:612)需 Get_Status_Is_Updated_From_Server(UPDT_INV1) && ...(UPDT_INV2) 同时成立;设对象 GINLONG_SLAVE、地址 g_u8_fw_sdram2、长度 get_length_file_updating(UPDT_INV2)成立 → SEND_OFF_GRID_COMMAND;否则 → UPGRADE_DSP_FINISH(注意:DSP 升级要求 ARM 与从机任务同时挂起)
SEND_OFF_GRID_COMMAND(:634)内层循环 3 次发送 55 字节离网指令,收 300 ms,判 buf[1]==0x01 && buf[2]==0x03命中 → ENTER_DSP_UPGRADE_BOOT;err_cnt > 10 → ENTER_DSP_UPGRADE_BOOT
ENTER_DSP_UPGRADE_BOOT(:666)OSTimeDlyHMSM(0,0,1,0) 1 s;发 15 字节 0xC5 跳 BOOT;收 500 msbuf[2]==0xC5 && buf[4]==0xFF → WRITE_DSP_API_DATA,并把对象重置为 SLAVE/sdram2/UPDT_INV2、offset=0、行号=0、memset(upgrade_buf)、usart2_init(19200);否则 err_cnt++,>10 → UPGRADE_DSP_FINISH
WRITE_DSP_API_DATA(:706)一次性把整段数据 ginlong_send_upgrade_data(u8_upgrade_addr, u32_upgrade_file_len) 发出(不过 fill_* 组帧,原始字节流);然后 OSTimeDlyHMSM(0,0,5,0) 等 5 s无条件 → WAIT_DSP_ERASE_RESPONSE
WAIT_DSP_ERASE_RESPONSE(:722)erase_failed_cnt == 0 或 10 时发 27 字节变体查询,否则发 15 字节标准查询;收 1500 msbuf[2]==0xC7 && buf[4]==0x02 → WRITE_DSP_FIRMWARE_DATA,对象切到 MASTER/sdram1/UPDT_INV1、偏移与行号归零、memset(upgrade_buf)、erase_failed_cnt=0、延时 1 s;否则延时 1 s 后 erase_failed_cnt++,==10 → 退回 WRITE_DSP_API_DATA(重传 API 数据),>20 → UPGRADE_DSP_FINISH
WRITE_DSP_FIRMWARE_DATA(:770)行号推进同 ARM;fill_dsp_upgrade_data_accl_code();发送;收 300 msbuf[2]==0xC6 && buf[3]==0x0A && buf[6]==发送行号低字节 && buf[5]==0x00:offset += LEN-2,upgrade_line_num++,err=0;offset >= file_len → EXIT_DSP_UPGRADE。否则 err++,>10 → UPGRADE_DSP_FINISH
EXIT_DSP_UPGRADE(:830)延时 1 s;发 15 字节 0xC7 查询(取擦写状态);收 300 msbuf[2]==0xC7 && buf[4]==0x03 → 发 15 字节 0xC5 退出指令,延时 200 ms,再收 300 ms;若 buf[2]==0xC5 && buf[4]==0x00 → UPGRADE_DSP_FINISH。随后 erase_failed_cnt++,>20 → UPGRADE_DSP_FINISH
UPGRADE_DSP_FINISH(:869)复位状态与计数器、memset(&s_t_upgrade_tab,0,...)、usart2_init(9600)、usart2_clear_buffer()、OSTimeDlyHMSM(0, 0, 40, 0) 阻塞 40 s,最后 Set_Status_Is_Updated_From_Server(UPDT_INV1/FALSE) 与 (UPDT_INV2/FALSE)无条件回到 UPGRADE_DSP_IDLE

DSP 状态机的三个独特设计:

  1. WRITE_DSP_API_DATA 整包直发:不经 u8_upgrade_buf,直接把 SDRAM 中的镜像原样发送(ginlong_upgrade.c:716),这是"API/参数段"的专用上传方式,与后续逐包写固件格式完全不同。
  2. 擦除失败的回退重传:erase_failed_cnt == 10 触发从 WAIT_DSP_ERASE_RESPONSE 退回 WRITE_DSP_API_DATA(:758-761),把"擦除不成功"归因为"API 数据未正确落地",是一个闭环纠正机制。
  3. 变体查询指令补偿:第 0 次和第 10 次擦除查询用 27 字节版本(s_u8_query_dsp_erase_flash),中间用 15 字节版本(:726-733),推测与 DSP BOOT 状态机对指令长度的识别差异有关。

4.6 发送流控(ginlong_send_upgrade_data,行 58-75)

if (abs(OSTimeGet() - s_u32_send_pack_tick) < DAT_PCK_UPGRADE_INV)   // 300
    OSTimeDlyHMSM(0, 0, 0, abs(DAT_PCK_UPGRADE_INV - abs(OSTimeGet() - s_u32_send_pack_tick)));
s_u32_send_pack_tick = OSTimeGet();
usart2_clear_buffer();
usart2_send_data(buf, len, 5000);

4.7 调度时序与 uCOS-II 配合

4.8 关键全局变量的读写

变量读写
s_t_upgrade_tab全部状态、百分比查询接口、组帧函数IDLE 状态初始化对象与长度;组帧函数写 u8_upgrade_buf;WRITE 状态更新 u32_upgrade_file_offset 与 u16_upgrade_line_num;FINISH 状态整体 memset
g_u8_receive_upgrade_buf各状态的应答判断(buf[1]/[2]/[3]/[4]/[5]/[6]/[9])inv_recv_upgrade_data() 内部逐字节写入
g_u8_fw_sdram1 / g_u8_fw_sdram2组帧时按 (line-1)*1024 偏移读取固件字节(fill_*_accl_code、WRITE_DSP_API_DATA)不由本文件写,由 net/upd_download.c 下载填充
g_t_inv_info.t_run_info.u8_run_mode不经本文件由 ginlong_comm_usart.c:4871/4873/4882 依据 ginlong_read_upgrade_flag_*() 设为 cUpdateMaster(8)/cUpdateSlave(9)/cUpdateArm(10)
Set_Status_Is_Updated_From_Server(...)各 IDLE 状态读 Get_Status_...两个 FINISH 状态置 FALSE,清除升级任务标志

5. 对外接口

本文件导出的函数与变量:

符号声明位置调用方
ginlong_upgrade_arm_handleginlong_upgrade.h:88ginlong_comm_usart.c:4885
ginlong_upgrade_dsp_handleginlong_upgrade.h:89ginlong_comm_usart.c:4875
ginlong_send_upgrade_dataginlong_upgrade.h:91本文件内部;头文件已导出但未见他文件调用
ginlong_read_upgrade_flag_masterginlong_upgrade.h:92inverter.c:3816、ginlong_comm_usart.c:4870
ginlong_read_upgrade_percent_masterginlong_upgrade.h:93inverter.c:3851
ginlong_read_upgrade_flag_slaveginlong_upgrade.h:94inverter.c:3886、ginlong_comm_usart.c:4872
ginlong_read_upgrade_percent_slaveginlong_upgrade.h:95inverter.c:3921
ginlong_read_upgrade_flag_armginlong_upgrade.h:96inverter.c:3972
ginlong_read_upgrade_percent_armginlong_upgrade.h:97inverter.c:3999
g_u8_receive_upgrade_buf无声明仅本文件使用
inv_recv_upgrade_data无声明仅本文件使用
fill_arm_upgrade_data_accl_code / fill_dsp_upgrade_data_accl_code无声明仅本文件使用

本文件调用的外部函数/变量:

名称来源模块
OSTimeGet、OSTimeDlyHMSMuCOS-II
usart2_init、usart2_clear_buffer、usart2_send_data、usart2_get_data_len、usart2_read_data_asynchardware/usart2/usart2.h
Get_Status_Is_Updated_From_Server、Set_Status_Is_Updated_From_Server、get_length_file_updatingnet/upd_download.h
UPDT_INV1、UPDT_INV2、UPDT_INV3、g_u8_fw_sdram1、g_u8_fw_sdram2net/server.h
abs、memset、printfC 标准库
hex_file_parse.h、inv_download.h、sdram.h已 include 但本文件中无直接引用

6. 依赖关系

依赖(include,ginlong_upgrade.c:13-19): ginlong_upgrade.h、upd_download.h、inv_download.h、hex_file_parse.h、server.h、sdram.h、usart2.h。

被依赖:

7. 备注与风险点

严重

  1. inv_recv_upgrade_data() 中缓冲区越界写(:156-165):l_u16_recv_len 完全来自报文内容(buf[3] 与 buf[4]),未做任何上界检查,就直接用于 *(buf + l_u16_index + l_tick) 与 *(buf + l_u16_recv_len + l_tick) 写入。缓冲区仅 200 字节(g_u8_receive_upgrade_buf[200],ginlong_upgrade.c:25)。若 UART 受干扰或逆变器返回异常长度(最大可达 0xFFFF),会直接写穿全局区,可能破坏相邻全局变量甚至栈。建议在 :146 前对 l_u16_recv_len 做 > INV_RECV_UPGRADE_BUFFER_SIZE - 8 的钳位并返回 0。
  1. buf[l_u16_len-1] 在 l_u16_len == 0 时下标为 -1(:182):该分支进入条件正是 l_u16_len == 0(第 109 行 if(l_u16_len) 为假),因此 buf[-1] 是必然发生的越界读(只不过读到的值不影响结果,因为两个分支都写 *len = 0)。这是一段逻辑上完全无效且不安全的代码,建议直接删除整个 else if,改为 *len = 0。
  1. 长度高字节拼接写错(:149):l_u16_recv_len = (data << 16) | l_u16_recv_len; 注释为"协议数据长度高8位",但 data << 16 对 16 位目标变量而言低 16 位全为 0,赋值时被截断,高字节被彻底丢弃(应为 data << 8)。当前之所以没出问题,是因为所有 0xC4 应答的长度高字节都是 0x00(如 7E 01 C4 0A 00 ...),一旦出现长度 > 255 的应答就会解析错误,并可能触发上述第 1 条的越界写。

中等

  1. SEND_ARM_OFF_GRID_COMMAND 是死状态:ginlong_upgrade.h:49 定义了该状态,:394 有完整实现,但 UPGRADE_ARM_IDLE 直接跳到 ENTER_ARM_UPGRADE_BOOT(:386),全代码中没有任何向 SEND_ARM_OFF_GRID_COMMAND 的迁移。也就是说 ARM 升级从不发送离网指令,逆变器在上位机侧保持并网状态下被直接跳进 BOOT。是否是设计意图需要与逆变器协议方确认。
  1. 两处错误处理与注释相反::418-422 与 :658-662,注释写"升级失败,退出升级,上传错误信息",实际却迁移到 ENTER_ARM_UPGRADE_BOOT / ENTER_DSP_UPGRADE_BOOT(继续尝试),并非退出。属于注释与实现不一致,容易误导维护者。
  1. UPGRADE_DSP_FINISH 中阻塞 40 s(:877):OSTimeDlyHMSM(0, 0, 40, 0) 在任务上下文里睡 40 秒,期间该任务完全不可响应。若该任务还负责其他通信/看门狗喂狗,存在看门狗复位或通信失联的隐患。
  1. err_cnt > 10 判定与内层循环叠加(:396-422):SEND_ARM_OFF_GRID_COMMAND 的内层 for(i=0;i<3;i++) 每次都会 err_cnt++,所以外层一次调用最多 +3,>10 实际需要约 4 次调用。但 ENTER_ARM_UPGRADE_BOOT 的 if(10 == err_cnt) 依赖精确等于 10,而该计数器在 SEND 状态里可能从 9 直接跳到 11,导致 usart2_init(57600) 被跳过——这是一处真实的时序脆弱点(虽然该状态目前不可达)。
  1. ginlong_upgrade_percent_* 可能除零(:293、:325、:355):(u32_upgrade_file_offset*100) / u32_upgrade_file_len,当 get_length_file_updating() 返回 0(例如服务器下发的文件长度域异常)时会整数除零,在 Cortex-M 上默认触发 HardFault。
  1. u16_upgrade_file_offset*100 溢出:u32_upgrade_file_offset 最大可接近固件长度(数 MB),乘以 100 后仍在 u32 范围内(约 4.29e9),但若文件大于 42 MB 则溢出。当前锦浪固件远小于该值,暂不构成实际问题。

轻微

  1. ginlong_upgrade_handle 只有声明没有定义:ginlong_upgrade.h:90 声明,全工程无定义(grep -rn "ginlong_upgrade_handle" 仅命中头文件一行),是遗留死声明。
  1. u8_upgrade_buf[1035] 与 SEND_UPGRADE_DATA_SIZE 无编译期约束:ARM 帧最大占用 1032 字节、DSP 帧 1030 字节,1035 仅有 3 字节余量。若将来把 SEND_UPGRADE_DATA_SIZE 调大(如 2048),会静默栈/全局溢出。
  1. 校验和是简单累加和(:208-220、:252-263):sum & 0xFF 无进位旋转,抗随机误码能力很弱。同时 s_u16_accl_temp 类型为 u16,累加 1028 个字节不会溢出(最大 261, 实际 <65536),但如果未来包长超过 257 字节则需注意 u16 溢出后 & 0xFF 的结果仍然正确(模 256 语义下累加不敏感),实际无影响。
  1. s_u8_query_dsp_erase_flash[27] 结构可疑(:46-48):前 15 字节与 g_u8_query_dsp_erase_flash_instruction 完全相同,第 15 字节是 0xA7(既非 0x7E 起始码,也不是前一帧的校验 0xD2),后接 11 个零再以 0xD2 结尾。这看起来像是"两帧拼接后人为插入的填充",缺乏任何注释说明其必要性。
  1. inv_recv_upgrade_data 的轮询策略低效(:98-107):仅当接收长度为 15 或 16 时才提前退出,否则一律空等到 timeout。这意味着所有非 15/16 字节的应答(如 25 字节的 0xC3 应答)都会引入完整的超时延迟(300~2500 ms),是升级过程总耗时偏长的主要原因之一。
  1. g_u8_receive_upgrade_buf 声明为全局但头文件无 extern(:25):与 ginlong_comm_can.c 的 g_t_ginlong_comm_can_info 同类问题——本意是全局可访问,实际退化为文件私有,属于接口不一致。
  1. 冗余 include:hex_file_parse.h、inv_download.h、sdram.h 在本文件中未见直接引用(SDRAM 相关地址是经 server.h 的 g_u8_fw_sdram1/2 间接使用的)。
  1. 函数注释模板残留::268、:280、:300、:312、:330、:342 的注释标题全部写为 "kelong_read_upgrade_flag_*" / "kelong_read_upgrade_percent_*",而实现是 ginlong 版本,是从 kelong 升级模块复制后未清理的痕迹。