feat(ota): ETH口升级兼容方案与升级链路收尾, 软件版本V1.4

- 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不匹配提示日志
This commit is contained in:
2026-10-12 10:24:44 +08:00
parent 920bddf949
commit 5db05d061b
7 changed files with 572 additions and 485 deletions
+4 -4
View File
@@ -432,18 +432,18 @@
<Bp>
<Number>0</Number>
<Type>0</Type>
<LineNumber>569</LineNumber>
<LineNumber>503</LineNumber>
<EnabledFlag>1</EnabledFlag>
<Address>0</Address>
<Address>54178</Address>
<ByteObject>0</ByteObject>
<HtxType>0</HtxType>
<ManyObjects>0</ManyObjects>
<SizeOfObject>0</SizeOfObject>
<BreakByAccess>0</BreakByAccess>
<BreakIfRCount>0</BreakIfRCount>
<BreakIfRCount>1</BreakIfRCount>
<Filename>..\source\User\Src\CatOneTask.c</Filename>
<ExecCommand></ExecCommand>
<Expression></Expression>
<Expression>\\GateWay\../source/User/Src/CatOneTask.c\503</Expression>
</Bp>
</Breakpoint>
<WatchWindow1>
+1
View File
@@ -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);
+2 -2
View File
@@ -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
+3 -15
View File
@@ -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配置分流*/
+93 -27
View File
@@ -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();
+19 -1
View File
@@ -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;
+450 -436
View File
@@ -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)
{
uint8_t cmdLog = 0;
bool ret = false;
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;
rt_base_t level = rt_hw_interrupt_disable();
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;
}
if(ret) {
U_DBG_LOG("NetUpgrade pop: cmd=0x%02x\r\n", cmdLog);
}
rt_hw_interrupt_enable(level);
}
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();
}
return ret;
}/*****************************************************************************************
* 函 数 名: 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();