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();
+}