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

alphaess_upgrade.c

源文件路径:midware/inverter/alphaess/alphaess_upgrade.c 所属模块:midware/inverter/alphaess(AlphaESS 逆变器通信协议 — 固件升级) 代码行数:691

1. 文件职责

本文件实现 AlphaESS 逆变器(含 DSP 双核 CPU1/CPU2)的串口固件升级流程。它把已下载到 SDRAM 的固件镜像(g_u8_fw_sdram1 / g_u8_fw_sdram2)通过 USART2 按照 AlphaESS 私有升级协议分帧发送给逆变器 DSP,逐帧等待 ACK,最后发送结束帧并等待 DSP 完成 MD5 校验。它处在"服务器下载固件 → SDRAM 缓存 → 逆变器升级"数据流的最后一环,属于 alphaess_comm_usart.c 通信任务的一个子流程(由 is_inverter_firm_download_finished() 触发),不被独立任务调度。文件顶部由 g_t_alphaess_comm_info.u32_ems_inv_comm_tick 参与链路保活,向 g_t_inv_info.t_debug_log 回报错误原因与升级状态,并通过 alphaess_read_upgrade_* 系列接口向 inverter.c 暴露升级进度。

2. 关键数据结构与宏

名称类型说明
ALPHAESS_UPGRADE_TABLE_T结构体升级过程上下文表,唯一实例为静态变量 s_alphaess_upgrade_tab(alphaess_upgrade.c:31)。含文件长度、总帧数、当前帧号、SDRAM 基址、启动/数据/结束三个发送缓冲区、读写偏移、升级对象和完成时刻 tick
s_alphaess_upgrade_tab静态全局变量ALPHAESS_UPGRADE_TABLE_T 唯一实例,alphaess_upgrade.c:31,全文件的状态载体
ALPHAESS_UPGRADE_OBJECT_E枚举ALPHAESS_NONE / ALPHAESS_MASTER / ALPHAESS_SLAVE,标记当前正在升级 CPU1 还是 CPU2(alphaess_upgrade.h:30)
ALPHAESS_UPGRADE_SATE_E枚举升级状态机状态:ALPHA_UPGRADE_START(启动帧 0x41) / ALPHA_SEND_UPGRADE_FILE(数据帧 0x42) / ALPHA_UPGRADE_END(结束帧 0x43) / ALPHA_UPGRADE_FAULT(失败)(alphaess_upgrade.h:37)
UPGRADE_DATA_MAX_LEN宏224,单帧最大有效载荷字节数(alphaess_upgrade.h:24),决定 232 字节数据帧缓冲的布局
ALPHAESS_UPGRADE_DEBUG宏0,置 1 时打开 printf 调试输出(alphaess_upgrade.h:16)
EMS_INV_SHAKE_HANDS宏0x301,声明于本头文件但本文件未使用(仅 kelong 的 CAN 升级在用,见第 7 节)
EMS_INV_SEND_UPGRADE_FILE宏0x302,同上,本文件未使用
EMS_INV_CHECK_UPGRADE_FILE宏0x303,同上,本文件未使用
EMS_INV_CHECK_UPGRADE_SATTE宏0x304,同上,本文件未使用
INV_EMS_ACK宏0x401,同上,本文件未使用
g_t_alphaess_comm_info外部结构体变量alphaess_comm_usart.h:715,本文件只写其 u32_ems_inv_comm_tick 做链路保活
g_t_inv_info.t_debug_log外部结构体成员u32_err_reason(失败原因,取值见 inverter.h:383 起,如 SENDBOOTERR/RECVMASTERERR)、u8_upgrade_status、u8_err_tick
g_t_inv_info.t_run_info.u8_run_mode外部结构体成员升级中置 cUpdateMaster / cUpdateSlave,供 HMI/逻辑层显示运行模式

3. 函数清单

