fix(flash,upgrade): 注册应答/Flash并发/参数校验修复+升级后擦参数分区+注释统一UTF-8

- 注册应答先发后写Flash,重复注册参数无变化时跳过写入;SX1276发送前等待上一帧TxDone,防止应答帧被下一帧截断
- SPI Flash操作增加互斥锁(ReadPara/WritePara/AddLog/SavLogNum),修复多线程并发写坏参数镜像
- WritePara自行擦除参数区全部扇区并加越界保护;移除通用写函数中的日志簿记副作用(双擦+LastLogEndAddr污染)
- 开机参数加载增加CRC校验与通讯单元表语义清洗;修复忽略ReadPara返回值导致坏镜像放行并被回写洗白的问题
- RS485Send函数指针每次开机重设,不再从Flash恢复旧固件代码地址
- Boot升级完成置APP_BOOT_UPGRADE_FLAG,App启动时仅擦外部参数分区(扇区0~1,历史日志保留)并恢复标志
- 参数SavFlag绑定PARA_LAYOUT_VER布局版本戳,布局变更自动回退默认参数
- res gxjt改为仅擦参数分区,不再触发全片扫描
- 全部源码注释统一UTF-8(无BOM),修复历史GBK/UTF-8混合编码及乱码字符
- 软件版本 1.2 -> 1.3
This commit is contained in:
2026-09-03 19:00:24 +08:00
parent a6c8cd8a71
commit fe4b9fabf8
7 changed files with 158 additions and 40 deletions
+63 -7
View File
@@ -75,8 +75,19 @@ void GateWayInit(void)
boot_para_t bootParam;
dev_boot_read_param(&bootParam);
rt_kprintf("AppFlag: 0x%08x\r\n", bootParam.AppFlag);
/*升级完成后Boot会设AppFlag=APP_START_FLAG并清UpdateFlag=0,
检测到升级异常(UpdateFlag仍为APP_UPDATE_FLAG 或 非首次且AppFlag无效)时擦外置Flash*/
/*升级完成后BootAppFlag=APP_BOOT_UPGRADE_FLAG(每次升级均置位,不限首次)。
App启动时据此擦除参数分区(仅扇区0~1,非全片擦除)并恢复APP_START_FLAG
随后ReadPara读到0xFF→SavFlag无效→自动加载默认参数,传感器重新注册。
须在下方升级异常检测之前处理,避免AppFlag!=APP_START_FLAG误触发全片擦除*/
if(bootParam.AppFlag == APP_BOOT_UPGRADE_FLAG) {
rt_kprintf("Upgrade done, erasing parameter partition...\r\n");
for(uint32_t Sector = GATEWEY_PARA_SAV_SECTOR; Sector < LOG_INFO_START_SECTOR; Sector++) {
SpiFlashEraseSector(Sector * SPIFLASH_SECTORSIZE);
}
bootParam.AppFlag = APP_START_FLAG;
dev_boot_write_param(bootParam);
}
/*检测升级异常(UpdateFlag仍为APP_UPDATE_FLAG 或 非首次且AppFlag无效)时擦外置Flash*/
if(bootParam.UpdateFlag == APP_UPDATE_FLAG
|| (bootParam.FirstRunFlag == 0 && bootParam.AppFlag != APP_START_FLAG)) {
rt_kprintf("Boot upgrade detected, erasing external Flash...\r\n");
@@ -85,8 +96,21 @@ void GateWayInit(void)
dev_boot_write_param(bootParam);
}
bool NeedSavePara = false;
int ret = ReadPara((uint8_t *)&GateWay.ConfigPara, sizeof(GWConfigPara_t));
if(GateWay.ConfigPara.SavFlag != LOG_SAV_FLAG) {
/*ReadPara返回-1表示CRC校验失败(参数镜像损坏)。原实现只检查SavFlag:
SavFlag位于结构体最前部,"部分损坏"的镜像(前部有效后部乱码)恰好保留合法
SavFlag,坏数据会被整体装入RAM——正是culs显示后面设备参数乱码的根源。
CRC失败必须回退默认参数,让传感器走重新注册流程*/
if(ret != 0) {
rt_kprintf("Para CRC Error, reset to default!\r\n");
}
if(ret != 0 || GateWay.ConfigPara.SavFlag != LOG_SAV_FLAG) {
/*CRC正常但SavFlag无效:flash参数为空(全新设备/曾执行res gxjt/Boot整片擦除后)
打印区分于CRC错误路径,便于现场排障判断走的是默认参数还是正常恢复*/
if(ret == 0)
rt_kprintf("Para empty, load default!\r\n");
NeedSavePara = true;
memset((uint8_t *)&GateWay.ConfigPara, 0x00, sizeof(GWConfigPara_t));
GateWay.ConfigPara.SavFlag = LOG_SAV_FLAG;
/*通道/服务器等默认参数全部从Boot参数区(BootPara_t)获取, 由Boot直接配置*/
@@ -111,9 +135,7 @@ void GateWayInit(void)
GateWay.ConfigPara.Rs485Ch2.UpgradeEnable = false;
GateWay.ConfigPara.Rs485Ch2.CommUnitEnable = false;
GateWay.ConfigPara.Rs485Ch2.Power = false;
GateWay.ConfigPara.Rs485Ch1.RS485Send = RS485Ch1UartSend;
GateWay.ConfigPara.Rs485Ch2.RS485Send = RS485Ch2UartSend;
GateWay.ConfigPara.Lora.OnOff = true;
GateWay.ConfigPara.Lora.ucChannel = 8;
GateWay.ConfigPara.Lora.ucPower = 20;
@@ -125,6 +147,36 @@ void GateWayInit(void)
GateWay.ConfigPara.OutageFlag = false;
}
else {
/*防御被"洗白"的脏数据(镜像部分损坏但CRC合法):对通讯单元表做语义校验,
清除无效条目,避免乱码条目占用注册槽位导致新设备注册不上*/
for(int i = 0; i < COMMUNIT_NUM_MAX; i++) {
CommUnitPara_t *CU = &GateWay.ConfigPara.CommUnitArray[i];
uint8_t RegByte;
memcpy(&RegByte, &CU->RegFlag, 1); //绕过编译器对bool取值0/1的假定,读取原始字节
bool EntryValid = (RegByte == 0 || RegByte == 1);
if(EntryValid && RegByte == 1) {
bool MacAllFF = true, MacAll00 = true;
for(int j = 0; j < 6; j++) {
if(CU->Mac[0][j] != 0xFF) MacAllFF = false;
if(CU->Mac[0][j] != 0x00) MacAll00 = false;
}
if(CU->SensorN == 0 || CU->SensorN > SENSOR_NUM_MAX || MacAllFF || MacAll00)
EntryValid = false;
}
if(!EntryValid) {
memset(CU, 0x00, sizeof(CommUnitPara_t));
NeedSavePara = true;
}
}
if(NeedSavePara)
rt_kprintf("CommUnit table dirty, cleaned!\r\n");
}
/*函数指针不允许从Flash恢复:正常加载路径下RS485Send来自旧固件写入Flash的
代码地址,固件升级后代码布局变化会使该指针失效(野指针)。每次开机统一重设*/
GateWay.ConfigPara.Rs485Ch1.RS485Send = RS485Ch1UartSend;
GateWay.ConfigPara.Rs485Ch2.RS485Send = RS485Ch2UartSend;
/*网关MAC编码从Boot参数区(BootPara_t.GwMac)获取, 由Boot从固定地址0x4000同步
(协议规定: Mac[0]=VerInfo, Mac[1:3]=PrjNum, Mac[4:5]=GwNum)*/
@@ -212,7 +264,11 @@ void GateWayInit(void)
//WritePara((uint8_t *)&GateWay.ConfigPara, sizeof(GWConfigPara_t));
}
WritePara((uint8_t *)&GateWay.ConfigPara, sizeof(GWConfigPara_t));
/*原实现开机无条件回写参数:一方面无谓增加一次擦写磨损;另一方面若读出的
是坏数据,回写会重新计算CRC把脏数据"洗白"成永久合法,无法自愈。
仅在参数被重置或清洗过时才落盘*/
if(NeedSavePara)
WritePara((uint8_t *)&GateWay.ConfigPara, sizeof(GWConfigPara_t));
LogInit();