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

set_upgrade_s5.c

源文件路径:midware/inverter/set/set_upgrade_s5.c 所属模块:midware/inverter/set(盛能杰 S5 / S5-II / M10 逆变器通信) 代码行数:727

1. 文件职责

本文件实现盛能杰 Smile5 系列(SUB_S5 / SUB_S5_II / SUB_M10)逆变器主控 DSP、从机 DSP 与 DCDC(ARM) 模块的串口 Bootloader 固件升级。它处在"云端下发固件 → UART2 逐块搬进 SDRAM(g_u8_fw_sdram1/2)→ 本文件通过 UART2 把固件写入逆变器"这条链路的末端,是整个过程唯一真正与逆变器 Bootloader 对话的代码。

它由 midware/inverter/inverter.c:1090 的 task_inverter(uCOS-II 任务,周期 80 ms,inverter.c:1188)→ set_comm_usart_s5.c:6199 的 senergys5_inverter_comm() 周期调用:一旦发现固件已下载完成就把正常遥测通信让位给升级流程。状态机的每一次进入只推进"一步"(发一帧或发一个 2 KB 数据块),靠 s_senergys5_upgrade_info.upgrade_status 在多次调用之间保持进度。

进度与标志通过 inverter.c 的 inverter_read_upgrade_flag_* / inverter_read_upgrade_percent_* 聚合后上报云端与 HMI(inverter.c:3812/3847/3882/3917/3976/4003)。

2. 关键数据结构与宏

2.1 本文件定义/持有的对象