函数名作用域说明
alphaess_inv_com_replystatic在 timeout 个 tick 内轮询 alphaess_recv_data(),判断逆变器回帧是否为升级 ACK(0x41/0x43 回帧看 buf[2]=buf[3]=0x06,0x42 回帧看 buf[5]=0x06)
send_ems_send_start_cmdstatic组装并发送 0x41 启动帧(含双核文件长度/MD5/总帧数/版本号),发送前先做 MD5 一致性校验
send_ems_send_upgrade_file_cmdstatic组装并发送 0x42 数据帧(CPU 号 + 帧号 + 本帧字节数 + 载荷 + CRC),超时 500 tick
send_ems_send_end_cmdstatic组装并发送 0x43 结束帧(载荷为 ASCII "End"),超时由调用方传入
alphaess_upgrade_handle全局升级主状态机,每次被通信任务调用推进一个步骤
alphaess_read_upgrade_flag_master全局返回当前升级对象是否为 CPU1(MASTER)
alphaess_read_upgrade_percent_master全局返回 CPU1 升级进度百分比 = cpu1_file_offset*100 / cpu1_file_len
alphaess_read_upgrade_flag_slave全局返回当前升级对象是否为 CPU2(SLAVE)
alphaess_read_upgrade_percent_slave全局返回 CPU2 升级进度百分比 = cpu2_file_offset*100 / cpu2_file_len
alphaess_read_upgrade_file_offset全局返回 CPU1+CPU2 已传输字节偏移之和
alphaess_get_upgrade_finish_tick全局返回升级成功完成时刻的系统 tick

4. 核心逻辑解析

4.1 状态机与迁移条件

状态存在函数内的 static u8 s_u8_upgrade_sate(alphaess_upgrade.c:247),另有 s_u8_send_err_cnt、s_u8_rev_err_cnt、s_u8_upgrade_fault_cnt、s_u16_cpu1_file_send_cnt、s_u16_cpu2_file_send_cnt 五个静态计数器。

状态迁移图(简化):START --ACK--> SEND_FILE(cpu1) --ACK--> SEND_FILE(cpu2) --ACK--> END --ACK--> START;任意环节发送或接收连续超限 → FAULT --> START。

4.2 MD5 一致性校验(启动前置条件)

send_ems_send_start_cmd(93 行)在组帧前做四路 MD5 比对(105–120):

两者均经 hex_ascii_to_bin() 转成 16 字节二进制后逐字节比较,只有当 16 字节全部相同(l_u8_md5_check_cnt == 16)才允许发启动帧,否则直接 return FALSE,状态机会把它当作"发送失败"累计 s_u8_send_err_cnt,3 次后进入 FAULT。注意 l_u8_md5_check_cnt 是 u8,上限 16 无溢出风险。

4.3 通信协议帧格式

三帧均以从机地址 ALPHAESS_SLAVR_ADDRESS = 0x03 起始,CRC 为 Modbus CRC16(GetCrcCode_Modbus),低字节在前。

启动帧 0x41(发送 112 字节,缓冲区 u8_upgrade_start_buf[112] 刚好用满)

偏移长度内容
010x03 从机地址
110x41 启动命令
2–54CPU1 文件长度(大端,源自 g_u8_fw_sdram1[0..3])
6–2116CPU1 文件 MD5(源自 g_u8_fw_sdram1[4..19])
22–232CPU1 总帧数(大端,源自 g_u8_fw_sdram1[20..21])
24–5532CPU1 软件版本号(g_u8_fw_sdram1[22..53])
56–594CPU2 文件长度(g_u8_fw_sdram1[54..57])
60–7516CPU2 MD5(g_u8_fw_sdram1[58..73])
76–772CPU2 总帧数(g_u8_fw_sdram1[74..75])
78–10932CPU2 软件版本号(g_u8_fw_sdram1[76..107])
110–1112CRC16(低字节在前)

即 SDRAM 中固件前面有 128 字节描述头(双核长度/MD5/帧数/版本),故数据区从偏移 128 开始。

数据帧 0x42(最长 232 字节,缓冲区 u8_upgrade_data_buf[232])

偏移长度内容
010x03
110x42 数据命令
21CPU 号:0x01=CPU1,0x02=CPU2
3–42帧序号(大端,u16_file_send_cnt)
51本帧有效载荷字节数 byte_num
6…5+nn载荷 addr[offset + i],非末帧 n=224
尾 2 字节2CRC16(低字节在前)

总长 = 6 + n + 2,n=224 时为 232,正好等于缓冲区大小。

结束帧 0x43(7 字节,缓冲区 u8_upgrade_end_buf[7]):03 43 45 4E 44 CRCL CRCH,其中 45 4E 44 为 ASCII "End"。

