@@ -1,446 +1,460 @@
# include "Update.h"
# include "DebugCmd.h"
# include "main.h"
# include "bsp.h"
# include "spiflash.h"
# include <rthw.h> /*rt_hw_interrupt_disable/enable: TX queue critical section*/
extern GateWayPara_t GateWay ;
static void NetUpgradeSavePkg ( uint32_t PkgCnt ) ; /*fwd decl: Update() calls it before its definition*/
# include "Update.h"
# include "DebugCmd.h"
# include "main.h"
# include "bsp.h"
# include "spiflash.h"
# include <rthw.h> /*rt_hw_interrupt_disable/enable: TX queue critical section*/
extern GateWayPara_t GateWay ;
static void NetUpgradeSavePkg ( uint32_t PkgCnt ) ; /*fwd decl: Update() calls it before its definition*/
/*****************************************************************************************
* 函 数 名 称 : Update
* 功 能 描 述 : 固 件 升 级 , 此 函 数 在 解 析 到 升 级 指 令 时 调 用
* 参 数 : PayLoad 协 议 负 载 , 需 去 除 帧 头 等 信 息 , 只 传 负 载 数 据
* 返 回 值 : 无
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void Update ( uint8_t * PayLoad )
{
UpDataRequset_t Req ;
boot_para_t cfg = { 0 } ;
memcpy ( & Req , PayLoad , sizeof ( UpDataRequset_t ) ) ;
/*固件大小校验: 为0或超出B区容量时拒绝升级, 不置标志不复位*/
if ( Req . AppSize = = 0 | | Req . AppSize > FLASH_APPB_SIZE ) {
U_DBG_LOG ( " AppSize invalid: %d (max %d) \r \n " , ( int ) Req . AppSize , ( int ) FLASH_APPB_SIZE ) ;
return ;
}
/*读回现有boot参数,保留FirstRunFlag等字段*/
dev_boot_read_param ( & cfg ) ;
cfg . AppSize = Req . AppSize ;
cfg . Crc32Check = Req . AppCrc32 ;
cfg . PackageCnt = Req . PackageNum ;
cfg . AppFlag = 0 ;
cfg . UpdateFlag = APP_UPDATE_FLAG ;
memcpy ( cfg . GwMac , GateWay . ConfigPara . GwMac , 6 ) ;
U_DBG_LOG ( " Receive upgrade command: \r \n " ) ;
U_DBG_LOG ( " PackageNum: %d \r \n " , cfg . PackageCnt ) ;
U_DBG_LOG ( " AppSize: %d \r \n " , cfg . AppSize ) ;
U_DBG_LOG ( " AppCrc32: 0x%08x \r \n " , cfg . Crc32Check ) ;
U_DBG_LOG ( " System Reset! \r \n " ) ;
NetUpgradeSavePkg ( 0 ) ;
SAVE_BOOT_PARA ( & cfg ) ;
U_DELAY_MS ( 200 ) ;
U_SYSTEM_TESET ( ) ;
}
/*****************************************************************************************
* 函 数 名 称 : UpdateInit
* 功 能 描 述 : 升 级 参 数 初 始 化 , 需 要 在 程 序 启 动 , 外 设 初 始 化 完 后 调 用
* 参 数 : 无
* 返 回 值 : 无
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void UpdateInit ( void )
{
boot_para_t cfg ;
READ_BOOT_PARA ( & cfg ) ;
/*参照hc32l170_app: 仅确保AppFlag=APP_START_FLAG, 使Boot下次直接跳转App
( 网 关 MAC / 服 务 器 / Lora配置从BootPara_t获取 , 由 Boot配置 ) */
if ( cfg . AppFlag ! = APP_START_FLAG ) {
cfg . AppFlag = APP_START_FLAG ;
SAVE_BOOT_PARA ( & cfg ) ;
}
}
/*****************************************************************************************
* Cat1远程升级模块 ( 0x7D 帧 , 与 Boot RS485升级协议一致 )
* 服 务 器 ( 升 级 主 机 ) - - TCP - - > Cat1模组 - - USART1 - - > 本 模 块 :
* 收 0x81 升 级 请 求 ( 校 验 / 擦 暂 存 区 / 断 点 续 传 判 定 ) → 0x82 分 包 顺 序 写 入 外 部 SPI Flash暂存区
* → 每 收 NUP_WINDOW包回0x02请求帧 ( 服 务 端 窗 口 流 控 ) → 收 完 CRC32校验通过后置
* APP_UPDATE_SPI_FLAG复位 , 由 Boot完成 " 外部Flash→B区→A区 " 搬 运
* 协 议 帧 : 0x7D + DevMac ( 6 ) + Cmd + Len + Payload + CRC16 ( Modbus ) , 命 令 0x81 / 0x82 / 0x01 / 0x02
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define NUP_PACKET_DATA 200 //每包数据长度(与服务端/Boot一致)
# define NUP_STASH_PROG_MAGIC 0xA5A5C35A //升级进度记录魔数
# define NUP_WINDOW 8 //每收W包回一次请求帧
# define NUP_TIMEOUT_MS 3000 //等包超时(Cat1延迟大)
# define NUP_TIMEOUT_MAX 5 //连续超时次数上限
# define NUP_IDLE_TIMEOUT_MS 1800000UL //整体无进展超时(30分钟)
# define NUP_FRAME_BUF_MAX 3072 //0x7D组帧缓冲上限(容纳URC单次喂入的多帧拼接)
# define NUP_APP_MAX (232 * 1024) //A区容量上限
/*升级进度记录(存外部Flash扇区4037): 断电/断连后据此判定断点续传*/
typedef struct {
uint32_t Magic ; //NUP_STASH_PROG_MAGIC
uint32_t AppSize ; //固件大小
uint32_t Crc32 ; //固件CRC32
uint32_t PkgCnt ; //已连续收到的包数
} FwStashProg_t ;
static struct {
bool Active ; //升级会话进行中
uint16_t PkgCnt ; //总包数
uint32_t AppSize ; //固件大小
uint32_t Crc32 ; //固件CRC32
uint16_t ExpectIdx ; //期望接收的包序号
uint16_t LastAckIdx ; //最近一次0x02应答的包序号(窗口流控基准)
uint32_t LastTick ; //最近收包时刻(rt_tick, 1ms)
uint32_t ErrCnt ; //连续超时次数
uint8_t TxQ [ 4 ] [ 40 ] ; //应答帧队列(0x01/0x02连发互不挤占)
uint8_t TxQLen [ 4 ] ;
uint8_t TxHead ;
uint8_t TxChan [ 4 ] ; /*每帧来源通道: ACK从收到0x81的通道原路返回*/
uint8_t CurCh ; /*当前会话通道: Feed进入时记录*/ //队首索引(出队)
uint8_t TxCnt ; //队列帧数
} Nup ;
static uint8_t NupFrameBuf [ NUP_FRAME_BUF_MAX ] ; //0x7D组帧缓冲(TCP分片跨URC拼接)
static uint16_t NupFrameLen = 0 ;
static uint64_t NupErasedMap = 0 ; /*bit i: stash sector i erased this session (NOR writes only 1->0, erase-before-write required)*/
/*软件CRC32: 与Boot GetCrc32/服务端zlib.crc32同算法(0xEDB88320反射, 初末值0xFFFFFFFF)*/
static uint32_t NetUpgradeCrc32Stash ( uint32_t Len )
{
uint32_t Crc = 0xFFFFFFFF ;
uint32_t Done = 0 ;
uint8_t Buf [ 256 ] ;
while ( Done < Len ) {
uint16_t n = ( Len - Done > sizeof ( Buf ) ) ? sizeof ( Buf ) : ( uint16_t ) ( Len - Done ) ;
FwStashRead ( Buf , Done , n ) ;
for ( uint16_t i = 0 ; i < n ; i + + ) {
Crc ^ = Buf [ i ] ;
for ( int b = 0 ; b < 8 ; b + + ) {
Crc = ( Crc & 1 ) ? ( Crc > > 1 ) ^ 0xEDB88320 : ( Crc > > 1 ) ;
}
}
Done + = n ;
}
return ~ Crc ;
}
/*进度记录持久化(扇区4037): PkgCnt=0表示清除*/
static void NetUpgradeSavePkg ( uint32_t PkgCnt )
{
FwStashProg_t Prog ;
Prog . Magic = ( PkgCnt > 0 ) ? NUP_STASH_PROG_MAGIC : 0 ;
Prog . AppSize = Nup . AppSize ;
Prog . Crc32 = Nup . Crc32 ;
Prog . PkgCnt = PkgCnt ;
FwStashProgWrite ( ( uint8_t * ) & Prog , sizeof ( Prog ) ) ;
}
/*待发帧入队(0x7D原始帧, 由CatOne主循环经AT+LIPSEND直发, 绕过0x7A打包)*/
static void NetUpgradePushTx ( uint8_t Cmd , uint8_t * Payload , uint8_t Len )
{
uint16_t crc ;
rt_base_t level = rt_hw_interrupt_disable ( ) ;
if ( Nup . TxCnt > = 4 ) {
rt_hw_interrupt_enable ( level ) ;
return ; /*queue full: drop*/
}
{
uint8_t Tail = ( Nup . TxHead + Nup . TxCnt ) % 4 ;
Nup . TxQ [ Tail ] [ 0 ] = 0x7D ;
memcpy ( & Nup . TxQ [ Tail ] [ 1 ] , GateWay . ConfigPara . GwMac , 6 ) ;
Nup . TxQ [ Tail ] [ 7 ] = Cmd ;
Nup . TxQ [ Tail ] [ 8 ] = Len ;
memcpy ( & Nup . TxQ [ Tail ] [ 9 ] , Payload , Len ) ;
crc = CRC_Modbus ( CRC16_BASE , Nup . TxQ [ Tail ] , 9 + Len ) ;
Nup . TxQ [ Tail ] [ 9 + Len ] = crc & 0xFF ;
Nup . TxQ [ Tail ] [ 10 + Len ] = ( crc > > 8 ) & 0xFF ;
Nup . TxQLen [ Tail ] = 11 + Len ;
Nup . TxChan [ Tail ] = Nup . CurCh ; /*绑定来源通道*/
Nup . TxCnt + + ;
}
rt_hw_interrupt_enable ( level ) ;
U_DBG_LOG ( " NetUpgrade push: cmd=0x%02x cnt=%d \r \n " , Cmd , Nup . TxCnt ) ;
}
static void NetUpgradeSendAck ( uint8_t Status , uint16_t Resumed )
{
uint8_t p [ 3 ] ;
p [ 0 ] = Status ;
p [ 1 ] = Resumed & 0xFF ;
p [ 2 ] = ( Resumed > > 8 ) & 0xFF ;
NetUpgradePushTx ( 0x01 , p , 3 ) ; //0x20全新/0x21续传/0x32拒绝
}
static void NetUpgradeSendReq ( uint16_t Idx )
{
uint8_t p [ 4 ] ;
p [ 0 ] = Idx & 0xFF ;
p [ 1 ] = ( Idx > > 8 ) & 0xFF ;
p [ 2 ] = Nup . PkgCnt & 0xFF ;
p [ 3 ] = ( Nup . PkgCnt > > 8 ) & 0xFF ;
NetUpgradePushTx ( 0x02 , p , 4 ) ; //请求从Idx起的下一批(收满PkgCnt视为完成)
}
/*0x81升级请求: 合法性检查/回ACK并启动下载*/
static void NetUpgradeOnReq ( uint8_t * Payload , uint8_t Len )
{
UpDataRequset_t Req ;
if ( Len < sizeof ( Req ) ) {
return ;
}
memcpy ( & Req , Payload , sizeof ( Req ) ) ;
if ( Req . AppSize = = 0 | | Req . AppSize > NUP_APP_MAX ) {
NetUpgradeSendAck ( 0x32 , 0 ) ;
U_DBG_LOG ( " NetUpgrade reject: AppSize=%d \r \n " , Req . AppSize ) ;
return ;
}
/*断点续传已取消: 每次点击升级均从0%全新下载*/
NetUpgradeSavePkg ( 0 ) ;
NupErasedMap = 0 ;
Nup . Active = true ;
Nup . PkgCnt = Req . PackageNum ;
Nup . AppSize = Req . AppSize ;
Nup . Crc32 = Req . AppCrc32 ;
Nup . ExpectIdx = 0 ;
Nup . ErrCnt = 0 ;
Nup . LastTick = rt_tick_get ( ) ;
Nup . LastAckIdx = 0 ;
U_DBG_LOG ( " NetUpgrade req: Pkg=%d Size=%d Crc=0x%08x Resumed=0 \r \n " ,
Nup . PkgCnt , Nup . AppSize , Nup . Crc32 ) ;
NetUpgradeSendAck ( 0x20 , 0 ) ;
NetUpgradeSendReq ( 0 ) ; //请求首包, 服务端收到0x02才开始发包
}
/*0x82升级数据: 严格按期望包号顺序写暂存区, 收满窗口回请求帧, 收完CRC校验*/
static void NetUpgradeOnData ( uint8_t * Payload , uint8_t Len )
{
uint16_t Idx , Total ;
uint8_t DataLen ;
uint32_t WriteLen ;
if ( ! Nup . Active | | Len < 5 ) {
return ;
}
Idx = Payload [ 0 ] | ( Payload [ 1 ] < < 8 ) ;
Total = Payload [ 2 ] | ( Payload [ 3 ] < < 8 ) ;
DataLen = Payload [ 4 ] ;
if ( Total ! = Nup . PkgCnt ) {
return ; //会话不匹配
}
if ( Idx ! = Nup . ExpectIdx ) {
return ; //重复/乱序包: 丢弃(服务端按期望包号重发)
}
if ( Idx = = Nup . PkgCnt - 1 ) {
WriteLen = Nup . AppSize - ( uint32_t ) Idx * NUP_PACKET_DATA ; //尾包实长
if ( DataLen < WriteLen ) {
return ;
}
}
else {
if ( DataLen < NUP_PACKET_DATA ) {
return ;
}
WriteLen = NUP_PACKET_DATA ;
}
{
/*NOR only writes 1->0: erase touched stash sectors before write (one erase per sector per session), fixes stale-bit corruption on 2nd upgrade*/
uint32_t Sec0 = ( uint32_t ) Idx * NUP_PACKET_DATA / SPIFLASH_SECTORSIZE ;
uint32_t Sec1 = ( ( uint32_t ) Idx * NUP_PACKET_DATA + WriteLen - 1 ) / SPIFLASH_SECTORSIZE ;
for ( uint32_t s = Sec0 ; s < = Sec1 ; s + + ) {
if ( ( NupErasedMap & ( ( uint64_t ) 1 < < s ) ) = = 0 ) {
FwStashEraseSector ( s ) ;
NupErasedMap | = ( uint64_t ) 1 < < s ;
}
}
}
FwStashWrite ( & Payload [ 5 ] , ( uint32_t ) Idx * NUP_PACKET_DATA , WriteLen ) ;
Nup . ExpectIdx + + ;
Nup . LastTick = rt_tick_get ( ) ;
Nup . ErrCnt = 0 ;
if ( Nup . ExpectIdx > = Nup . PkgCnt ) {
if ( NetUpgradeCrc32Stash ( Nup . AppSize ) = = Nup . Crc32 ) {
U_DBG_LOG ( " NetUpgrade stash verified, reboot to bootloader \r \n " ) ;
UpdateFromStash ( Nup . AppSize , Nup . Crc32 ) ; //置APP_UPDATE_SPI_FLAG并复位
}
U_DBG_LOG ( " NetUpgrade stash CRC failed, restart download \r \n " ) ;
//校验失败: 从0重下(写前按需擦除保证暂存区干净)
NetUpgradeSavePkg ( 0 ) ;
NupErasedMap = 0 ;
Nup . ExpectIdx = 0 ;
NetUpgradeSendReq ( 0 ) ;
return ;
}
if ( Nup . ExpectIdx - Nup . LastAckIdx > = NUP_WINDOW ) {
NetUpgradeSendReq ( Nup . ExpectIdx ) ; //收满窗口, 请求下一批(与断点起点无关)
Nup . LastAckIdx = Nup . ExpectIdx ;
}
if ( Nup . ExpectIdx % ( NUP_WINDOW * 4 ) = = 0 ) {
NetUpgradeSavePkg ( Nup . ExpectIdx ) ; //定期持久化进度(32包一次)
}
}
static void NetUpgradeFrameHandler ( uint8_t Cmd , uint8_t * Payload , uint8_t Len )
{
U_DBG_LOG ( " NetUpgrade frame: cmd=0x%02x len=%d \r \n " , Cmd , Len ) ;
switch ( Cmd ) {
case 0x81 :
NetUpgradeOnReq ( Payload , Len ) ;
break ;
case 0x82 :
NetUpgradeOnData ( Payload , Len ) ;
break ;
default :
break ;
}
}
/*****************************************************************************************
* 函 数 名 称 : NetUpgradeFeed
* 功 能 描 述 : 喂 入 Cat1下行数据片段 ( + LIPURC二进制 ) , 流 式 拼 接 提 取 完 整 0x7D 升 级 帧
* 参 数 : data / len , 二 进 制 数 据 片 段 ( TCP分片有序 )
* 返 回 值 : true = 数 据 已 被 升 级 模 块 消 费 ( 帧 头 0x7D ) ; false = 非 0x7D 流 , 调 用 方 交 还 原 0x7A 路 径
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool NetUpgradeFeed ( uint8_t * data , uint16_t len , uint8_t ch )
{
Nup . CurCh = ch ; /*记录会话通道*/
if ( NupFrameLen = = 0 ) {
uint16_t skip = 0 ;
while ( skip < len & & data [ skip ] ! = 0x7D ) {
skip + + ;
}
if ( skip > = len ) {
return false ;
}
data + = skip ;
len - = skip ;
}
if ( NupFrameLen + len > NUP_FRAME_BUF_MAX ) {
NupFrameLen = 0 ; //溢出保护, 重新同步
return true ;
}
memcpy ( & NupFrameBuf [ NupFrameLen ] , data , len ) ;
NupFrameLen + = len ;
while ( NupFrameLen > = 11 ) {
if ( NupFrameBuf [ 0 ] ! = 0x7D ) {
uint16_t i ;
for ( i = 1 ; i < NupFrameLen ; i + + ) {
if ( NupFrameBuf [ i ] = = 0x7D ) break ;
}
if ( i > = NupFrameLen ) {
NupFrameLen = 0 ;
break ;
}
memmove ( NupFrameBuf , & NupFrameBuf [ i ] , NupFrameLen - i ) ;
NupFrameLen - = i ;
continue ;
}
uint16_t total = 11 + NupFrameBuf [ 8 ] ;
if ( NupFrameLen < total ) {
break ; //帧未收齐, 等下一分片
}
uint16_t calc = CRC_Modbus ( CRC16_BASE , NupFrameBuf , total - 2 ) ;
uint16_t recv = NupFrameBuf [ total - 2 ] | ( NupFrameBuf [ total - 1 ] < < 8 ) ;
if ( calc = = recv ) {
/*MAC filter: only the device whose own GwMac matches the 0x7D frame header handles the frame,
broadcast 0x81 is answered by the target device only , other devices drop it silently */
if ( memcmp ( & NupFrameBuf [ 1 ] , GateWay . ConfigPara . GwMac , 6 ) = = 0 ) {
NetUpgradeFrameHandler ( NupFrameBuf [ 7 ] , & NupFrameBuf [ 9 ] , NupFrameBuf [ 8 ] ) ;
}
else {
U_DBG_LOG ( " NetUpgrade MAC mismatch, check upgrade dialog MAC \r \n " ) ;
}
memmove ( NupFrameBuf , & NupFrameBuf [ total ] , NupFrameLen - total ) ;
NupFrameLen - = total ;
}
else {
memmove ( NupFrameBuf , & NupFrameBuf [ 1 ] , NupFrameLen - 1 ) ;
NupFrameLen - = 1 ;
}
}
return true ;
}
/*****************************************************************************************
* 函 数 名 称 : Update
* 功 能 描 述 : 固 件 升 级 , 此 函 数 在 解 析 到 升 级 指 令 时 调 用
* 参 数 : PayLoad 协 议 负 载 , 需 去 除 帧 头 等 信 息 , 只 传 负 载 数 据
* 返 回 值 : 无
* 函 数 名 : NetUpgradePopTx
* 功 能 : 取 出 本 通 道 应 答 帧 ( 0x01 ACK / 0x02 请 求 ) , 供 CatOne主循环 / EthTask经AT + LIPSEND直发
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void Update ( uint8_t * PayLoad )
{
UpDataRequset_t Req ;
boot_para_t cfg = { 0 } ;
memcpy ( & Req , PayLoad , sizeof ( UpDataRequset_t ) ) ;
/*固件大小校验: 为0或超出B区容量时拒绝升级, 不置标志不复位*/
if ( Req . AppSize = = 0 | | Req . AppSize > FLASH_APPB_SIZE ) {
U_DBG_LOG ( " AppSize invalid: %d (max %d) \r \n " , ( int ) Req . AppSize , ( int ) FLASH_APPB_SIZE ) ;
return ;
}
/*读回现有boot参数,保留FirstRunFlag等字段*/
dev_boot_read_param ( & cfg ) ;
cfg . AppSize = Req . AppSize ;
cfg . Crc32Check = Req . AppCrc32 ;
cfg . PackageCnt = Req . PackageNum ;
cfg . AppFlag = 0 ;
cfg . UpdateFlag = APP_UPDATE_FLAG ;
memcpy ( cfg . GwMac , GateWay . ConfigPara . GwMac , 6 ) ;
U_DBG_LOG ( " Receive upgrade command: \r \n " ) ;
U_DBG_LOG ( " PackageNum: %d \r \n " , cfg . PackageCnt ) ;
U_DBG_LOG ( " AppSize: %d \r \n " , cfg . AppSize ) ;
U_DBG_LOG ( " AppCrc32: 0x%08x \r \n " , cfg . Crc32Check ) ;
U_DBG_LOG ( " System Reset! \r \n " ) ;
NetUpgradeSavePkg ( 0 ) ;
SAVE_BOOT_PARA ( & cfg ) ;
U_DELAY_MS ( 200 ) ;
U_SYSTEM_TESET ( ) ;
}
/*****************************************************************************************
* 函 数 名 称 : UpdateInit
* 功 能 描 述 : 升 级 参 数 初 始 化 , 需 要 在 程 序 启 动 , 外 设 初 始 化 完 后 调 用
* 参 数 : 无
* 返 回 值 : 无
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void UpdateInit ( void )
{
boot_para_t cfg ;
READ_BOOT_PARA ( & cfg ) ;
/*参照hc32l170_app: 仅确保AppFlag=APP_START_FLAG, 使Boot下次直接跳转App
( 网 关 MAC / 服 务 器 / Lora配置从BootPara_t获取 , 由 Boot配置 ) */
if ( cfg . AppFlag ! = APP_START_FLAG ) {
cfg . AppFlag = APP_START_FLAG ;
SAVE_BOOT_PARA ( & cfg ) ;
}
}
/*****************************************************************************************
* Cat1远程升级模块 ( 0x7D 帧 , 与 Boot RS485升级协议一致 )
* 服 务 器 ( 升 级 主 机 ) - - TCP - - > Cat1模组 - - USART1 - - > 本 模 块 :
* 收 0x81 升 级 请 求 ( 校 验 / 擦 暂 存 区 / 断 点 续 传 判 定 ) → 0x82 分 包 顺 序 写 入 外 部 SPI Flash暂存区
* → 每 收 NUP_WINDOW包回0x02请求帧 ( 服 务 端 窗 口 流 控 ) → 收 完 CRC32校验通过后置
* APP_UPDATE_SPI_FLAG复位 , 由 Boot完成 " 外部Flash→B区→A区 " 搬 运
* 协 议 帧 : 0x7D + DevMac ( 6 ) + Cmd + Len + Payload + CRC16 ( Modbus ) , 命 令 0x81 / 0x82 / 0x01 / 0x02
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define NUP_PACKET_DATA 200 //每包数据长度(与服务端/Boot一致)
# define NUP_STASH_PROG_MAGIC 0xA5A5C35A //升级进度记录魔数
# define NUP_WINDOW 8 //每收W包回一次请求帧
# define NUP_TIMEOUT_MS 3000 //等包超时(Cat1延迟大)
# define NUP_TIMEOUT_MAX 5 //连续超时次数上限
# define NUP_IDLE_TIMEOUT_MS 1800000UL //整体无进展超时(30分钟)
# define NUP_FRAME_BUF_MAX 3072 //0x7D组帧缓冲上限(容纳URC单次喂入的多帧拼接)
# define NUP_APP_MAX (232 * 1024) //A区容量上限
/*升级进度记录(存外部Flash扇区4037): 断电/断连后据此判定断点续传*/
typedef struct {
uint32_t Magic ; //NUP_STASH_PROG_MAGIC
uint32_t AppSize ; //固件大小
uint32_t Crc32 ; //固件CRC32
uint32_t PkgCnt ; //已连续收到的包数
} FwStashProg_t ;
static struct {
bool Active ; //升级会话进行中
uint16_t PkgCnt ; //总包数
uint32_t AppSize ; //固件大小
uint32_t Crc32 ; //固件CRC32
uint16_t ExpectIdx ; //期望接收的包序号
uint16_t LastAckIdx ; //最近一次0x02应答的包序号(窗口流控基准)
uint32_t LastTick ; //最近收包时刻(rt_tick, 1ms)
uint32_t ErrCnt ; //连续超时次数
uint8_t TxQ [ 4 ] [ 40 ] ; //应答帧队列(0x01/0x02连发互不挤占)
uint8_t TxQLen [ 4 ] ;
uint8_t TxHead ; //队首索引(出队)
uint8_t TxCnt ; //队列帧数
} Nup ;
static uint8_t NupFrameBuf [ NUP_FRAME_BUF_MAX ] ; //0x7D组帧缓冲(TCP分片跨URC拼接)
static uint16_t NupFrameLen = 0 ;
static uint64_t NupErasedMap = 0 ; /*bit i: stash sector i erased this session (NOR writes only 1->0, erase-before-write required)*/
/*软件CRC32: 与Boot GetCrc32/服务端zlib.crc32同算法(0xEDB88320反射, 初末值0xFFFFFFFF)*/
static uint32_t NetUpgradeCrc32Stash ( uint32_t Len )
{
uint32_t Crc = 0xFFFFFFFF ;
uint32_t Done = 0 ;
uint8_t Buf [ 256 ] ;
while ( Done < Len ) {
uint16_t n = ( Len - Done > sizeof ( Buf ) ) ? sizeof ( Buf ) : ( uint16_t ) ( Len - Done ) ;
FwStashRead ( Buf , Done , n ) ;
for ( uint16_t i = 0 ; i < n ; i + + ) {
Crc ^ = Buf [ i ] ;
for ( int b = 0 ; b < 8 ; b + + ) {
Crc = ( Crc & 1 ) ? ( Crc > > 1 ) ^ 0xEDB88320 : ( Crc > > 1 ) ;
}
}
Done + = n ;
}
return ~ Crc ;
}
/*进度记录持久化(扇区4037): PkgCnt=0表示清除*/
static void NetUpgradeSavePkg ( uint32_t PkgCnt )
{
FwStashProg_t Prog ;
Prog . Magic = ( PkgCnt > 0 ) ? NUP_STASH_PROG_MAGIC : 0 ;
Prog . AppSize = Nup . AppSize ;
Prog . Crc32 = Nup . Crc32 ;
Prog . PkgCnt = PkgCnt ;
FwStashProgWrite ( ( uint8_t * ) & Prog , sizeof ( Prog ) ) ;
}
/*待发帧入队(0x7D原始帧, 由CatOne主循环经AT+LIPSEND直发, 绕过0x7A打包)*/
static void NetUpgradePushTx ( uint8_t Cmd , uint8_t * Payload , uint8_t Len )
{
uint16_t crc ;
rt_base_t level = rt_hw_interrupt_disable ( ) ;
if ( Nup . TxCnt > = 4 ) {
rt_hw_interrupt_enable ( level ) ;
return ; /*queue full: drop*/
}
{
uint8_t Tail = ( Nup . TxHead + Nup . TxCnt ) % 4 ;
Nup . TxQ [ Tail ] [ 0 ] = 0x7D ;
memcpy ( & Nup . TxQ [ Tail ] [ 1 ] , GateWay . ConfigPara . GwMac , 6 ) ;
Nup . TxQ [ Tail ] [ 7 ] = Cmd ;
Nup . TxQ [ Tail ] [ 8 ] = Len ;
memcpy ( & Nup . TxQ [ Tail ] [ 9 ] , Payload , Len ) ;
crc = CRC_Modbus ( CRC16_BASE , Nup . TxQ [ Tail ] , 9 + Len ) ;
Nup . TxQ [ Tail ] [ 9 + Len ] = crc & 0xFF ;
Nup . TxQ [ Tail ] [ 10 + Len ] = ( crc > > 8 ) & 0xFF ;
Nup . TxQLen [ Tail ] = 11 + Len ;
Nup . TxCnt + + ;
}
rt_hw_interrupt_enable ( level ) ;
U_DBG_LOG ( " NetUpgrade push: cmd=0x%02x cnt=%d \r \n " , Cmd , Nup . TxCnt ) ;
}
static void NetUpgradeSendAck ( uint8_t Status , uint16_t Resumed )
{
uint8_t p [ 3 ] ;
p [ 0 ] = Status ;
p [ 1 ] = Resumed & 0xFF ;
p [ 2 ] = ( Resumed > > 8 ) & 0xFF ;
NetUpgradePushTx ( 0x01 , p , 3 ) ; //0x20全新/0x21续传/0x32拒绝
}
static void NetUpgradeSendReq ( uint16_t Idx )
{
uint8_t p [ 4 ] ;
p [ 0 ] = Idx & 0xFF ;
p [ 1 ] = ( Idx > > 8 ) & 0xFF ;
p [ 2 ] = Nup . PkgCnt & 0xFF ;
p [ 3 ] = ( Nup . PkgCnt > > 8 ) & 0xFF ;
NetUpgradePushTx ( 0x02 , p , 4 ) ; //请求从Idx起的下一批(收满PkgCnt视为完成)
}
/*0x81升级请求: 合法性检查/回ACK并启动下载*/
static void NetUpgradeOnReq ( uint8_t * Payload , uint8_t Len )
{
UpDataRequset_t Req ;
if ( Len < sizeof ( Req ) ) {
return ;
}
memcpy ( & Req , Payload , sizeof ( Req ) ) ;
if ( Req . AppSize = = 0 | | Req . AppSize > NUP_APP_MAX ) {
NetUpgradeSendAck ( 0x32 , 0 ) ;
U_DBG_LOG ( " NetUpgrade reject: AppSize=%d \r \n " , Req . AppSize ) ;
return ;
}
/*断点续传已取消: 每次点击升级均从0%全新下载*/
NetUpgradeSavePkg ( 0 ) ;
NupErasedMap = 0 ;
Nup . Active = true ;
Nup . PkgCnt = Req . PackageNum ;
Nup . AppSize = Req . AppSize ;
Nup . Crc32 = Req . AppCrc32 ;
Nup . ExpectIdx = 0 ;
Nup . ErrCnt = 0 ;
Nup . LastTick = rt_tick_get ( ) ;
Nup . LastAckIdx = 0 ;
U_DBG_LOG ( " NetUpgrade req: Pkg=%d Size=%d Crc=0x%08x Resumed=0 \r \n " ,
Nup . PkgCnt , Nup . AppSize , Nup . Crc32 ) ;
NetUpgradeSendAck ( 0x20 , 0 ) ;
NetUpgradeSendReq ( 0 ) ; //请求首包, 服务端收到0x02才开始发包
}
/*0x82升级数据: 严格按期望包号顺序写暂存区, 收满窗口回请求帧, 收完CRC校验*/
static void NetUpgradeOnData ( uint8_t * Payload , uint8_t Len )
{
uint16_t Idx , Total ;
uint8_t DataLen ;
uint32_t WriteLen ;
if ( ! Nup . Active | | Len < 5 ) {
return ;
}
Idx = Payload [ 0 ] | ( Payload [ 1 ] < < 8 ) ;
Total = Payload [ 2 ] | ( Payload [ 3 ] < < 8 ) ;
DataLen = Payload [ 4 ] ;
if ( Total ! = Nup . PkgCnt ) {
return ; //会话不匹配
}
if ( Idx ! = Nup . ExpectIdx ) {
return ; //重复/乱序包: 丢弃(服务端按期望包号重发)
}
if ( Idx = = Nup . PkgCnt - 1 ) {
WriteLen = Nup . AppSize - ( uint32_t ) Idx * NUP_PACKET_DATA ; //尾包实长
if ( DataLen < WriteLen ) {
return ;
}
}
else {
if ( DataLen < NUP_PACKET_DATA ) {
return ;
}
WriteLen = NUP_PACKET_DATA ;
}
{
/*NOR only writes 1->0: erase touched stash sectors before write (one erase per sector per session), fixes stale-bit corruption on 2nd upgrade*/
uint32_t Sec0 = ( uint32_t ) Idx * NUP_PACKET_DATA / SPIFLASH_SECTORSIZE ;
uint32_t Sec1 = ( ( uint32_t ) Idx * NUP_PACKET_DATA + WriteLen - 1 ) / SPIFLASH_SECTORSIZE ;
for ( uint32_t s = Sec0 ; s < = Sec1 ; s + + ) {
if ( ( NupErasedMap & ( ( uint64_t ) 1 < < s ) ) = = 0 ) {
FwStashEraseSector ( s ) ;
NupErasedMap | = ( uint64_t ) 1 < < s ;
}
}
}
FwStashWrite ( & Payload [ 5 ] , ( uint32_t ) Idx * NUP_PACKET_DATA , WriteLen ) ;
Nup . ExpectIdx + + ;
Nup . LastTick = rt_tick_get ( ) ;
Nup . ErrCnt = 0 ;
if ( Nup . ExpectIdx > = Nup . PkgCnt ) {
if ( NetUpgradeCrc32Stash ( Nup . AppSize ) = = Nup . Crc32 ) {
U_DBG_LOG ( " NetUpgrade stash verified, reboot to bootloader \r \n " ) ;
UpdateFromStash ( Nup . AppSize , Nup . Crc32 ) ; //置APP_UPDATE_SPI_FLAG并复位
}
U_DBG_LOG ( " NetUpgrade stash CRC failed, restart download \r \n " ) ;
//校验失败: 从0重下(写前按需擦除保证暂存区干净)
NetUpgradeSavePkg ( 0 ) ;
NupErasedMap = 0 ;
Nup . ExpectIdx = 0 ;
NetUpgradeSendReq ( 0 ) ;
return ;
}
if ( Nup . ExpectIdx - Nup . LastAckIdx > = NUP_WINDOW ) {
NetUpgradeSendReq ( Nup . ExpectIdx ) ; //收满窗口, 请求下一批(与断点起点无关)
Nup . LastAckIdx = Nup . ExpectIdx ;
}
if ( Nup . ExpectIdx % ( NUP_WINDOW * 4 ) = = 0 ) {
NetUpgradeSavePkg ( Nup . ExpectIdx ) ; //定期持久化进度(32包一次)
}
}
static void NetUpgradeFrameHandler ( uint8_t Cmd , uint8_t * Payload , uint8_t Len )
{
U_DBG_LOG ( " NetUpgrade frame: cmd=0x%02x len=%d \r \n " , Cmd , Len ) ;
switch ( Cmd ) {
case 0x81 :
NetUpgradeOnReq ( Payload , Len ) ;
break ;
case 0x82 :
NetUpgradeOnData ( Payload , Len ) ;
break ;
default :
break ;
}
}
/*****************************************************************************************
* 函 数 名 称 : NetUpgradeFeed
* 功 能 描 述 : 喂 入 Cat1下行数据片段 ( + LIPURC二进制 ) , 流 式 拼 接 提 取 完 整 0x7D 升 级 帧
* 参 数 : data / len , 二 进 制 数 据 片 段 ( TCP分片有序 )
* 返 回 值 : true = 数 据 已 被 升 级 模 块 消 费 ( 帧 头 0x7D ) ; false = 非 0x7D 流 , 调 用 方 交 还 原 0x7A 路 径
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool NetUpgradeFeed ( uint8_t * data , uint16_t len )
{
if ( NupFrameLen = = 0 ) {
uint16_t skip = 0 ;
while ( skip < len & & data [ skip ] ! = 0x7D ) {
skip + + ;
}
if ( skip > = len ) {
return false ;
}
data + = skip ;
len - = skip ;
}
if ( NupFrameLen + len > NUP_FRAME_BUF_MAX ) {
NupFrameLen = 0 ; //溢出保护, 重新同步
return true ;
}
memcpy ( & NupFrameBuf [ NupFrameLen ] , data , len ) ;
NupFrameLen + = len ;
while ( NupFrameLen > = 11 ) {
if ( NupFrameBuf [ 0 ] ! = 0x7D ) {
uint16_t i ;
for ( i = 1 ; i < NupFrameLen ; i + + ) {
if ( NupFrameBuf [ i ] = = 0x7D ) break ;
}
if ( i > = NupFrameLen ) {
NupFrameLen = 0 ;
break ;
}
memmove ( NupFrameBuf , & NupFrameBuf [ i ] , NupFrameLen - i ) ;
NupFrameLen - = i ;
continue ;
}
uint16_t total = 11 + NupFrameBuf [ 8 ] ;
if ( NupFrameLen < total ) {
break ; //帧未收齐, 等下一分片
}
uint16_t calc = CRC_Modbus ( CRC16_BASE , NupFrameBuf , total - 2 ) ;
uint16_t recv = NupFrameBuf [ total - 2 ] | ( NupFrameBuf [ total - 1 ] < < 8 ) ;
if ( calc = = recv ) {
/*MAC filter: only the device whose own GwMac matches the 0x7D frame header handles the frame,
broadcast 0x81 is answered by the target device only , other devices drop it silently */
if ( memcmp ( & NupFrameBuf [ 1 ] , GateWay . ConfigPara . GwMac , 6 ) = = 0 ) {
NetUpgradeFrameHandler ( NupFrameBuf [ 7 ] , & NupFrameBuf [ 9 ] , NupFrameBuf [ 8 ] ) ;
}
memmove ( NupFrameBuf , & NupFrameBuf [ total ] , NupFrameLen - total ) ;
NupFrameLen - = total ;
}
else {
memmove ( NupFrameBuf , & NupFrameBuf [ 1 ] , NupFrameLen - 1 ) ;
NupFrameLen - = 1 ;
}
}
return true ;
}
/*****************************************************************************************
* 函 数 名 称 : NetUpgradePopTx
* 功 能 描 述 : 取 待 发 应 答 帧 ( 0x01 ACK / 0x02 请 求 ) , 由 CatOne主循环经AT + LIPSEND直发
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool NetUpgradePopTx ( uint8_t * Out , uint16_t * OutLen )
bool NetUpgradePopTx ( uint8_t * Out , uint16_t * OutLen , uint8_t ch )
{
bool ret = false ;
uint8_t cmdLog = 0 ;
rt_base_t level = rt_hw_interrupt_disable ( ) ;
if ( Nup . TxCnt > 0 ) {
cmdLog = Nup . TxQ [ Nup . TxHead ] [ 7 ] ;
memcpy ( Out , Nup . TxQ [ Nup . TxHead ] , Nup . TxQLen [ Nup . TxHead ] ) ;
* OutLen = Nup . TxQLen [ Nup . TxHead ] ;
Nup . TxHead = ( Nup . TxHead + 1 ) % 4 ;
for ( uint8_t idx = 0 ; idx < Nup . TxCnt ; idx + + ) {
uint8_t slot = ( Nup . TxHead + idx ) % 4 ;
if ( Nup . TxChan [ slot ] ! = ch ) continue ; /*跳过其他通道的帧(Cat1/ETH各自取自己的ACK)*/
memcpy ( Out , Nup . TxQ [ slot ] , Nup . TxQLen [ slot ] ) ;
* OutLen = Nup . TxQLen [ slot ] ;
uint8_t keep = Nup . TxCnt - idx - 1 ; /*slot之后的帧数: 依次前移紧凑*/
for ( uint8_t m2 = 0 ; m2 < keep ; m2 + + ) {
uint8_t curSlot = ( slot + m2 ) % 4 ;
uint8_t nxt = ( slot + m2 + 1 ) % 4 ;
memcpy ( Nup . TxQ [ curSlot ] , Nup . TxQ [ nxt ] , 40 ) ;
Nup . TxChan [ curSlot ] = Nup . TxChan [ nxt ] ;
Nup . TxQLen [ curSlot ] = Nup . TxQLen [ nxt ] ;
}
Nup . TxCnt - - ;
ret = true ;
break ;
}
rt_hw_interrupt_enable ( level ) ;
if ( ret ) {
U_DBG_LOG ( " NetUpgrade pop: cmd=0x%02x \r \n " , cmdLog ) ;
}
return ret ;
}
bool NetUpgradeActive ( void )
{
return Nup . Active ;
}
/*****************************************************************************************
* 函 数 名 称 : NetUpgradeTick
* 功 能 描 述 : 1 ms轮询 ( CatOne主循环调用 ) : 等 包 超 时 催 发 请 求 , 长 期 无 进 展 放 弃 会 话
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void NetUpgradeTick ( void )
{
if ( ! Nup . Active ) {
if ( NupFrameLen > 0 ) {
NupFrameLen = 0 ; /*非会话期清理残片, 防止后续0x7A业务流被组帧器吞掉*/
}
return ;
}
uint32_t Now = rt_tick_get ( ) ;
if ( Now - Nup . LastTick > NUP_IDLE_TIMEOUT_MS ) {
Nup . Active = false ;
NupFrameLen = 0 ;
U_DBG_LOG ( " NetUpgrade idle timeout(30min), abort session \r \n " ) ;
return ;
}
if ( Now - Nup . LastTick > NUP_TIMEOUT_MS ) {
Nup . LastTick = Now ;
NupFrameLen = 0 ; /*等包超时: 残片必为损坏流, 清掉重同步*/
Nup . ErrCnt + + ;
if ( Nup . ErrCnt > NUP_TIMEOUT_MAX ) {
Nup . Active = false ; //放弃会话(不复位), 服务器可重新0x81发起
NupFrameLen = 0 ;
U_DBG_LOG ( " NetUpgrade timeout, abort session \r \n " ) ;
return ;
}
NetUpgradeSendReq ( Nup . ExpectIdx ) ; //催发: 服务端重发当前批次
}
}
/*****************************************************************************************
* 函 数 名 称 : UpdateFromStash
* 功 能 描 述 : 暂 存 区 校 验 通 过 后 , 置 APP_UPDATE_SPI_FLAG并复位 , Boot从外部Flash搬运升级
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void UpdateFromStash ( uint32_t AppSize , uint32_t Crc32 )
{
boot_para_t cfg ;
READ_BOOT_PARA ( & cfg ) ;
cfg . AppSize = AppSize ;
cfg . Crc32Check = Crc32 ;
cfg . PackageCnt = ( uint16_t ) ( ( AppSize + NUP_PACKET_DATA - 1 ) / NUP_PACKET_DATA ) ;
cfg . AppFlag = 0 ;
cfg . UpdateFlag = APP_UPDATE_SPI_FLAG ;
NetUpgradeSavePkg ( 0 ) ; /*清除进度记录:本次升级已完结,下次升级从0重新开始*/
U_DBG_LOG ( " Upgrade ready: Size=%d Crc=0x%08x, reboot \r \n " , AppSize , Crc32 ) ;
SAVE_BOOT_PARA ( & cfg ) ;
U_DELAY_MS ( 200 ) ;
U_SYSTEM_TESET ( ) ;
}
} /*****************************************************************************************
* 函 数 名 : NetUpgradeActive
* 功 能 : 升 级 会 话 状 态 查 询
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool NetUpgradeActive ( void )
{
return Nup . Active ;
}
/*****************************************************************************************
* 函 数 名 称 : NetUpgradeTick
* 功 能 描 述 : 1 ms轮询 ( CatOne主循环调用 ) : 等 包 超 时 催 发 请 求 , 长 期 无 进 展 放 弃 会 话
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void NetUpgradeTick ( void )
{
if ( ! Nup . Active ) {
if ( NupFrameLen > 0 ) {
NupFrameLen = 0 ; /*非会话期清理残片, 防止后续0x7A业务流被组帧器吞掉*/
}
return ;
}
uint32_t Now = rt_tick_get ( ) ;
if ( Now - Nup . LastTick > NUP_IDLE_TIMEOUT_MS ) {
Nup . Active = false ;
NupFrameLen = 0 ;
U_DBG_LOG ( " NetUpgrade idle timeout(30min), abort session \r \n " ) ;
return ;
}
if ( Now - Nup . LastTick > NUP_TIMEOUT_MS ) {
Nup . LastTick = Now ;
NupFrameLen = 0 ; /*等包超时: 残片必为损坏流, 清掉重同步*/
Nup . ErrCnt + + ;
if ( Nup . ErrCnt > NUP_TIMEOUT_MAX ) {
Nup . Active = false ; //放弃会话(不复位), 服务器可重新0x81发起
NupFrameLen = 0 ;
U_DBG_LOG ( " NetUpgrade timeout, abort session \r \n " ) ;
return ;
}
NetUpgradeSendReq ( Nup . ExpectIdx ) ; //催发: 服务端重发当前批次
}
}
/*****************************************************************************************
* 函 数 名 称 : UpdateFromStash
* 功 能 描 述 : 暂 存 区 校 验 通 过 后 , 置 APP_UPDATE_SPI_FLAG并复位 , Boot从外部Flash搬运升级
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void UpdateFromStash ( uint32_t AppSize , uint32_t Crc32 )
{
boot_para_t cfg ;
READ_BOOT_PARA ( & cfg ) ;
cfg . AppSize = AppSize ;
cfg . Crc32Check = Crc32 ;
cfg . PackageCnt = ( uint16_t ) ( ( AppSize + NUP_PACKET_DATA - 1 ) / NUP_PACKET_DATA ) ;
cfg . AppFlag = 0 ;
cfg . UpdateFlag = APP_UPDATE_SPI_FLAG ;
NetUpgradeSavePkg ( 0 ) ; /*清除进度记录:本次升级已完结,下次升级从0重新开始*/
U_DBG_LOG ( " Upgrade ready: Size=%d Crc=0x%08x, reboot \r \n " , AppSize , Crc32 ) ;
SAVE_BOOT_PARA ( & cfg ) ;
U_DELAY_MS ( 200 ) ;
U_SYSTEM_TESET ( ) ;
}