EMS 知识库
首页 / knowledge/代码解析/net

ems_boot_handle.c

源文件路径:net/ems_boot_handle.c 所属模块:net(网络应用层 — EMS 固件升级) 代码行数:240

1. 文件职责

本文件负责 GD32F407 片内 Flash 起始处 EMS Bootloader(0x08000000 起 11552 字节底层程序)的版本识别、旧版自动打补丁升级,以及"是否允许进入 BOOT 升级"的准入判定。

它处在 EMS 整机升级数据流的最末端:云端/Modbus 下发固件 → 写入外挂 SPI Flash → calculate_ems_flash_md5() 校验通过后,由 net/ems_download.c:172 调用本文件的 ems_upgrade_boot_check() 决定能否落盘重启。

get_ems_boot_ver() 从片内 Flash 绝对地址 0x08000000 + 0x22B4 读取 8 字节版本串,被云端上报(net/ems.c:2084、net/ems.c:2458)和 Modbus 寄存器读取(midware/interface/modbus/modbus.c:4254、:4261、:4268)共用,是 EMS 对外暴露"底层版本"的唯一来源。

当识别到 Boot 为 V1.00.03 时,本文件把整片 Boot 镜像复制到 SDRAM,按固定偏移打补丁(版本号字符 + 两处跳转字节),再用 MD5 双向校验(旧版校验 + 新版校验)后回写片内 Flash,实现 V1.00.03 → V1.00.04 的静默升级。

整个回写过程在 OSSchedLock() 保护下执行,完成后由上层复位系统从新 Boot 启动。

2. 关键数据结构与宏

名称类型说明
MCU_START_ADDRESS宏0x08000000,片内 Flash / Bootloader 起始地址(ems_boot_handle.c:24)
MCU_BOOT_FLASH_LEN宏11552,参与 MD5 计算与回写的 Boot 字节数(:25)
BOOT_VER_ADDRESS_OFFSET宏0x22B4,Boot 版本字符串在 Boot 镜像内的偏移(:26)
s_u8_bootloader_ver全局静态数组 char[9]get_ems_boot_ver() 的返回缓冲,8 字符版本 + '\0'(:31)
s_u8_boot_buf全局静态数组 u8[SDRAM_16K],__attribute__((at(SDRAM_BOOT_FILE_ADDR)))Boot 镜像暂存缓冲,定位在 SDRAM(:32)
s_u8_old_boot_md5全局静态常量数组 const u8[16]Boot V1.00.03 全片 MD5 期望值(:33)
s_u8_new_boot_md5全局静态常量数组 const u8[16]打补丁后 Boot V1.00.04 全片 MD5 期望值(:34)
SDRAM_16K宏(外部 hardware/sdram/sdram.h:30)16 * 1024,SDRAM 分区单位
SDRAM_BOOT_FILE_ADDR宏(外部 hardware/sdram/sdram.h:43)SDRAM_FAULT_LOG_START_ADDR + SDRAM_20K,本文件专用 16K 区
FMC_READY / FMC_FLAG_*枚举/宏(外部 gd32f4xx_fmc.h)片内 Flash 操作返回码与标志位
CTL_SECTOR_NUMBER_0 / CTL_SECTOR_NUMBER_1宏(外部 gd32f4xx_fmc.h:257-258)片内 Flash 扇区 0 / 1(各 16KB,合计 32KB = APP 起始偏移 0x08000)

本文件未定义结构体、枚举、联合体和位域。

3. 函数清单

函数名作用域说明
on_chip_flash_write(u32 WriteAddr, u8 *pBuffer, u32 NumToWrite)static逐字节调用 fmc_byte_program 写片内 Flash,单次序列失败则从起点整段重试,最多 3 次(:49)
ems_boot_handle(void)staticBoot 补丁主流程:读片内 Boot → 打补丁 → MD5 校验 → 擦扇区回写 → 再校验(:86)
get_ems_boot_ver(void)全局清空静态缓冲后从 0x080022B4 读 8 字节,返回 Boot 版本字符串指针(:196)
ems_upgrade_boot_check(u32 file_len)全局依据当前 Boot 版本与待升级文件长度,判断是否允许进入 BOOT 升级(:215)

另外在文件头有两条前置声明与定义重复:static u8 ems_boot_handle(void);(:39)和 static u8 on_chip_flash_write(u32, u8 *, u32);(:40)。

4. 核心逻辑解析

4.1 Boot 版本读取 get_ems_boot_ver()(:196-206)

memset(s_u8_bootloader_ver, 0x00, 9);
for i in 0..7:  s_u8_bootloader_ver[i] = *(volatile uint8_t*)(0x08000000 + 0x22B4 + i);
return s_u8_bootloader_ver;

4.2 Boot 补丁主流程 ems_boot_handle()(:86-187)

流程分为"校验-打补丁-再校验"与"擦写回写-再校验"两段,各自带一个 5 次重试循环。

第一段:读取与打补丁(:92-136)

for (k = 0; k < 5; k++)
    1) memset(s_u8_boot_buf, 0xFF, SDRAM_16K)                      // 全片填 0xFF
    2) 逐字节读 0x08000000 .. +11551 到 s_u8_boot_buf[0..11551]     // 11552 次 volatile 读
    3) MD5Init / MD5Update(11552) / MD5Final
    4) if (memcmp(md5.digest, s_u8_old_boot_md5, 16) != 0) continue;  // 必须等于 V1.00.03 的 MD5
    5) 按偏移打补丁(见下表)
    6) 重新算 MD5,若 != s_u8_new_boot_md5 则 continue
    7) l_u8_res = TRUE; break;
IWDG_Feed();
if (!l_u8_res) return FALSE;

补丁偏移表(:111-123):