响应/超时机制:所有发送走 alphaess_write_data(buf, len, timeout)(内部 usart2_send_data);接收走 alphaess_inv_com_reply(timeout) → alphaess_recv_data()。判断 ACK 的依据(64、68):

alphaess_recv_data 内部对 buf[1]==0x42 按 CRC over 前 6 字节、对 0x41/0x43 按 CRC over 前 4 字节校验,校验失败置 *len = 0,因此升级流程里只有 CRC 正确的回帧才会被 alphaess_inv_com_reply 看到。

各环节超时值:启动帧发送 500、启动 ACK 600、数据帧发送 500、数据帧 ACK 600、结束帧发送 500、结束 ACK 30000(等 DSP MD5)。

4.4 定时/调度时序与任务配合

本文件没有自己的任务、信号量或消息邮箱,全部是同步阻塞式顺序代码。调用链为:

alphaess_comm_usart.c:5441(通信主循环) → alphaess_upgrade_handle()。

前置条件在 alphaess_comm_usart.c:5435:if (is_inverter_firm_download_finished()),即固件已完整下载到 SDRAM。调用前后:

等待 tick 用的是 OSTimeGet() 与忙循环 + abs() 差值比较,没有 OSTimeDly(OSTimeDly 只出现在被调用的 alphaess_recv_data 里),因此单次 alphaess_upgrade_handle 最多可阻塞数十秒(结束帧 30 秒)。

4.5 关键全局变量读写

变量读/写触发条件
g_t_alphaess_comm_info.u32_ems_inv_comm_tick写每次收到回帧(alphaess_upgrade.c:62)
g_t_inv_info.t_debug_log.u32_err_reason写各类发送/接收连续失败(288/302/351/364/391/404/454/467/495/508/557/566)
g_t_inv_info.t_debug_log.u8_upgrade_status写成功置 2(547),失败置 TRUE(605);alphaess_comm_usart.c:5470 据此调 set_info_status(INV_UPGRADE_LOG)
g_t_inv_info.t_debug_log.u8_err_tick写FAULT 分支每轮自增(604)
g_t_inv_info.t_run_info.u8_run_mode写CPU1/CPU2 数据帧 ACK 成功后置 cUpdateMaster / cUpdateSlave(341、444)
g_stEMSInfo.stUpt[].as8MD5Code读启动帧组帧前的 MD5 比对(109、111)
g_u8_fw_sdram1 / g_u8_fw_sdram2读描述头解析(124–169)与数据帧载荷(203)

5. 对外接口

本文件导出(供外部调用) —— 声明见 alphaess_upgrade.h:66-72:

导出符号调用方
alphaess_upgrade_handle()midware/inverter/alphaess/alphaess_comm_usart.c:5441
alphaess_read_upgrade_flag_master()midware/inverter/inverter.c:3820
alphaess_read_upgrade_percent_master()midware/inverter/inverter.c:3855
alphaess_read_upgrade_flag_slave()midware/inverter/inverter.c:3890
alphaess_read_upgrade_percent_slave()midware/inverter/inverter.c:3925
alphaess_read_upgrade_file_offset()midware/inverter/inverter.c:3947
alphaess_get_upgrade_finish_tick()midware/inverter/alphaess/alphaess_comm_usart.c:5450

未导出全局变量(仅 static):s_alphaess_upgrade_tab。

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

外部符号来源模块
alphaess_write_data / alphaess_recv_data / alphaess_comm_initmidware/inverter/alphaess/alphaess_comm_usart.c(.h:262/285/718)
alphaess_comm_init_canmidware/inverter/alphaess/alphaess_comm_can.h:208
get_curr_md5_code_fw_file / get_length_file_updating / set_flag_force_update / Set_Status_Is_Updated_From_Servernet/upd_download.h:38/25/39
is_bwinv_firm_download_finished / is_inverter_firm_download_finishednet/inv_download.h:35(后者在 usart 侧判断)
g_u8_fw_sdram1 / g_u8_fw_sdram2net/server.h:258-259,实际落在 hardware/sdram/sdram.h 定义的 BANK5 区(各 3MB)
hex_ascii_to_bincommon/convert(convert.h)
GetCrcCode_Modbuscommon/crc(crc.h)
OSTimeGetuCOS-II
g_t_alphaess_comm_infomidware/inverter/alphaess/alphaess_comm_usart.h:715
g_t_inv_info / cUpdateMaster / cUpdateSlave / SENDBOOTERR 等错误码midware/inverter/inverter.h(枚举自 inverter.h:383)
g_stEMSInfo.stUpt[] / UPDT_INV1 / UPDT_INV2net/upd_download.h、net/ems.h
get_board_type / BOARD_EMS3_6_4_Esystem/board_init/board_init.h:22/30
set_info_status日志/上报模块(ems 侧)

