From 5db05d061ba145327d09a3609a74931059f46b87 Mon Sep 17 00:00:00 2001 From: YuanHongbin <975559679@qq.com> Date: Mon, 12 Oct 2026 10:24:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(ota):=20ETH=E5=8F=A3=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=96=B9=E6=A1=88=E4=B8=8E=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E9=93=BE=E8=B7=AF=E6=94=B6=E5=B0=BE,=20=E8=BD=AF=E4=BB=B6?= =?UTF-8?q?=E7=89=88=E6=9C=ACV1.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ETH口参考Cat1接入0x7D升级: 下行IsHexText校验/NetUpgradeFeed第一优先/明文直通DebugAnalyze, 升级应答帧主循环最顶部直发 - 升级TX应答队列按通道绑定(TxChan/CurCh): Cat1/ETH各自取回本通道ACK, 双链路互不抢占 - ETH接收改为8KB单生产单消费环形缓冲: 修复IRQ与Rev线程单缓冲竞态丢包 - 升级会话期间ETH状态机旁路: 防RESET/OFF复位模组断链; 会话内纯hex流模式(奇数尾字符跨块拼接) - Public: IsHexText公共化(Cat1/Eth共用), AsciiToHex内部计数器uint16修复(512字符块溢出丢帧) - Cat1: 升级帧带CH_CAT1通道标签, 空闲5分钟保活重连只建链不上传 - 移除全部[ETHDBG]调试打印, 新增MAC不匹配提示日志 --- Project/GateWay/MDK/GateWay.uvoptx | 8 +- Project/GateWay/source/User/Inc/Public.h | 1 + Project/GateWay/source/User/Inc/Update.h | 4 +- Project/GateWay/source/User/Src/CatOneTask.c | 18 +- Project/GateWay/source/User/Src/EthTask.c | 120 ++- Project/GateWay/source/User/Src/Public.c | 20 +- Project/GateWay/source/User/Src/Update.c | 886 ++++++++++--------- 7 files changed, 572 insertions(+), 485 deletions(-) diff --git a/Project/GateWay/MDK/GateWay.uvoptx b/Project/GateWay/MDK/GateWay.uvoptx index 1dcfca5..d5c10e9 100644 --- a/Project/GateWay/MDK/GateWay.uvoptx +++ b/Project/GateWay/MDK/GateWay.uvoptx @@ -432,18 +432,18 @@ 0 0 - 569 + 503 1 -
0
+
54178
0 0 0 0 0 - 0 + 1 ..\source\User\Src\CatOneTask.c - + \\GateWay\../source/User/Src/CatOneTask.c\503
diff --git a/Project/GateWay/source/User/Inc/Public.h b/Project/GateWay/source/User/Inc/Public.h index efd2764..a1a2c2e 100644 --- a/Project/GateWay/source/User/Inc/Public.h +++ b/Project/GateWay/source/User/Inc/Public.h @@ -358,6 +358,7 @@ struct GateWay_t{ int MuchRevCallBack(GateWayPara GateWay, uint8_t *rData, uint16_t rLen); int AuchRevCallBack(GateWayPara GateWay, uint8_t *rData, uint16_t rLen); uint16_t CRC_Modbus(uint16_t wBase, __IO uint8_t *para, uint16_t length); +bool IsHexText(char *s, uint16_t len); uint16_t AsciiToHex(char *ASCData, uint8_t *HexData, uint16_t sLen); void HexToAscii(uint8_t *HexData, char *ASCData, uint8_t sLen); int NetCommOrgData(GateWayPara GateWay, uint8_t *MegData, uint8_t *OutData); diff --git a/Project/GateWay/source/User/Inc/Update.h b/Project/GateWay/source/User/Inc/Update.h index 2513f86..5e711c3 100644 --- a/Project/GateWay/source/User/Inc/Update.h +++ b/Project/GateWay/source/User/Inc/Update.h @@ -34,8 +34,8 @@ void Update(uint8_t *PayLoad); void UpdateInit(void); /*Cat1远程升级(0x7D帧)接口 - CatOneTask调用: 固件分包写外部SPI Flash暂存区, 收完置APP_UPDATE_SPI_FLAG复位*/ -bool NetUpgradeFeed(uint8_t *data, uint16_t len); //喂入Cat1下行数据片段, 返回true=已被升级模块消费 -bool NetUpgradePopTx(uint8_t *Out, uint16_t *OutLen); //取待发应答帧(0x01/0x02, 0x7D原始帧) +bool NetUpgradeFeed(uint8_t *data, uint16_t len, uint8_t ch); //喂入下行数据片段(带来源通道), 返回true=已被升级模块消费 +bool NetUpgradePopTx(uint8_t *Out, uint16_t *OutLen, uint8_t ch); //取本通道待发应答帧(0x01/0x02, 0x7D原始帧) bool NetUpgradeActive(void); //升级会话进行中 void NetUpgradeTick(void); //1ms轮询: 超时催发与会话放弃 void UpdateFromStash(uint32_t AppSize, uint32_t Crc32); //暂存区校验通过后置标志复位进Boot diff --git a/Project/GateWay/source/User/Src/CatOneTask.c b/Project/GateWay/source/User/Src/CatOneTask.c index 5d80013..c340005 100644 --- a/Project/GateWay/source/User/Src/CatOneTask.c +++ b/Project/GateWay/source/User/Src/CatOneTask.c @@ -488,7 +488,7 @@ void CatOneLoopHandler(void) case CAT_ONE_WAIT_SEND: /*Cat1远程升级应答帧优先(0x7D原始帧直发, 绕过0x7A打包)*/ - if(NetUpgradePopTx(CatOneTxBuff, &CatOneTxLen)) { + if(NetUpgradePopTx(CatOneTxBuff, &CatOneTxLen, CH_CAT1)) { CatSendErrCnt = 0; Cat1UpgTxFlag = 1; /*升级应答帧标记*/ CatOne.Cat1Status = CAT_ONE_SEND_DATA; @@ -651,18 +651,6 @@ static void CatOneInit(GateWayPara GateWay) GateWay->MuchRevCallBack : GateWay->AuchRevCallBack; } -/*hex文本合法性: 全部字符∈[0-9a-fA-F]且偶数长度(AsciiToHex不校验字符合法性, 非hex明文须先识别直通)*/ -static bool Cat1IsHexText(char *s, int len) -{ - if(len <= 0 || len % 2 != 0) - return false; - for(int i = 0; i < len; i++) { - char c = s[i]; - if(!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) - return false; - } - return true; -} /*+LIPURC异步事件统一分发: 与AT状态机无关, 任何状态下都能处理升级帧/业务数据*/ /*一批数据包的URC会连发, 多条可能挤进同一缓冲块: 必须循环解析每一条*/ static void CatOneUrcHandle(GateWayPara GateWay, char *RxBuff) @@ -686,7 +674,7 @@ static void CatOneUrcHandle(GateWayPara GateWay, char *RxBuff) if(sLen > 0) { memset(hData, 0x00, sizeof(hData)); /*ASCII hex转二进制(服务器下发为hex文本)*/ - hLen = Cat1IsHexText(rData, sLen) ? AsciiToHex(rData, hData, sLen) : 0; + hLen = IsHexText(rData, sLen) ? AsciiToHex(rData, hData, sLen) : 0; if(hLen == 0) { /*非hex明文(如网络调试助手直发命令): 原样直通调试解析, 与ETH口明文直通一致*/ hLen = (sLen < (int)sizeof(hData)) ? sLen : (int)sizeof(hData) - 1; @@ -695,7 +683,7 @@ static void CatOneUrcHandle(GateWayPara GateWay, char *RxBuff) } CatSendErrCnt = 0; Cat1RevErrCnt = 0; /*server data arrived: reload watchdog*/ - if(hLen > 0 && NetUpgradeFeed(hData, hLen)) { + if(hLen > 0 && NetUpgradeFeed(hData, hLen, CH_CAT1)) { /*0x7D frame consumed by NetUpgrade*/ } else if(hLen > 0 && hData[0] == 0x7A) { /*0x7A业务帧: 按Much/Auch配置分流*/ diff --git a/Project/GateWay/source/User/Src/EthTask.c b/Project/GateWay/source/User/Src/EthTask.c index d91fe52..5cba03b 100644 --- a/Project/GateWay/source/User/Src/EthTask.c +++ b/Project/GateWay/source/User/Src/EthTask.c @@ -1,14 +1,30 @@ #include "EthTask.h" #include "Public.h" #include "DebugCmd.h" +#include "Update.h" static rt_sem_t EthIRQ_Sem = RT_NULL; static rt_thread_t EthRev_Thread = RT_NULL; static EthTask_t Eth; -static char EthRxBuff[ETH_RX_LEN_MAX]; -static uint16_t EthRxLen; +/*单生产(IRQ)单消费(Rev)环形缓冲: 无锁, 读写指针独立安全; 防Rev处理期间IRQ覆盖丢包*/ +#define ETH_RING_SIZE 8192 +static uint8_t EthRingBuf[ETH_RING_SIZE]; +static volatile uint16_t EthRingW = 0, EthRingR = 0; +static uint16_t EthRingLen(void) +{ + return (uint16_t)((EthRingW - EthRingR + ETH_RING_SIZE) % ETH_RING_SIZE); +} +static uint16_t EthRingGet(uint8_t *dst, uint16_t max) +{ + uint16_t n = 0; + while(n < max && EthRingR != EthRingW) { + dst[n++] = EthRingBuf[EthRingR]; + EthRingR = (uint16_t)((EthRingR + 1) % ETH_RING_SIZE); + } + return n; +} static uint8_t EthRevTimeOutCnt; static uint8_t Payload[ETH_RX_LEN_MAX]; @@ -79,7 +95,6 @@ static int GateWayRegister(uint8_t *TxBuff) void EthLoopHandler(void) { rt_err_t result; - switch(Eth.Status) { case ETH_IDEL: rt_thread_delay(1); @@ -156,6 +171,11 @@ void EthLoopHandler(void) break; case ETH_WAIT_SEND: + if(NetUpgradePopTx(Eth.TxBuff, &Eth.TxLen, CH_ETH)) { /*升级应答帧第一优先(仅取本通道帧)*/ + + Eth.Status = ETH_SEND_DATA; + break; + } result = rt_mq_recv(ChannelMQ(*Eth.GateWay, CH_ETH), Payload, ETH_RX_LEN_MAX, 100); if(result == RT_EOK) { Eth.TxLen = NetCommOrgData(Eth.GateWay, Payload, Eth.TxBuff); @@ -188,6 +208,7 @@ void EthLoopHandler(void) } HexToAscii(Eth.TxBuff, CommUnitSendASCII, Eth.TxLen); EthRevTimeOutCnt = 0; + EthUartSend((uint8_t *)CommUnitSendASCII, Eth.TxLen * 2); CommUnitSendASCII[Eth.TxLen * 2] = 0; ETH_DBG_LOG("Eth Send: %s\r\n", CommUnitSendASCII); @@ -259,13 +280,11 @@ static void EthInit(GateWayPara GateWay) void EthIrqCallbackHandler(uint8_t rData) { - if(EthRevTimeOutCnt == 0) - EthRxLen = 0; - - if(EthRxLen < ETH_RX_LEN_MAX) { - EthRxBuff[EthRxLen++] = rData; + if((uint16_t)((EthRingW + 1) % ETH_RING_SIZE) != EthRingR) { /*未满才入, IRQ不阻塞*/ + EthRingBuf[EthRingW] = rData; + EthRingW = (uint16_t)((EthRingW + 1) % ETH_RING_SIZE); } - + /*强制入环: 满时覆盖旧数据, IRQ永不阻塞*/ EthRevTimeOutCnt = 3; } @@ -274,7 +293,7 @@ void EthOverHandler(void) if(EthRevTimeOutCnt > 0) EthRevTimeOutCnt--; - if(EthRxLen > 0 && EthRevTimeOutCnt == 0) { + if(EthRingLen() > 0 && EthRevTimeOutCnt == 0) { EthRevTimeOutCnt = 20; rt_sem_release(EthIRQ_Sem); } @@ -292,24 +311,59 @@ static void EthRev_Thread_Entry(void *parameter) while(1) { result = rt_sem_take(EthIRQ_Sem, 500); - if(result == RT_EOK || EthRxLen > 1) { - if(EthRxLen == 0) continue; - memcpy(RxBuffTemp, EthRxBuff, EthRxLen); - RxBuffTemp[EthRxLen] = 0; - memset(HexData, 0x00, EthRxLen / 2); - uint16_t ret = AsciiToHex(RxBuffTemp, HexData, EthRxLen); - if(ret > 0 && HexData[0] == 0x7A) { - Eth.SendNoDataCnt = 0; - if(GateWay->ConfigPara.Much == CH_ETH) - GateWay->MuchRevCallBack(GateWay, HexData, ret); - else - GateWay->AuchRevCallBack(GateWay, HexData, ret); + uint16_t n = EthRingLen(); + if(result == RT_EOK || n > 1) { + if(n == 0) continue; + n = EthRingGet((uint8_t *)RxBuffTemp, ETH_RX_LEN_MAX - 1); + RxBuffTemp[n] = 0; + uint16_t ret = 0; + if(NetUpgradeActive()) { + /*升级会话: 纯hex流模式, 奇数尾字符跨块拼接(模组512字节透传块会切在帧任意位置)*/ + static char hexTail = 0; + static bool tailV = false; + if(tailV && n < ETH_RX_LEN_MAX) { + memmove(&RxBuffTemp[1], RxBuffTemp, n); + RxBuffTemp[0] = hexTail; + n++; tailV = false; + } else { tailV = false; } + if(n % 2 == 1) { hexTail = RxBuffTemp[n - 1]; tailV = true; n--; } + RxBuffTemp[n] = 0; + if(n > 0 && IsHexText(RxBuffTemp, n)) { + memset(HexData, 0x00, ETH_RX_LEN_MAX / 2); + ret = AsciiToHex(RxBuffTemp, HexData, n); + bool fed = (ret > 0) ? NetUpgradeFeed(HexData, ret, CH_ETH) : false; + /*注意: 会话内禁止全量hex打印, 串口打印阻塞EthRev线程导致USART RX缓冲溢出丢包*/ + + if(fed) Eth.SendNoDataCnt = 0; /*清看门狗防升级会话被复位*/ + } } - else if(EthRxLen > 1) { - DebugAnalyze(GateWay, (uint8_t *)RxBuffTemp, EthRxLen); + else { + /*非会话: hex业务帧/明文命令分流(0x81握手在此分支进入升级模块)*/ + memset(HexData, 0x00, n / 2); + bool hexv = IsHexText(RxBuffTemp, n); + if(hexv) { + ret = AsciiToHex(RxBuffTemp, HexData, n); + } + + + bool fed = (ret > 0) ? NetUpgradeFeed(HexData, ret, CH_ETH) : false; + + if(fed) { + Eth.SendNoDataCnt = 0; + } + else if(ret > 0 && HexData[0] == 0x7A) { + Eth.SendNoDataCnt = 0; + if(GateWay->ConfigPara.Much == CH_ETH) + GateWay->MuchRevCallBack(GateWay, HexData, ret); + else + GateWay->AuchRevCallBack(GateWay, HexData, ret); + } + else if(n > 1) { + DebugAnalyze(GateWay, (uint8_t *)RxBuffTemp, n); + } } - memset(EthRxBuff, 0x00, ETH_RX_LEN_MAX); - EthRxLen = 0; + if(EthRingLen() > 0) + rt_sem_release(EthIRQ_Sem); /*缓冲仍有数据: 自释放信号量连续泵空*/ } } } @@ -339,8 +393,20 @@ void Eth_Thread_Entry(void *parameter) rt_kprintf("Eth Thread is running!\r\n"); while(1) { + /*升级会话应答帧: 最高优先直发, 不依赖通道配置/LINK状态/状态机(ACK原路CH_ETH返回)*/ + if(NetUpgradeActive() && NetUpgradePopTx(Eth.TxBuff, &Eth.TxLen, CH_ETH)) { + HexToAscii(Eth.TxBuff, CommUnitSendASCII, Eth.TxLen); + CommUnitSendASCII[Eth.TxLen * 2] = 0; + + EthUartSend((uint8_t *)CommUnitSendASCII, Eth.TxLen * 2); + } if(!ChannelIsActive(GateWay->ConfigPara, CH_ETH)) { - rt_thread_delay(100); + rt_thread_delay(20); + continue; + } + if(NetUpgradeActive()) { + /*升级会话: 状态机旁路(防RESET/OFF复位模组断链), 应答走主循环直发+接收线程收包*/ + rt_thread_delay(5); continue; } EthLoopHandler(); diff --git a/Project/GateWay/source/User/Src/Public.c b/Project/GateWay/source/User/Src/Public.c index 035b292..f03912c 100644 --- a/Project/GateWay/source/User/Src/Public.c +++ b/Project/GateWay/source/User/Src/Public.c @@ -126,10 +126,28 @@ void HexToAscii(uint8_t *HexData, char *ASCData, uint8_t sLen) sLen, 需要转的字符串长度 * 返 回 值: 无 *****************************************************************************************/ +/***************************************************************************************** +* 函数名称: IsHexText +* 功能描述: 判断缓冲区内容是否为合法hex文本(AsciiToHex不校验字符合法性, 非hex明文须先识别) +* 参 数: s, 待判断字符串; len, 字符长度 +* 返 回 值: true=合法hex文本(全字符∈[0-9a-fA-F]且偶数长度) +*****************************************************************************************/ +bool IsHexText(char *s, uint16_t len) +{ + if(len == 0 || len % 2 != 0) + return false; + for(uint16_t i = 0; i < len; i++) { + char c = s[i]; + if(!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) + return false; + } + return true; +} + uint16_t AsciiToHex(char *ASCData, uint8_t *HexData, uint16_t sLen) { char chr; - uint8_t HexLen = 0; + uint16_t HexLen = 0; /*uint16: 大块(512字符=256字节)时uint8溢出归0导致ret=0丢帧*/ if(sLen % 2 == 1) return 0; diff --git a/Project/GateWay/source/User/Src/Update.c b/Project/GateWay/source/User/Src/Update.c index 07ee712..cff6936 100644 --- a/Project/GateWay/source/User/Src/Update.c +++ b/Project/GateWay/source/User/Src/Update.c @@ -1,446 +1,460 @@ -#include "Update.h" -#include "DebugCmd.h" -#include "main.h" -#include "bsp.h" -#include "spiflash.h" -#include /*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 /*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 -* 功能描述: 1ms轮询(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 +* 功能描述: 1ms轮询(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(); +}