diff --git a/.trae/documents/cat1-remote-upgrade-plan.md b/.trae/documents/cat1-remote-upgrade-plan.md new file mode 100644 index 0000000..95b350c --- /dev/null +++ b/.trae/documents/cat1-remote-upgrade-plan.md @@ -0,0 +1,85 @@ +# Cat1 远程升级方案设计(待评审) + +## 总体架构 + +App 扮演"接收端"角色(与 Boot 在 RS485 升级中的角色相同),服务器扮演升级主机: + +``` +服务器(升级主机) --TCP--> 合宙Cat1模组 --USART1--> App(RT-Thread) + │ ├─ 收0x7D帧(0x81/0x82) → 固件分包写外部SPI Flash暂存区 + │←────────── 0x01 ACK(带进度) ──────────────┤ + │ └─ 收完→CRC32校验→置UpdateFlag=APP_UPDATE_SPI_FLAG→复位 +复位 + └→ Boot: 检测APP_UPDATE_SPI_FLAG + → SPI Flash逐扇区读 → 写内部B区 → CRC32(B) + → 搬运B→A → CRC32(A) → 擦SPI Flash暂存区 → 置完成标志 → 复位跳App +``` + +**升级协议完全复用 RS485 的 0x7D 帧**:帧头 0x7D + DevMac(6) + Cmd(1) + PayloadLen(1) + Payload + CRC16(Modbus),命令 0x81 升级请求{PackageNum, AppSize, AppCrc32} / 0x82 数据{PackageIndex, PackageNum, DataLen, Data[200]} / 0x01 ACK / 0x02 请求数据。服务器端协议不变。 + +## 关键决策与理由 + +1. **Boot 不驱动 Cat1**:AT 注网 30 秒起、Boot 仅 32KB 分区(当前 Code 16KB)、模组 TCP 连接态在 MCU 复位后不可控。收包/重试/续传等复杂逻辑全部放 App(RTOS 资源丰富),Boot 只做"从外部 Flash 搬运到 B 区"这一简单确定性动作。 +2. **固件暂存外部 SPI Flash**:16MB 尾部划 58×4KB=232KB 暂存区(扇区 4038~4095),日志环形写入回绕点同步从 16MB 末尾前移到扇区 4038。 +3. **复用 UpdateFlag 机制**:新增标志值 `APP_UPDATE_SPI_FLAG`(建议 0xA5A55A5A,与现有 START=0x55AA5A5A / UPDATE=0xA5A5AA55 / BOOT_UPGRADE=0x5A5A55A5 不冲突),`boot_para_t` 结构不变,RS485 本地升级路径完全不受影响。 +4. **依赖 TCP 可靠有序**:应用层丢包仅发生在断连/模组重启,因此用"断点续传 + 超时重发请求"而非滑动窗选择重传,状态机与 Boot 版本同构,服务器实现简单。 + +## 升级流程(含 Cat1 特化处理) + +1. 服务器 → `0x81{PackageNum, AppSize, AppCrc32}` +2. App 校验 DevMac/合法性(AppSize==0 或 >232KB 拒绝)→ 回 `0x01 ACK`: + - 载荷建议扩展为 `{Status, ResumedIdx}`:`0x20`=全新开始(App 擦暂存区)、`0x21`=断点续传(已收 ResumedIdx 包,服务器从该包续发)。**注意:原 0x01 载荷 1 字节(0x20),扩展为 3 字节需服务器端同步适配(新版本服务器)** +3. 服务器从 ResumedIdx 起发 `0x82{PackageIndex, ...}`: + - **流控(推荐窗口式)**:服务器连发 W 包(建议 W=8~16)→ App 校验 PackageIdx 顺序写入暂存区(乱序/重复包丢弃)→ App 主动发 `0x02{期望包号}` → 服务器继续。停等式(每包一问一答)在 Cat1 上太慢:1160 包 × RTT≈2~5 分钟+,窗口式约 30~60 秒 + - TCP 保证有序,App 端只需顺序校验 + 期望包号判定 +4. App 收完 → `CRC32(暂存区, AppSize)`: + - 通过 → 回 `0x01{0x30 完成}` → 写 boot_para_t{AppSize, Crc32Check, UpdateFlag=APP_UPDATE_SPI_FLAG} → 200ms → 复位 + - 失败 → 回 `0x01{0x31 CRC错}` → 不复位,清暂存区等待服务器重发(可多次) +5. Boot 检测 `APP_UPDATE_SPI_FLAG` → 逐 8KB:读外部 2×4KB 扇区 → 擦写内部 B 区 1×8KB 扇区 → `CRC32(B)` → `CopyAppBtoA()`(现有)→ `CRC32(A)` → **擦外部暂存区(防残留误判,同 RS485 路径 B 区擦除教训)** → 置 `APP_BOOT_UPGRADE_FLAG` → 复位跳 App +6. App 启动(`APP_BOOT_UPGRADE_FLAG`)→ 擦外部参数分区 → 上线后服务器可查版本确认升级成功 + +### 超时与重试参数(Cat1 特化,均为 App 侧状态机) + +| 参数 | Boot RS485 值 | Cat1 版建议 | 理由 | +|------|--------------|------------|------| +| 等包超时 | 500ms | **3 秒** | Cat1 RTT + 模组缓冲 | +| 重试次数 | 3 | **5** | 偶发断连需模组重连 | +| 断连恢复 | — | 模组重连(现有逻辑)后服务器重发 0x81,ACK 带进度续传 | TCP 断开后应用层无从补偿 | +| 整体超时 | — | 30 分钟无进展 → 放弃升级(清标志,不复位) | 防止无限等待 | + +### 断电自愈矩阵(Cat1 路径) + +| 断电/异常时机 | 恢复行为 | +|--------------|---------| +| App 收包中断连/断电 | 暂存区+进度保留 → 重连后 0x81 续传 | +| App 置标志后、复位前 | Boot 正常走 SPI Flash→B→A | +| Boot 搬运中(写B区)断电 | UpdateFlag 仍为 SPI_FLAG → B 区 CRC 有效则重搬;无效则清标志回老 App | +| 搬运完、擦暂存区中断电 | 参数未写 → B CRC 判定重搬(B 仍在);参数已写 → 跳新 App | + +## 改动清单 + +### App(GateWay 工程) +| 文件 | 改动 | +|------|------| +| `spiflash.h/c` | 新增 `FLASH_FW_STASH_BASE`(扇区 4038 起)+ StashErase/StashWrite/StashRead;`AddLog` 回绕点 16MB→扇区 4038;新增 CRC32(与 Boot GetCrc32 同算法 0xEDB88320) | +| `CatOneTask.c`(或新文件) | Cat1 通道增加 0x7D 帧组帧解析(`+LIPURC` hex→二进制后按 0x7D 组帧,与现有 0x7A 链路并存,按帧头区分);升级接收状态机(移植 Boot AnalyzeData+BL 状态机,超时/重试参数按上表) | +| `Update.c` | 新增 `UpdateFromStash()`:写 boot_para_t(APP_UPDATE_SPI_FLAG + AppSize + Crc32Check)→ 复位 | +| `bsp.h`(App) | `APP_UPDATE_SPI_FLAG` 宏;`boot_para_t` 不动 | +| `main.h`(App) | 版本号随迭代递增 | + +### Boot(HC32F460BootLoader 工程) +| 文件 | 改动 | +|------|------| +| `bsp.c/h` | SPI Flash 驱动:与 App 相同总线(需确认外部 Flash 挂载的 SPI 外设与引脚,Boot 复用),W25Q 写使能/扇区擦/页读命令;约 +2~3KB(Boot 当前 16KB,32KB 分区内余量足够) | +| `main.c/h` | `APP_UPDATE_SPI_FLAG` 宏 + main() 决策分支(SPI_FLAG → 新状态 `BL_STASH_TO_B`);`BL_STASH_TO_B`:逐 8KB 外部读→B 区写→CRC(B) 通过进 `BL_COPY_TO_A`(现有),失败清标志回老 App;`BL_COPY_TO_A` 成功分支追加"擦外部暂存区" | + +### 服务器端(不在本仓库,需同步开发) +- 实现 0x81/0x82 升级主机逻辑:窗口流控(W 包一批)、解析 0x02 期望包号、断点续传(ACK 的 ResumedIdx)、完成/失败状态处理 + +## 风险与待确认项 + +1. **调试口与 Cat1 共用 USART1(PB08 方向切换)**:升级期间必须保证 UART 方向锁定在 Cat1 侧(`CAT1_ON()`),升级状态机运行期间禁止调试打印切向(`U_DBG_LOG` 若触发 `DBG_ON()` 会破坏 TCP 数据)。实施时需审查 DEBUG=1 下所有升级路径的打印。 +2. **外部 Flash 挂载总线待确认**:读 spiflash.c 确认 SPI 外设号/引脚/QSPI 模式,Boot 驱动按此实现。 +3. **日志区历史数据**:回绕点前移到扇区 4038(约 15.77MB),已存历史日志超出的部分会被覆盖——现网日志增长慢,实际影响小,但需知晓。 +4. **0x01 ACK 载荷扩展**(1→3 字节)需服务器端配合,若服务器不改动则退化为停等式流控(慢但兼容)。 +5. **升级流量**:232KB/次,Cat1 资费可忽略。 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/MDK/RTE/RTOS/board.c b/Project/GateWay/MDK/RTE/RTOS/board.c index 9bbfa65..e7d9228 100644 --- a/Project/GateWay/MDK/RTE/RTOS/board.c +++ b/Project/GateWay/MDK/RTE/RTOS/board.c @@ -76,9 +76,9 @@ void rt_hw_board_init(void) static int uart_init(void) { -//#error "TODO 2: Enable the hardware uart and config baudrate." - DbgOrCat1Uart_Config(500000); -// RS485Ch1_Config(500000); + /*USART1专用Cat1: 上电恒115200+CAT1方向*/ + DbgOrCat1Uart_Config(115200); + CAT1_ON(); return 0; } INIT_BOARD_EXPORT(uart_init); @@ -86,15 +86,8 @@ INIT_BOARD_EXPORT(uart_init); void rt_hw_console_output(const char *str) { rt_enter_critical(); - switch(GateWay.ConfigPara.Duch) { - case CH_RS485_1: RS485Ch1UartSend((uint8_t *)str, strlen(str)); break; - case CH_RS485_2: RS485Ch2UartSend((uint8_t *)str, strlen(str)); break; - case CH_ETH: EthUartSend((uint8_t *)str, strlen(str)); break; - case CH_LORA: Sx1276LoRaSendBuffer((uint8_t *)str, strlen(str)); break; - case CH_CAT1: DebugOrCat1UartSend((uint8_t *)str, strlen(str)); break; - case CH_DBG: DebugOrCat1UartSend((uint8_t *)str, strlen(str)); break; - default: DebugOrCat1UartSend((uint8_t *)str, strlen(str)); break; - } + /*USART1专用Cat1后, 调试输出统一走RS485 Ch1*/ + RS485Ch1UartSend((uint8_t *)str, strlen(str)); rt_exit_critical(); } diff --git a/Project/GateWay/source/Module/GateWay_Debug/DebugCmd.c b/Project/GateWay/source/Module/GateWay_Debug/DebugCmd.c index 835f0d1..8317a94 100644 --- a/Project/GateWay/source/Module/GateWay_Debug/DebugCmd.c +++ b/Project/GateWay/source/Module/GateWay_Debug/DebugCmd.c @@ -43,24 +43,8 @@ void Debug_Printf(char *format, ...) va_list args; va_start(args, format); vsprintf(&tempBuff[0], format, args); - if(GateWay && GateWay->ConfigPara.Duch == CH_CAT1 - && GateWay->ConfigPara.Much != CH_CAT1 - && GateWay->ConfigPara.Auch != CH_CAT1 - && !(GateWay->ConfigPara.CuchMask & CUCH_CAT1)) { - char *line = tempBuff; - while(line && *line) { - char *end = strstr(line, "\r\n"); - uint16_t n = end ? (uint16_t)(end - line + 2) : (uint16_t)strlen(line); - if(n > 240) n = 240; - if(n > 0) Cat1DuchSend(line, n); - line = end ? end + 2 : NULL; - } - } else { - rt_kprintf(tempBuff); - } - if(ParaEchoCh1) { - RS485Ch1UartSend((uint8_t *)tempBuff, strlen(tempBuff)); - } + /*USART1专用Cat1后, 调试输出统一走RS485 Ch1(经rt_kprintf→rt_hw_console_output)*/ + rt_kprintf(tempBuff); va_end(args); } /***************************************************************************************** @@ -98,18 +82,8 @@ void Debug_Thread_Entry(void *parameter) *****************************************************************************************/ void DbgOrCat1RxIrqCallback(uint8_t rData) { - if(ChannelIsActive(GateWay->ConfigPara, CH_CAT1)) { - Cat1IrqCallback(rData); - return; - } - - if(DebugRxTimeOut1mSCnt == 0) - DebugRxLen = 0; - - DebugRxTimeOut1mSCnt = 3; - if(DebugRxLen < DBG_RX_LEN_MAX) { - DebugRxBuff[DebugRxLen++] = rData; - } + /*USART1专用Cat1: 接收数据无条件进Cat1处理(不再按配置分流)*/ + Cat1IrqCallback(rData); } @@ -1305,9 +1279,8 @@ void DebugCmdSelDebugChannel(int argc, char *argv[]) DBG_LOG("Invalid channel.\r\n"); return; } - if((ch == CH_DBG && ChannelIsActive(GateWay->ConfigPara, CH_CAT1)) - || (ch == CH_CAT1 && ChannelIsActive(GateWay->ConfigPara, CH_DBG))) { - DBG_LOG("USART1 occupied by dbg/cat1 pair.\r\n"); + if(ch == CH_DBG || ch == CH_CAT1) { + DBG_LOG("USART1 is dedicated to Cat1, duch switch denied.\r\n"); return; } if(ChannelIsActive(GateWay->ConfigPara, ch) diff --git a/Project/GateWay/source/User/Inc/Public.h b/Project/GateWay/source/User/Inc/Public.h index d4b7edc..877b977 100644 --- a/Project/GateWay/source/User/Inc/Public.h +++ b/Project/GateWay/source/User/Inc/Public.h @@ -1,4 +1,4 @@ -#ifndef __PUBLIC_H +#ifndef __PUBLIC_H #define __PUBLIC_H #include "bsp.h" @@ -359,7 +359,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); -uint8_t AsciiToHex(char *ASCData, uint8_t *HexData, uint8_t sLen); +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); int CommUnitAnalyze(GateWayPara GateWay, uint8_t *rData, uint16_t rLen, SendData Response); diff --git a/Project/GateWay/source/User/Inc/Update.h b/Project/GateWay/source/User/Inc/Update.h index 626f523..2513f86 100644 --- a/Project/GateWay/source/User/Inc/Update.h +++ b/Project/GateWay/source/User/Inc/Update.h @@ -32,5 +32,12 @@ typedef struct { //输出接口 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 NetUpgradeActive(void); //升级会话进行中 +void NetUpgradeTick(void); //1ms轮询: 超时催发与会话放弃 +void UpdateFromStash(uint32_t AppSize, uint32_t Crc32); //暂存区校验通过后置标志复位进Boot #endif diff --git a/Project/GateWay/source/User/Inc/bsp.h b/Project/GateWay/source/User/Inc/bsp.h index d47c579..61194b4 100644 --- a/Project/GateWay/source/User/Inc/bsp.h +++ b/Project/GateWay/source/User/Inc/bsp.h @@ -35,6 +35,7 @@ #define APP_START_FLAG 0x55AA5A5A #define APP_UPDATE_FLAG 0xA5A5AA55 #define APP_BOOT_UPGRADE_FLAG 0x5A5A55A5 +#define APP_UPDATE_SPI_FLAG 0xA5A55A5A //Cat1远程升级: 固件已存外部SPI Flash暂存区, Boot据此搬运 #define WATCH_DOG 1 diff --git a/Project/GateWay/source/User/Inc/spiflash.h b/Project/GateWay/source/User/Inc/spiflash.h index 4228fed..065f808 100644 --- a/Project/GateWay/source/User/Inc/spiflash.h +++ b/Project/GateWay/source/User/Inc/spiflash.h @@ -40,6 +40,12 @@ uint32_t SpiFlashReadId(void); #define LOG_INFO_START_SECTOR 0x02 #define LOG_SAV_START_SECTOR 0x03 +/*固件升级暂存区(外部Flash尾部): 扇区4037=升级进度区, 扇区4038~4095=固件镜像(232KB,与Boot A/B分区一致)*/ +#define FLASH_FW_STASH_PROG_SECTOR 4037 +#define FLASH_FW_STASH_SECTOR 4038 +#define FLASH_FW_STASH_SECTORS 58 +#define LOG_SAV_END_ADDR (FLASH_FW_STASH_PROG_SECTOR * SPIFLASH_SECTORSIZE) + //#if (LORA_MODULE == WH_LR36_L) //#define LOG_SAV_SIZE 78 //#define LOG_SAV_NUM_SECTOR 52 //每扇区存储数量 @@ -70,6 +76,12 @@ void AddLog(uint8_t *wData, uint32_t wLen); int ReadLog(LogHeader Header, uint8_t **wData, uint32_t LogIdx); int ReadPara(uint8_t *Para, uint32_t ParaLen); int WritePara(uint8_t *Para, uint32_t ParaLen); +void FwStashEraseAll(void); +void FwStashProgRead(uint8_t *rData, uint16_t rLen); +void FwStashProgWrite(uint8_t *wData, uint16_t wLen); +void FwStashEraseSector(uint32_t SectorIdx); +int FwStashWrite(uint8_t *wData, uint32_t Offset, uint16_t wLen); +int FwStashRead(uint8_t *rData, uint32_t Offset, uint16_t rLen); /* USER CODE END */ diff --git a/Project/GateWay/source/User/Src/CatOneTask.c b/Project/GateWay/source/User/Src/CatOneTask.c index 55146f2..9f2b90e 100644 --- a/Project/GateWay/source/User/Src/CatOneTask.c +++ b/Project/GateWay/source/User/Src/CatOneTask.c @@ -1,15 +1,18 @@ #include "CatOneTask.h" #include "Public.h" #include "DebugCmd.h" +#include "Update.h" static rt_sem_t CatOneRev_Sem = RT_NULL; static rt_sem_t CatOneIRQ_Sem = RT_NULL; static rt_thread_t Cat1Rev_Thread = RT_NULL; -static uint8_t CAT1DispEn = false; +static uint8_t CAT1DispEn = true; /*默认常开: Cat1调试信息始终输出, 可用Cat1DBGOnOff()动态关闭*/ static CatOne_t CatOne; -static char CatOneRxBuff[CAT_ONE_REV_LEN_MAX]; +#define CAT_ONE_URC_LEN_MAX 4096 //+LIPURC一批连发8包约3.6K, 预留4K + +static char CatOneRxBuff[CAT_ONE_URC_LEN_MAX]; static uint16_t CatOneRxLen; static uint8_t CatOneTxBuff[CAT_ONE_REV_LEN_MAX]; @@ -298,41 +301,11 @@ void CatOneAtCmdAnalyze(char *RxBuff) CatOne.ATCmdRet = CAT_ONE_RET_ERR; break; case CAT_ONE_AT_NULL: - p = strstr(RxBuff, "+LIPURC"); - if(p != NULL) { - char rData[120]; - uint8_t hData[60]; - int rLen, hLen, ret; - - if(strstr(p, "+LIPURC: 0,0") != NULL) { - CAT1_DBG_LOG("TCP connection disconnected.\r\n\r\n"); - CatOne.TcpConnFlag = false; - CatOne.Cat1Status = CAT_ONE_OFF; - return; - } - - ret = sscanf(p, "+LIPURC: 0,1,%d,%119s\r\n", &rLen, rData); - if(ret == 2) { - if(rLen < 24) - return; - memset(hData, 0x00, 60); - hLen = AsciiToHex(rData, hData, rLen); - CatSendErrCnt = 0; - if(CatOne.GateWay->ConfigPara.Much == CH_CAT1) - CatOne.GateWay->MuchRevCallBack(CatOne.GateWay, hData, hLen); - else - CatOne.GateWay->AuchRevCallBack(CatOne.GateWay, hData, hLen); - } - else { - return; - } - rt_sem_release(CatOneRev_Sem); - return; - } if(CatOne.TcpConnFlag && RxBuff[0] > ' ') { DebugAnalyze(CatOne.GateWay, (uint8_t *)RxBuff, strlen(RxBuff)); return; } + break; default: CatOne.ATCmdRet = CAT_ONE_RET_ERR; @@ -415,6 +388,9 @@ void CatOneLoopHandler(void) static rt_err_t result; uint16_t ret; static uint8_t Cat1RevErrCnt = 0; + static uint8_t Cat1UpgTxFlag = 0; /*当前LIPSEND是否升级应答帧: 发完即回WAIT_SEND*/ + + NetUpgradeTick(); /*Cat1远程升级会话超时管理*/ switch(CatOne.Cat1Status){ case CAT_ONE_IDEL: @@ -493,6 +469,13 @@ void CatOneLoopHandler(void) break; case CAT_ONE_WAIT_SEND: + /*Cat1远程升级应答帧优先(0x7D原始帧直发, 绕过0x7A打包)*/ + if(NetUpgradePopTx(CatOneTxBuff, &CatOneTxLen)) { + CatSendErrCnt = 0; + Cat1UpgTxFlag = 1; /*升级应答帧标记*/ + CatOne.Cat1Status = CAT_ONE_SEND_DATA; + break; + } result = rt_mq_recv(ChannelMQ(*CatOne.GateWay, CH_CAT1), Payload, CAT_ONE_REV_LEN_MAX, 1000); if(result == RT_EOK) { CatSendErrCnt = 0; @@ -527,7 +510,13 @@ void CatOneLoopHandler(void) Cat1RevErrCnt = 0; break; } - CatOneAtCmdSend(CAT_ONE_LIPSEND, 5000, CAT_ONE_REV_DATA); + if(Cat1UpgTxFlag) { /*升级应答帧: 发完即回WAIT_SEND取下一帧, 不等服务器响应*/ + Cat1UpgTxFlag = 0; + CatOneAtCmdSend(CAT_ONE_LIPSEND, 5000, CAT_ONE_WAIT_SEND); + } + else { + CatOneAtCmdSend(CAT_ONE_LIPSEND, 5000, CAT_ONE_REV_DATA); + } break; case CAT_ONE_REV_DATA: @@ -601,9 +590,8 @@ void CatOneLoopHandler(void) CatOne.RegisterDelayCnt = 0; CAT1_DBG_LOG("Cat1 Power Off!\r\n"); CatOne.Cat1Status = CAT_ONE_IDEL; - CatOne.ResetDelayCnt = CATONE_RESET_DELAY_TIME_MAX; - DBG_ON(); - break; + CatOne.ResetDelayCnt = CATONE_RESET_DELAY_TIME_MAX; + break; case CAT_ONE_RESET: CatSendErrCnt = 0; @@ -644,10 +632,54 @@ static void CatOneInit(GateWayPara GateWay) GateWay->MuchRevCallBack : GateWay->AuchRevCallBack; } +/*+LIPURC异步事件统一分发: 与AT状态机无关, 任何状态下都能处理升级帧/业务数据*/ +/*一批数据包的URC会连发, 多条可能挤进同一缓冲块: 必须循环解析每一条*/ +static void CatOneUrcHandle(GateWayPara GateWay, char *RxBuff) +{ + static char rData[3010]; /*static: 大缓冲+单线程访问*/ + static uint8_t hData[1510]; + char *p; + int rLen = 0, hLen; + + if(strstr(RxBuff, "+LIPURC: 0,0") != NULL) { + CAT1_DBG_LOG("TCP connection disconnected.\r\n\r\n"); + CatOne.TcpConnFlag = false; + CatOne.Cat1Status = CAT_ONE_OFF; + return; + } + + p = strstr(RxBuff, "+LIPURC: 0,1,"); + while(p != NULL) { + if(sscanf(p, "+LIPURC: 0,1,%d,%3000s", &rLen, rData) == 2) { + if(rLen >= 24) { + if((int)strlen(rData) >= rLen) { + memset(hData, 0x00, sizeof(hData)); /*ASCII hex转二进制(服务器下发为hex文本)*/ + hLen = AsciiToHex(rData, hData, rLen); + CatSendErrCnt = 0; + if(hLen > 0 && NetUpgradeFeed(hData, hLen)) { + /*0x7D升级帧已由Cat1远程升级模块消费*/ + } + else if(GateWay->ConfigPara.Much == CH_CAT1) + GateWay->MuchRevCallBack(GateWay, hData, hLen); + else + GateWay->AuchRevCallBack(GateWay, hData, hLen); + } + else { + CAT1_DBG_LOG("LIPURC trunc, drop!\r\n"); + } + } + } + else { + CAT1_DBG_LOG("LIPURC parse fail, drop!\r\n"); + } + p = strstr(p + 13, "+LIPURC: 0,1,"); /*头部长13字节, 继续找下一条URC*/ + } + rt_sem_release(CatOneRev_Sem); /*通知AT状态机: 已收到服务器数据(维持REV_DATA快速返回)*/ +} void CatOneRev_Thread_Entry(void *parameter) { rt_err_t result; - char RxBuffTemp[CAT_ONE_REV_LEN_MAX]; + static char RxBuffTemp[CAT_ONE_URC_LEN_MAX]; /*static: 大缓冲避撑爆线程栈(单线程访问)*/ GateWayPara GateWay = (GateWayPara)parameter; @@ -659,15 +691,14 @@ void CatOneRev_Thread_Entry(void *parameter) memcpy(RxBuffTemp, CatOneRxBuff, CatOneRxLen); RxBuffTemp[CatOneRxLen] = 0; CatOneRxLen = 0; - char *urc = strstr(RxBuffTemp, "+LIPURC: 0,1,"); - if(urc) { - char *data = strchr(strchr(strchr(urc, ',')+1, ',')+1, ',') + 1; - char *end = strchr(data, '\r'); - if(end) *end = 0; - DebugAnalyze(GateWay, (uint8_t *)data, strlen(data)); + /*+LIPURC是模组异步事件: 统一在此分发(升级/业务), 不进AT状态机*/ + if(strstr(RxBuffTemp, "+LIPURC") != NULL) { + CatOneUrcHandle(GateWay, RxBuffTemp); } - CatOneAtCmdAnalyze(RxBuffTemp); - memset(CatOneRxBuff, 0x00, CAT_ONE_REV_LEN_MAX); + else { + CatOneAtCmdAnalyze(RxBuffTemp); + } + memset(CatOneRxBuff, 0x00, CatOneRxLen + 1); } } } @@ -699,10 +730,7 @@ void CatOne_Thread_Entry(void *parameter) rt_kprintf("CatOne Thread is running!\r\n"); while(1) { - if(!ChannelIsActive(GateWay->ConfigPara, CH_CAT1)) { - rt_thread_delay(100); - continue; - } + /*USART1专用Cat1: 上电即持续注网连接, 不再依赖通道配置*/ CatOneLoopHandler(); } } @@ -712,7 +740,7 @@ void Cat1IrqCallback(uint8_t rData) if(Cat1RevTimeOutCnt == 0) CatOneRxLen = 0; - if(CatOneRxLen < CAT_ONE_REV_LEN_MAX) { + if(CatOneRxLen < CAT_ONE_URC_LEN_MAX) { CatOneRxBuff[CatOneRxLen++] = rData; } @@ -749,3 +777,4 @@ void CatOneTriggerRegister(void) CAT1_ON(); CatOneReset(); } + diff --git a/Project/GateWay/source/User/Src/EthTask.c b/Project/GateWay/source/User/Src/EthTask.c index 0aa8249..d91fe52 100644 --- a/Project/GateWay/source/User/Src/EthTask.c +++ b/Project/GateWay/source/User/Src/EthTask.c @@ -297,7 +297,7 @@ static void EthRev_Thread_Entry(void *parameter) memcpy(RxBuffTemp, EthRxBuff, EthRxLen); RxBuffTemp[EthRxLen] = 0; memset(HexData, 0x00, EthRxLen / 2); - uint8_t ret = AsciiToHex(RxBuffTemp, HexData, EthRxLen); + uint16_t ret = AsciiToHex(RxBuffTemp, HexData, EthRxLen); if(ret > 0 && HexData[0] == 0x7A) { Eth.SendNoDataCnt = 0; if(GateWay->ConfigPara.Much == CH_ETH) diff --git a/Project/GateWay/source/User/Src/Public.c b/Project/GateWay/source/User/Src/Public.c index 672074f..035b292 100644 --- a/Project/GateWay/source/User/Src/Public.c +++ b/Project/GateWay/source/User/Src/Public.c @@ -126,7 +126,7 @@ void HexToAscii(uint8_t *HexData, char *ASCData, uint8_t sLen) sLen, 需要转的字符串长度 * 返 回 值: 无 *****************************************************************************************/ -uint8_t AsciiToHex(char *ASCData, uint8_t *HexData, uint8_t sLen) +uint16_t AsciiToHex(char *ASCData, uint8_t *HexData, uint16_t sLen) { char chr; uint8_t HexLen = 0; diff --git a/Project/GateWay/source/User/Src/Update.c b/Project/GateWay/source/User/Src/Update.c index 3f17192..4e9250c 100644 --- a/Project/GateWay/source/User/Src/Update.c +++ b/Project/GateWay/source/User/Src/Update.c @@ -64,4 +64,361 @@ void UpdateInit(void) } } +/***************************************************************************************** +* 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; + +/*软件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 NetUpgradeSaveProg(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; + if(Nup.TxCnt >= 4) { + return; //队列满(正常时序最多2帧, 满则丢弃) + } + 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++; +} + +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; + uint32_t Resumed = 0; + 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; + } + if(Nup.Active && Nup.AppSize == Req.AppSize && Nup.Crc32 == Req.AppCrc32) { + Resumed = Nup.ExpectIdx; //同会话重复0x81: 内存进度优先 + } + else { + FwStashProg_t Prog; + FwStashProgRead((uint8_t *)&Prog, sizeof(Prog)); + if(Prog.Magic == NUP_STASH_PROG_MAGIC && Prog.AppSize == Req.AppSize + && Prog.Crc32 == Req.AppCrc32 && Prog.PkgCnt > 0 && Prog.PkgCnt < Req.PackageNum) { + Resumed = Prog.PkgCnt; //断电/断连后断点续传 + } + else { + FwStashEraseAll(); //全新升级: 擦暂存区 + NetUpgradeSaveProg(0); + } + Nup.Active = true; + Nup.PkgCnt = Req.PackageNum; + Nup.AppSize = Req.AppSize; + Nup.Crc32 = Req.AppCrc32; + Nup.ExpectIdx = Resumed; + Nup.ErrCnt = 0; + Nup.LastTick = rt_tick_get(); + } + Nup.LastAckIdx = Resumed; + U_DBG_LOG("NetUpgrade req: Pkg=%d Size=%d Crc=0x%08x Resumed=%d\r\n", + Nup.PkgCnt, Nup.AppSize, Nup.Crc32, Resumed); + NetUpgradeSendAck(Resumed > 0 ? 0x21 : 0x20, Resumed); + NetUpgradeSendReq(Resumed); //ACK后主动请求首包, 服务端收到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; + } + 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"); + FwStashEraseAll(); //校验失败: 擦暂存从头重下 + NetUpgradeSaveProg(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) { + NetUpgradeSaveProg(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) { + 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) +{ + if(Nup.TxCnt == 0) { + return false; + } + memcpy(Out, Nup.TxQ[Nup.TxHead], Nup.TxQLen[Nup.TxHead]); + *OutLen = Nup.TxQLen[Nup.TxHead]; + Nup.TxHead = (Nup.TxHead + 1) % 4; + Nup.TxCnt--; + return true; +} + +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; + + 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(); +} diff --git a/Project/GateWay/source/User/Src/bsp.c b/Project/GateWay/source/User/Src/bsp.c index b2ac7f7..9f52364 100644 --- a/Project/GateWay/source/User/Src/bsp.c +++ b/Project/GateWay/source/User/Src/bsp.c @@ -400,9 +400,14 @@ static void RS485Ch1UsartErrIrqCallback(void) * 参 数: 无 * 返 回 值: 无 *****************************************************************************************/ +static volatile bool RS485Ch1TxFlag = false; /*RS485Ch1发送期间置位: RX中断丢弃回环自收字节*/ + static void RS485Ch1UsartRxIrqCallback(void) { uint8_t rData = RS485Ch1UartRec(); + if(RS485Ch1TxFlag) { + return; /*发送回环自收字节丢弃*/ + } RS485Ch1RxIrqCallback(rData); } @@ -502,11 +507,13 @@ void RS485Ch1_Config(uint32_t BaudRate) void RS485Ch1UartSend(uint8_t *sData, uint16_t sLen) { RS485_CH1_TX(); + RS485Ch1TxFlag = true; for(int i = 0; i < sLen; i++) { USART_SendData(RS485_CH1_USART_CH, sData[i]); while(USART_GetStatus(RS485_CH1_USART_CH, UsartTxComplete) == Reset); } RS485_CH1_RX(); + RS485Ch1TxFlag = false; } /***************************************************************************************** diff --git a/Project/GateWay/source/User/Src/main.c b/Project/GateWay/source/User/Src/main.c index 383398a..dfdb1bd 100644 --- a/Project/GateWay/source/User/Src/main.c +++ b/Project/GateWay/source/User/Src/main.c @@ -285,10 +285,10 @@ void GateWayInit(void) } else rt_kprintf("History Num: 0\r\n"); - bool cat1Active = ChannelIsActive(GateWay.ConfigPara, CH_CAT1); + /*USART1专用Cat1: 上电恒115200+CAT1方向, 调试输出统一走RS485 Ch1(见rt_hw_console_output)*/ bool ethActive = ChannelIsActive(GateWay.ConfigPara, CH_ETH); - if(cat1Active) { DbgOrCat1Uart_Config(115200); CAT1_ON(); } - else { DBG_ON(); DbgOrCat1Uart_Config(500000); } + DbgOrCat1Uart_Config(115200); + CAT1_ON(); if(ethActive) { ETH_ON(); rt_thread_delay(1000); ETHReset(); } } diff --git a/Project/GateWay/source/User/Src/spiflash.c b/Project/GateWay/source/User/Src/spiflash.c index 1f21e00..b705eea 100644 --- a/Project/GateWay/source/User/Src/spiflash.c +++ b/Project/GateWay/source/User/Src/spiflash.c @@ -323,7 +323,7 @@ void CheckDelSecter(uint32_t wAddr, uint16_t wLen) uint32_t DelSectorNum = 0; uint32_t SurplusSpace = SPIFLASH_SECTORSIZE - (wAddr % SPIFLASH_SECTORSIZE); - if((wAddr + wLen) > SPIFLASH_SIZE) { + if((wAddr + wLen) > LOG_SAV_END_ADDR) { DelOffsetAddr = LOG_SAV_START_SECTOR * SPIFLASH_SECTORSIZE; SetLastLogEndAddr(DelOffsetAddr + wLen); SetLoopSavFlag(1); @@ -342,7 +342,7 @@ void CheckDelSecter(uint32_t wAddr, uint16_t wLen) SpiFlashEraseSector(DelOffsetAddr); DelOffsetAddr += SPIFLASH_SECTORSIZE; } - if(DelOffsetAddr == SPIFLASH_SIZE) { + if(DelOffsetAddr == LOG_SAV_END_ADDR) { DelOffsetAddr = LOG_SAV_START_SECTOR * SPIFLASH_SECTORSIZE; } FindFirstLogAddr = DelOffsetAddr; @@ -363,7 +363,7 @@ void CheckDelSecter(uint32_t wAddr, uint16_t wLen) } if(i == LOG_SAV_SIZE_MAX) { ReadAddr += LOG_SAV_SIZE_MAX; - if(ReadAddr >= SPIFLASH_SIZE) { //找不到下一条数据,重置flash + if(ReadAddr >= LOG_SAV_END_ADDR) { //找不到下一条数据,重置flash Debug_Printf("Add LOG Error, Next Data No Find!\r\n"); SetLogNum(0); SetLoopSavFlag(0); @@ -407,7 +407,7 @@ void AddLog(uint8_t *wData, uint32_t wLen) //判断是否跨扇区,跨扇区需要先擦除下一扇区 CheckDelSecter(LLEndAddr, LogSize); - if(LogH->LogEndAddr > SPIFLASH_SIZE) { + if(LogH->LogEndAddr > LOG_SAV_END_ADDR) { LLEndAddr = LOG_SAV_START_SECTOR * SPIFLASH_SECTORSIZE; LogH->LogEndAddr = ReadLastLogEndAddr(); } @@ -419,6 +419,61 @@ void AddLog(uint8_t *wData, uint32_t wLen) } +/*升级进度记录读写(扇区4037): 记录AppSize/Crc32/已收包数, 断电断连后支持断点续传*/ +void FwStashProgRead(uint8_t *rData, uint16_t rLen) +{ + FlashLock(); + SpiFlashReadAnyLengthData(rData, FLASH_FW_STASH_PROG_SECTOR * SPIFLASH_SECTORSIZE, rLen); + FlashUnlock(); +} + +void FwStashProgWrite(uint8_t *wData, uint16_t wLen) +{ + FlashLock(); + SpiFlashEraseSector(FLASH_FW_STASH_PROG_SECTOR * SPIFLASH_SECTORSIZE); + SpiFlashWriteAnyLengthData(wData, FLASH_FW_STASH_PROG_SECTOR * SPIFLASH_SECTORSIZE, wLen); + FlashUnlock(); +} + +/*固件升级暂存区操作: 内部带互斥, 避免与AddLog/WritePara的SPI事务交错*/ +void FwStashEraseAll(void) +{ + FlashLock(); + for(int i = 0; i < FLASH_FW_STASH_SECTORS; i++) { + SpiFlashEraseSector(FLASH_FW_STASH_SECTOR * SPIFLASH_SECTORSIZE + i * SPIFLASH_SECTORSIZE); + } + FlashUnlock(); +} + +void FwStashEraseSector(uint32_t SectorIdx) +{ + FlashLock(); + SpiFlashEraseSector(FLASH_FW_STASH_SECTOR * SPIFLASH_SECTORSIZE + SectorIdx * SPIFLASH_SECTORSIZE); + FlashUnlock(); +} + +int FwStashWrite(uint8_t *wData, uint32_t Offset, uint16_t wLen) +{ + if(Offset + wLen > FLASH_FW_STASH_SECTORS * SPIFLASH_SECTORSIZE) { + return -1; + } + FlashLock(); + SpiFlashWriteAnyLengthData(wData, FLASH_FW_STASH_SECTOR * SPIFLASH_SECTORSIZE + Offset, wLen); + FlashUnlock(); + return 0; +} + +int FwStashRead(uint8_t *rData, uint32_t Offset, uint16_t rLen) +{ + if(Offset + rLen > FLASH_FW_STASH_SECTORS * SPIFLASH_SECTORSIZE) { + return -1; + } + FlashLock(); + SpiFlashReadAnyLengthData(rData, FLASH_FW_STASH_SECTOR * SPIFLASH_SECTORSIZE + Offset, rLen); + FlashUnlock(); + return 0; +} + /***************************************************************************************** * 函数名称: ReadLog * 功能描述: 读取日志