alphaess_upgrade.c:13-26 的 include 列表还包括 hex_file_parse.h、sdram.h、usart2.h、inverter.h、board_init.h 等。

6. 依赖关系

7. 备注与风险点

  1. alphaess_inv_com_reply 缓冲区过小(alphaess_upgrade.c:47):u8 buf[16] 只有 16 字节,而 alphaess_recv_data 会把 UART 收到的整帧(usart2_get_data_len() 长度,普通 Modbus 读帧可达 200+ 字节)拷贝进来(alphaess_comm_usart.c:315 的循环无长度上限保护),存在栈溢出风险。
  2. 收到非预期帧立即返回 FALSE(73–80):噪声帧或上一帧的残留回包会让本轮判定直接失败并计入错误次数,容错性差。
  3. abs() 用于 tick 差值(54、alphaess_comm_usart.c:5450):OSTimeGet() 是 32 位 tick,回绕后 abs() 结果不具单调性,理论上会误判超时;同时 l_u32_recv_start_tick 由 u32 减去 u32 再取 abs(参数为 int),符号扩展下有边界问题。
  4. byte_num 为 u8 会截断:末帧长度 u32_cpu2_file_len - u32_upgrade_cpu2_file_offset(474)直接作为 u8 形参传入,若剩余字节数 >255 则被截断,导致最后一帧少发数据;u32_cpu1_file_len - ... + 128(371)同理。
  5. CPU2 描述头从 u8_cpu1_upgrade_addr 读取(151–169):CPU2 的长度/MD5/帧数/版本全部从 g_u8_fw_sdram1 的描述头继续读取,而 u8_cpu2_upgrade_addr(= g_u8_fw_sdram2)只用于数据帧载荷。这依赖"128 字节描述头同时描述双核"这一隐含约定,若服务器端文件布局变化会静默错发。
  6. u8_upgrade_start_buf[112] 与组帧长度 112 完全相等:一旦协议追加任一字节字段就会越界,无任何余量或长度断言。
  7. 错误计数阈值不一致:CPU1 末帧 s_u8_rev_err_cnt > 3(389),CPU2 末帧却是 > 10(493),其余均为 > 3,疑为历史调试遗留。
  8. 分支条件可疑(410):is_bwinv_firm_download_finished() && get_board_type() != BOARD_EMS3_6_4_E 决定跳过 CPU2。该条件把"下载完成"与"板型"耦合,is_bwinv_firm_download_finished() 在升级途中通常恒为真,效果等价于"除 EMS3_6_4_E 外都不升级 CPU2",与注释"升级从机"语义不符,建议确认。
  9. 错误原因只在计数超限时才落盘:u32_err_reason 仅在 s_u8_send_err_cnt/s_u8_rev_err_cnt > 3 时写入(288、302 等),前三次失败完全不留痕,现场只能看到最后一次原因,且不同阶段的错误码会互相覆盖同一字段。
  10. ALPHA_UPGRADE_FAULT 是自旋退出而非真正中止 DSP 侧升级:该分支不发送任何取消帧,只清理本地状态,此时 DSP 可能仍停在等待数据的中间态,需要依赖下一轮 ALPHA_UPGRADE_START 重新握手恢复。
  11. 头文件中的 EMS_INV_* / INV_EMS_ACK 宏与本文件无关:这些是 kelong 的 CAN 升级命令字(midware/inverter/kelong/kelong_upgrade.c:133 起),拷贝到本头文件后从未使用,属版本遗留。
  12. u8_upgrade_object 用 u8 存枚举:ALPHAESS_UPGRADE_OBJECT_E 值 0/1/2,赋值与比较均依赖隐式转换,可读性差但无功能问题。
  13. 未使用的 include:hex_file_parse.h、ems.h、sdram.h(地址宏经 server.h 间接引入)在本文件中未见直接符号引用,属复制粘贴遗留。