偏移写入值说明
0x1FF20xF0Boot 内部字节,无注释
0x20E20xF0Boot 内部字节,无注释
0x22BB'4'版本串 "V1.00.03" → "V1.00.04"
0x22CA'2'汇编/常量区字节,无注释
0x22CB'2'同上
0x22CD'1'同上
0x22CE'1'同上
0x22D0'0'同上
0x22D1'4'同上

注意 0x22CA..0x22D1 只写了 6 个位置中的部分下标(跳过了 0x22CC、0x22CF),说明这些写入的是字符数组中的某一串数字的局部改值,具体语义在源码中没有注释说明。

第二段:擦写回写(:145-186)

l_u8_res = FALSE;
OSSchedLock();                                    // :147  锁调度器
fmc_unlock();                                     // :148
for (k = 0; k < 5; k++) {
    fmc_flag_clear(FMC_FLAG_RDDERR|PGSERR|PGMERR|WPERR|OPERR|END);   // :152
    if (fmc_sector_erase(CTL_SECTOR_NUMBER_0) != FMC_READY) continue;  // :154  擦 16KB
    if (fmc_sector_erase(CTL_SECTOR_NUMBER_1) != FMC_READY) continue;  // :159  擦 16KB
    if (on_chip_flash_write(MCU_START_ADDRESS, s_u8_boot_buf, 11552) == FALSE) continue;  // :164
    MD5Init/Update((char *)MCU_START_ADDRESS, 11552)/Final                                  // :169-171
    if (memcmp(md5.digest, s_u8_new_boot_md5, 16) != 0) continue;                            // :173
    l_u8_res = TRUE; break;
}
fmc_lock();                                        // :182
OSSchedUnlock();                                   // :183
IWDG_Feed();                                       // :184
return l_u8_res;                                   // :186

4.3 片内 Flash 逐字节写 on_chip_flash_write()(:49-77)

for (j = 0; j < 3; j++) {
    l_u8_res = TRUE;
    for (i = 0; i < NumToWrite; i++)
        if (fmc_byte_program(WriteAddr + i, pBuffer[i]) != FMC_READY) { l_u8_res = FALSE; break; }
    if (l_u8_res == FALSE) continue; else break;
}
return l_u8_res;

4.4 升级准入判定 ems_upgrade_boot_check()(:215-239)

if (memcmp(get_ems_boot_ver(), "V1.00.03", 8) == 0) {
    ems_boot_handle();                          // 先尝试把旧 Boot 补成 V1.00.04
    if (memcmp(get_ems_boot_ver(), "V1.00.03", 8) == 0 && get_length_file_updating(UPDT_EMS) >= 352*1024)
        return FALSE;                           // 补丁失败且待升级文件 ≥ 352KB → 拒绝升级
    else
        return TRUE;
}
else if (memcmp(get_ems_boot_ver(), "V1.00.04", 8) == 0) return TRUE;
else return FALSE;                              // 其它任何版本一律拒绝

这是本文件唯一的对外状态机判定,三分支语义如下:

当前 Boot 版本补丁结果待升级文件长度返回
V1.00.03成功升到 V1.00.04任意TRUE
V1.00.03仍为 V1.00.03< 352KBTRUE(旧 Boot 只能写 352K,小于此值可继续)
V1.00.03仍为 V1.00.03>= 352KBFALSE
V1.00.04—任意TRUE
其它—任意FALSE

4.5 定时/调度与 uCOS-II 配合

4.6 关键全局变量读写

变量读/写触发条件
s_u8_boot_buf写(源自片内 Flash 读取)、读(回写 Flash)每次 ems_boot_handle() 调用
s_u8_bootloader_ver写每次 get_ems_boot_ver() 调用,会先 memset 清零
get_length_file_updating(UPDT_EMS)(外部 g_stEMSInfo.stUpt[UPDT_EMS].u32LenFile)只读ems_upgrade_boot_check() 的长度判定

5. 对外接口

本文件导出(声明于 net/ems_boot_handle.h:14-15)

符号使用者
char *get_ems_boot_ver(void)net/ems.c:2084(拼 "EMSBootVer" 上报字段)、net/ems.c:2458(System restart 消息里带 bootver)、midware/interface/modbus/modbus.c:4254/4261/4268(REG_EMS_BOOT_VERSION_HIGH/MIDDLE/LOW_E 三个寄存器,用 sscanf 拆 V%d.%d.%d)
u8 ems_upgrade_boot_check(u32 file_len)net/ems_download.c:172

本文件内部静态符号

on_chip_flash_write(:49)、ems_boot_handle(:86)、s_u8_boot_buf、s_u8_bootloader_ver、s_u8_old_boot_md5、s_u8_new_boot_md5,均不导出。

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

外部符号来源模块
fmc_unlock / fmc_lock / fmc_sector_erase / fmc_byte_program / fmc_flag_clear、FMC_READY、FMC_FLAG_*、CTL_SECTOR_NUMBER_0/1HALLIB/GD32F4xx_standard_peripheral/Include/gd32f4xx_fmc.h
MD5Init / MD5Update / MD5Final / MD5_CTXcommon/md5.h
IWDG_Feedhardware/iwdg/iwdg.h:6
OSSchedLock / OSSchedUnlockuCOS-II
get_length_file_updatingnet/upd_download.c:327
SDRAM_16K / SDRAM_BOOT_FILE_ADDRhardware/sdram/sdram.h
memset / memcmp / strlen / size_t标准库(经 includes.h)

6. 依赖关系

依赖(本文件 include)

ems_boot_handle.h、upd_download.h、ems_download.h、sdram.h、iwdg.h、board_init.h、ems.h、server.h、md5.h、gd32f4xx_fmc.h(:13-22)。

被依赖(会用到本文件)

7. 备注与风险点