名称类型说明
s_senergys5_upgrade_info全局结构体变量(SENERGYS5_UPGRADE_INFO_T,__attribute__((at(SDRAM_SENERGYS5_UPGRADE_INFO_ADDR))),:25)升级上下文,被强制定位到 SDRAM 固定地址(hardware/sdram/sdram.h:67 标注 Len:3k)。上电不清零、跨软复位保留,是本文件唯一的状态载体
s_temp_buf[4]static u8 数组(:27)每块的 4 字节累加校验和暂存区(大端)。非持久化,掉电即失
senergys5_query_inv_update_versions_infoextern 函数声明(:28)冗余声明:set_comm_usart_s5.h:829 已有同签名声明;定义在 set_comm_usart_s5.c:1949,升级结束后回读主机版本寄存器
SENERGYS5_UPGRADE_INFO_T结构体(#pragma pack(1),定义于 set_upgrade_s5.h:45-62)升级上下文字段表(对象/状态/地址/偏移/长度/块计数/校验和/2048 字节数据缓冲/u8_set_dcdc_upgrade_on_flag),详见 set_upgrade_s5.h.md
SENERGYS5_UPGRADE_STATUS_E枚举(set_upgrade_s5.h:34-41)状态机五态:ENTER_BOOT=0、SHAKE_HANDS、SEND_FILE_PROPERTIES、SEND_UPGRADE_FILE、UPGRADE_END
SENERGYS5_UPGRADE_OBJECT_E枚举(set_upgrade_s5.h:26-32)升级对象:NONE=0、MASTER=1、SLAVE、DCDC

2.2 本文件主要使用的宏

名称类型说明
SENERGYS5_UPGRADE_DEBUG宏 0(set_upgrade_s5.h:15)全文件 printf 总开关,量产关闭
SENERGYS5_INV_REPLY_CODE宏 0xA5Bootloader 通用应答码;握手/块长度/块校验和/文件属性四个阶段都等它
SENERGYS5_INV_REPLY_CODE1宏 0x55本文件不使用(仅被 set_comm_usart_s5.c:428 用于通信探测)
SENERGYS5_INV_BLOCK_SIZE宏 2048每块数据字节数,同时是 u8_upgrade_buf 容量与每轮循环的偏移步长
SENERGYS5_ADDR_INV_UPADTE_VERSIONS_INFO宏 0x1A1C升级后回读版本的寄存器起始地址
SENERGYS5_NUM_INV_UPADTE_VERSIONS_INFO宏 0x0D回读寄存器个数 13(应答 26 字节)
SDRAM_SENERGYS5_UPGRADE_INFO_ADDR宏(hardware/sdram/sdram.h:67)升级上下文的固定 SDRAM 地址
UPDT_INV1 / UPDT_INV2 / UPDT_INV3枚举值(net/server.h:190-192,ENUpdateType,值为 2/3/4)分别对应主机固件、从机固件、ARM(DCDC) 固件
SUB_S5_II / SUB_M10枚举值(midware/inverter/inverter.h:332-333)决定数据块前是否追加 2 字节长度命令

2.3 逆向出的协议字段(本文件以内联常量形式写死,无宏)

名称值说明
ENTER_BOOT 命令头0x01 0xA0u8_upgrade_buf[0..1]
ENTER_BOOT 对象码0x01(主机) / 0x02(从机) / 0x05(DCDC)u8_upgrade_buf[2]
ENTER_BOOT 固定引导字0x33 0x77 0x99u8_upgrade_buf[3..5],三对象一致
ENTER_BOOT 口令对0x97 0xBA(主机) / 0x97 0xFE(从机) / 0x96 0x8A(DCDC)u8_upgrade_buf[6..7],随对象变化
期望应答码0x55(主机) / 0x56(从机) / 0x62(DCDC)存在 u8_upgrade_buf[8],不进 ENTER_BOOT 帧,在 SHAKE_HANDS 阶段单独发出
块长度命令0x08 0x00大端 0x0800=2048,仅 S5-II / M10 发送

3. 函数清单

函数名作用域说明
senergys5_data_recv_flag(void)static(:43)转发 usart2_is_recv_async_data(),判断 UART2 异步接收缓冲是否有字节
senergys5_send_data(u8 *data_buf, u16 data_len, u16 timeout)static(:63)先清 UART2 接收缓冲再发数据,避免上一帧残留回显污染本次应答判断
senergys5_upgrade_send_mdsp(const u8 *upgrade_buf, const u16 upgrade_len)static(:83)升级专用发送封装,固定 4000 ms 超时,调试模式下逐字节打印
senergys5_upgrade_recv_mdsp(const u8 reply_code, const u32 timeout)全局(:116)阻塞轮询等待指定应答码,超时返回 FALSE;被头文件导出供通信探测复用
senergys5_get_inv_upgrade_flag(void)全局(:160)查询主/从机固件是否已下载完成,并带副作用地置位 master/slave 待升级标志
senergys5_get_dcdc_start_upgrade_flag(void)全局(:184)查询 ARM(DCDC) 固件是否已下载完成,并置位 dcdc 待升级标志
senergys5_get_master_upgrade_flag(void)全局(:206)master_flag && object==MASTER,供上层切换运行模式
senergys5_read_upgrade_percent_master(void)全局(:220)主机进度百分比 = offset*100/file_len,对象不匹配返回 0
senergys5_get_slave_upgrade_flag(void)全局(:238)slave_flag && object==SLAVE
senergys5_read_upgrade_percent_slave(void)全局(:252)从机进度百分比
senergys5_get_dcdc_upgrade_flag(void)全局(:270)dcdc_flag && object==DCDC
senergys5_read_upgrade_percent_arm(void)全局(:284)DCDC 进度百分比
senergys5_upgrade_code_handle(const u8* buf, const u16 len, const u8 relpy)static(:308)"发一帧 + 等一个应答码"的原子动作,2000 ms 超时
senergys5_upgrade_send_file_properties(SENERGYS5_UPGRADE_INFO_T *upgrade_info)static(:339)从固件头部取 8 字节,拆成两次 4 字节写,并解析文件长度/总块数
senergys5_fill_dsp_upgrade_data(void)static(:384)按偏移读 2048 字节(末块取余数)并逐字节累加出 32 位校验和
senergys5_inv_upgrade_handle(void)全局(:429)升级状态机主体,单次调用只推进一个状态的一步

4. 核心逻辑解析

4.1 状态机总览与迁移条件

switch (s_senergys5_upgrade_info.upgrade_status) 位于 :439:

ENTER_BOOT ──成功──► SHAKE_HANDS ──成功──► SEND_FILE_PROPERTIES ──成功──► SEND_UPGRADE_FILE
   │ 失败×5                │ 失败×5                 │ 失败×5                     │
   └──重试(SHAKE_HANDS)    └──► END                └──► END                     │
                                                    ▲                          │
                       (对象=MASTER 且版本回读成功)│              块数用尽/失败×5
                                                    └──────────  UPGRADE_END ◄──┘

成功(收到期望应答码)→ SHAKE_HANDS;失败 err_count++,累计到 5 次时不清 END 而是回到 SHAKE_HANDS(:509-514,这是唯一"越级前进"的分支,见 7.3)。

4.2 帧格式与收发时序

阶段一 ENTER_BOOT(8 字节,无校验字段)

偏移  0     1     2        3     4     5     6     7
    0x01  0xA0  对象码  0x33  0x77  0x99  口令H 口令L
主机:          0x01                      0x97  0xBA   期望应答 0x55
从机:          0x02                      0x97  0xFE   期望应答 0x56
DCDC:          0x05                      0x96  0x8A   期望应答 0x62

发送长度 8(:500),应答码取 u8_upgrade_buf[8]。帧内无长度域、无 CRC,靠固定引导字 + 口令对做合法性判断。

阶段二 SHAKE_HANDS(1 字节):仅发 u8_upgrade_buf[8],即把阶段一"预留"的应答码作为握手字节真正发出(:522),等待 0xA5。

阶段三 SEND_FILE_PROPERTIES(4 + 4 字节,两段之间有 200 ms 静默)

:344-349 从 u8_upgrade_addr 起读 8 字节到 u8_upgrade_buf[0..7](固件头,含义未文档化);:351 以大端解析 buf[4..7] 为 u32_upgradr_file_size;:352 得 u32_upgradr_block_count = file_size / 2048(整除)。随后 :354 发 buf[0..3]、OSTimeDlyHMSM(0,0,0,200)、:362 发 buf[4..7](即文件长度),最后等 0xA5,超时 105000 ms(:368,注释"将 15s 超时,改成 105S",说明逆变器写 Flash 元信息耗时很长)。

阶段四 SEND_UPGRADE_FILE(每块 0/2 + 2048 + 4 字节)

  1. senergys5_fill_dsp_upgrade_data()(:588)准备 2 KB 数据与校验和。
  2. 仅当 get_inverter_sub() 为 SUB_S5_II 或 SUB_M10(:596),先发 2 字节 0x08 0x00(=2048,大端)并等 0xA5——这是"本块长度"命令;SUB_S5 不发。
  3. 发 2048 字节原始数据(:618,不经 senergys5_upgrade_code_handle,即发完不等应答)。
  4. 发 4 字节大端累加校验和并等 0xA5(:637)。
  5. 三步全部成功(err_count == 0)才 OSTimeDlyHMSM(0,0,0,200)、offset += 2048、send_block_count++(:662-667)。

超时/重试:发送层 usart2_send_data(..., 4000) 为 4000 ms(:95);senergys5_upgrade_code_handle 的等待为 2000 ms(:317);每阶段连续失败 5 次即放弃并置 UPGRADE_END。注意 u8_upgradr_err_count 是跨阶段共用的一个计数器,只有成功路径才清零,失败路径保留累加。

4.3 校验和计算(senergys5_fill_dsp_upgrade_data,:384-416)

l_u16_data_len = (offset + 2048 >= file_len) ? (file_len - offset) : 2048   // :392-399
l_addr_temp    = u8_upgrade_addr + offset                                   // :403
u32_check_code = Σ buf[i]  (i = 0..data_len-1,字节无符号累加进 u32)         // :410-414

结果按大端拆成 4 字节存 s_temp_buf(:591-594)。关键点:u32_check_code 只累加从 SDRAM 真实读到的 data_len 个字节,0xFF 填充部分不参与求和。

4.4 块循环的终止条件

:574 的判断是 u32_send_block_count == u32_upgradr_block_count。因为 u32_upgradr_block_count 是整除结果,当文件长度不是 2048 的整数倍时,最后一个不满 2048 的块永远不会被发送(send_block_count 只能取到整除值)。终止时 :576-584 清零 offset 与 send_block_count、状态置 UPGRADE_END,并记录 s_u32_query_inv_ver_tick = OSTimeGet() 作为版本回读的起算点。

4.5 收尾与版本回读(SENERGYS5_UPGRADE_END,:670-717)

对象处理
MASTER(:674-698)距 s_u32_query_inv_ver_tick 超过 10 s 且重试计数 < 12 时,调 senergys5_query_inv_update_versions_info(0x1A1C, 0x0D)。失败则计数 +1、重置 10 s 起算点(重试周期 10 s,最多 12 次 ≈ 120 s);成功则 master_flag = FALSE 且状态回到 ENTER_BOOT,从而接着升级从机。计数 ≥ 12 时同时放弃主、从两机标志
SLAVE(:699-702)直接 slave_flag = FALSE(状态保持 UPGRADE_END)
DCDC(:703-709)Set_Status_Is_Updated_From_Server(UPDT_INV3, FALSE)、dcdc_flag = FALSE、整体 memset 清零,然后置 u8_set_dcdc_upgrade_on_flag = TRUE
公共尾部(:711-716)主、从标志都为 FALSE 时清 UPDT_INV2/UPDT_INV1 的"服务器已就绪"状态并整体 memset

memset 后 upgrade_status 归 0 = ENTER_BOOT,因此所有复位路径都自动回到状态机入口;下一次 ENTER_BOOT 发现三 flag 全 FALSE 就置 UPGRADE_END 返回,形成稳定空闲态。

4.6 与 uCOS-II 任务/时序的配合

4.7 关键全局变量的读写关系

变量读写
s_senergys5_upgrade_info.upgrade_status每次进入 senergys5_inv_upgrade_handle 的 switch各状态迁移点;memset 归 0 等价于回 ENTER_BOOT
u8_upgrade_master_flag / u8_upgrade_slave_flagENTER_BOOT 选对象、get_*_upgrade_flag、:711senergys5_get_inv_upgrade_flag() 置位;UPGRADE_END 清除
u8_upgrade_dcdc_flag同上senergys5_get_dcdc_start_upgrade_flag() 置位;:706 清除
u8_upgrade_object三个 get_*_upgrade_flag、百分比函数ENTER_BOOT 按 flag 设定;memset 归 0
u8_upgrade_addrsend_file_properties、fill_dsp_upgrade_dataENTER_BOOT 设为 g_u8_fw_sdram1/2
u32_upgrade_file_offset / u32_send_block_count百分比、块循环:551 置 8;:665-666 递增;:579-580 清零
u8_set_dcdc_upgrade_on_flagset_comm_usart_s5.c:2831(触发逆变器强制下电):708 置 TRUE;set_comm_usart_s5.c:502/510 初始化;memset 清零
g_u8_fw_sdram1 / g_u8_fw_sdram2只读(固件源)由 net/server.c:69-70 与下载流程写入

5. 对外接口

5.1 本文件导出

名称类型使用者(真实搜索结果)
senergys5_inv_upgrade_handle()函数midware/inverter/set/set_comm_usart_s5.c:6211
senergys5_get_inv_upgrade_flag()函数set_comm_usart_s5.c:6201
senergys5_get_dcdc_start_upgrade_flag()函数set_comm_usart_s5.c:6201
senergys5_get_master_upgrade_flag()函数set_comm_usart_s5.c:6213、midware/inverter/inverter.c:3812
senergys5_get_slave_upgrade_flag()函数set_comm_usart_s5.c:6218、inverter.c:3882
senergys5_get_dcdc_upgrade_flag()函数set_comm_usart_s5.c:6223、inverter.c:3976
senergys5_read_upgrade_percent_master()函数inverter.c:3847
senergys5_read_upgrade_percent_slave()函数inverter.c:3917
senergys5_read_upgrade_percent_arm()函数inverter.c:4003
senergys5_upgrade_recv_mdsp()函数set_comm_usart_s5.c:428(senergy_comm_status_check 通信探测)
s_senergys5_upgrade_info全局变量set_comm_usart_s5.c:502/510/2831/2833(初始化清零、置/读 DCDC 下电标记)

5.2 本文件调用或引用的外部符号

名称来源模块
usart2_clear_buffer、usart2_send_data、usart2_is_recv_async_data、usart2_read_data_asynchardware/usart2/usart2.h:9-13(hardware/usart2/usart2.c)
OSTimeGet、OSTimeDlyHMSMuCOS-II(UCOSII/)
is_inverter_firm_download_finishednet/inv_download.c:182(声明 inv_download.h:34)
Get_Status_Is_Updated_From_Server、Set_Status_Is_Updated_From_Server、get_length_file_updatingnet/upd_download.c:375/359/327(声明 upd_download.h:37-40)
g_u8_fw_sdram1、g_u8_fw_sdram2net/server.c:69-70(声明 server.h:258-259,各 3 MB)
senergys5_query_inv_update_versions_infomidware/inverter/set/set_comm_usart_s5.c:1949
get_inverter_sub、SUB_S5_II、SUB_M10midware/inverter/inverter.c:1369、inverter.h:332-333
SDRAM_SENERGYS5_UPGRADE_INFO_ADDRhardware/sdram/sdram.h:67
memset / printf / NULL / TRUE / FALSE / u8/u16/u32标准库与 includes.h

6. 依赖关系

依赖(本文件 :13-21 的 include)

被依赖

7. 备注与风险点

  1. memset(s_senergys5_upgrade_info.u8_upgrade_buf, 0xFF, sizeof(SENERGYS5_INV_BLOCK_SIZE));(:409)填错长度。SENERGYS5_INV_BLOCK_SIZE 是整型常量 2048,sizeof(int) == 4,所以这条语句只把前 4 个字节填成 0xFF,而不是 2048 字节(应写 sizeof(u8_upgrade_buf) 或直接 SENERGYS5_INV_BLOCK_SIZE)。后果:末块不足 2048 字节时,剩余字节是上一块遗留的旧数据而非 0xFF 填充,而校验和又只累加真实读到的字节,因此填充区内容与校验和不一致,逆变器端收到的尾部数据是脏的。本文件其它地方(:576)用的是 sizeof(s_temp_buf),写法不统一。
  2. 末块可能被整体丢弃:u32_upgradr_block_count = file_size / 2048(:352)为整除,而循环终止条件是块计数相等(:574)。文件长度非 2048 整数倍时,最后那不足 2048 字节的一段永远不会发出,只依赖逆变器端容忍缺尾。
  3. ENTER_BOOT 失败 5 次后"越级"进入 SHAKE_HANDS(:509-514)。此时阶段一并未成功、对象口令也没有被逆变器确认,但仍然跳过重试直接握手;与其它三个阶段的"失败 5 次即 UPGRADE_END"策略不一致。若保留意图是"有些固件进 Boot 后不回 0x55 而直接能握手",应在注释中说明。
  4. SEND_FILE_PROPERTIES 失败 5 次进入 UPGRADE_END 时没有复位 u32_upgrade_file_offset / u32_send_block_count(:556-565)。这些残值会留在被固定地址放置的 s_senergys5_upgrade_info 里;主机升级失败后 UPGRADE_END 若回读版本成功会回到 ENTER_BOOT 接着升从机,而进入 SEND_UPGRADE_FILE 时只重置了 offset(:551)、没有重置 send_block_count。当从机文件块数与残值巧合相等时(:574)会直接判定"已发完",把从机当成升级成功——这是一个静默失败路径。
  5. 百分比函数存在除零风险:senergys5_read_upgrade_percent_master/slave/arm(:228/260/292)都用 offset*100 / u32_upgrade_file_len,而 u32_upgrade_file_len 来自 get_length_file_updating()(服务器下发值)。若对象已选中但长度仍为 0(服务器未给出长度或下载流程未更新),就是整数除零。
  6. hex_file_parse.h、set_comm_can.h 两个 include(:17、:21)在本文件中完全未使用:无 hex_parse、无任何 CAN 符号引用,属复制自同类文件的残留。
  7. :577 有一行被注释掉的收尾命令 // l_u8_res = senergys5_upgrade_code_handle(s_temp_buf,2,SENERGYS5_INV_REPLY_CODE);,而 :576 仍在 memset(s_temp_buf, ...),说明"发完块后的 2 字节结束命令"被中途去掉,是版本遗留痕迹。
  8. 大量魔法数字内联在函数体里(:451-489 的引导字与口令、:434-435 的 0x08/0x00、:317 的 2000、:368 的 105000、:676 的 10*1000、:691 的 12 次),协议字典没有以宏形式沉淀在 set_upgrade_s5.h;头文件里反而留着零引用的 SENERGYS5_INV_UPDATE_SIZE(set_upgrade_s5.h:19)。
  9. u8_upgrade_addr 是非 const 裸指针且被写进固定 SDRAM 地址的结构体:指针值跨软复位保留,若固件版本变化导致 SDRAM 分区调整,复位后会指向错误区域;结构体本身也没有 magic/version 字段做布局校验(见 set_upgrade_s5.h.md 第 7 节)。
  10. 阻塞式等待与任务实时性:senergys5_upgrade_recv_mdsp(:116)是纯轮询死循环,task_inverter 在其返回前无法做任何事(文件属性阶段最长 105 s)。OSTimeDlyHMSM(...,50) 只提供 50 ms 粒度的让步,期间 usart2_read_data_async() 会丢弃任何不等于 reply_code 的字节——若逆变器应答含多字节或先发干扰字节,会直接误判为超时。
  11. DCDC 分支的 u8_set_dcdc_upgrade_on_flag = TRUE(:708)实际会被立即清掉:紧随其后的公共尾部判断(:711)读的是刚被 memset 清零的主/从标志,因此恒成立,:715 的第二次 memset 把刚置的标记又清零。也就是说"ARM 升级完成后强制逆变器下电"这一标记在 DCDC 路径下从未真正保持为 TRUE(该标记的消费点在 set_comm_usart_s5.c:2831)。疑似漏把该标记移到公共尾部之后。
  12. extern 声明重复::28 又声明了一遍 senergys5_query_inv_update_versions_info,而 set_comm_usart_s5.h:829 已有声明;若两处签名将来不同步会引发隐蔽错误。