diff --git a/Project/GateWay/source/Module/GateWay_Debug/DebugCmd.c b/Project/GateWay/source/Module/GateWay_Debug/DebugCmd.c index 8c68217..835f0d1 100644 --- a/Project/GateWay/source/Module/GateWay_Debug/DebugCmd.c +++ b/Project/GateWay/source/Module/GateWay_Debug/DebugCmd.c @@ -1029,9 +1029,11 @@ void DebugCmdRestoreFactory(int argc, char *argv[]) } DBG_LOG("Restore factory settings, GateWay Restart...\r\n"); - memset((uint8_t *)&GateWay->ConfigPara, 0x00, sizeof(GWConfigPara_t)); - WritePara((uint8_t *)&GateWay->ConfigPara, sizeof(GWConfigPara_t)); - rt_thread_delay(1000); + /*仅擦除参数分区(扇区0~1),非全片擦除;擦除后参数区全0xFF, + 重启后SavFlag无效自动加载Boot默认参数*/ + for(uint32_t Sector = GATEWEY_PARA_SAV_SECTOR; Sector < LOG_INFO_START_SECTOR; Sector++) { + SpiFlashEraseSector(Sector * SPIFLASH_SECTORSIZE); + } rt_thread_delay(1000); NVIC_SystemReset(); while(1); } @@ -1623,7 +1625,7 @@ static void DebugCmdCali(int argc, char *argv[]) TrySendFullPowerCmd(GateWay, ret); } } - else if(mac[2] == 0x02) { //RS485ͨѶ + else if(mac[2] == 0x02) { //RS485通讯 if(GateWay->ConfigPara.Rs485Ch1.Enable == true && !GateWay->ConfigPara.Rs485Ch1.CommUnitEnable) { GateWay->ConfigPara.Rs485Ch1.RS485Send(Rs485Cmd, sizeof(CommUnitFrameHeader_t) + 2); DBG_LOG("Calibrate CommUnit: %s!\r\n", argv[1]); @@ -1752,7 +1754,7 @@ static void DebugLaserConfig(int argc, char *argv[]) } } } - else if(mac[2] == 0x02) { //RS485ͨѶ + else if(mac[2] == 0x02) { //RS485通讯 if(GateWay->ConfigPara.Rs485Ch1.Enable == true && !GateWay->ConfigPara.Rs485Ch1.CommUnitEnable) { GateWay->ConfigPara.Rs485Ch1.RS485Send(Rs485Cmd, len); DBG_LOG("Control RS485 laser CommUnit: %s!\r\n", argv[1]); diff --git a/Project/GateWay/source/Module/sx127x/sx127x.c b/Project/GateWay/source/Module/sx127x/sx127x.c index 8879470..8b6a3fe 100644 --- a/Project/GateWay/source/Module/sx127x/sx127x.c +++ b/Project/GateWay/source/Module/sx127x/sx127x.c @@ -418,6 +418,14 @@ void Sx1276LoRaLoopHandler(GateWayPara GateWay) } void Sx1276LoRaSendBuffer(uint8_t* pucBuff, uint16_t ucLen) { + uint16_t TxDoneWaitCnt = 0; + /*LoRa发送是异步的(TxDone中断结束,IsrSx1276LoRaTxRx中将State恢复为IDLE)。 + 若上一帧仍在发送时再次调用本函数,下面设置STANDBY会把正在发送的帧截断, + 导致先发出的应答丢失(传感器收不到注册应答),故先等待上一帧发送完成*/ + while(LoRaPara.State == SX1276_TX && TxDoneWaitCnt < 2000) { + Ddl_Delay1ms(1); + TxDoneWaitCnt++; + } LoRaPara.State = SX1276_BUSY; if (ucLen > LORA_BUFF_SIZE){ ucLen = LORA_BUFF_SIZE; @@ -667,3 +675,4 @@ bool LoraSetFreqCent(uint32_t FreqCent) + diff --git a/Project/GateWay/source/User/Inc/Public.h b/Project/GateWay/source/User/Inc/Public.h index 404f266..d4b7edc 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" diff --git a/Project/GateWay/source/User/Inc/main.h b/Project/GateWay/source/User/Inc/main.h index b9e0b69..04e4a5f 100644 --- a/Project/GateWay/source/User/Inc/main.h +++ b/Project/GateWay/source/User/Inc/main.h @@ -4,7 +4,7 @@ #include "Public.h" #include "DebugCmd.h" -#define SOFTWARE_VERSION 12 +#define SOFTWARE_VERSION 13 #define HARDWARE_VERSION 12 extern bool MainDispEn; diff --git a/Project/GateWay/source/User/Src/Public.c b/Project/GateWay/source/User/Src/Public.c index d89cd9f..79b2fc7 100644 --- a/Project/GateWay/source/User/Src/Public.c +++ b/Project/GateWay/source/User/Src/Public.c @@ -615,22 +615,30 @@ int CommUnitAnalyze(GateWayPara GateWay, uint8_t *rData, uint16_t rLen, SendData // if(CUHeader->PayloadLen < 2) // return -1; - if(CommUnitIdx >= 0) { //设备存在 + if(CommUnitIdx >= 0) { //设备存在 MAIN_DBG_LOG("\r\nCommUnit has already been registered! Mac:%02X-%02X-%02X-%02X-%02X-%02X\r\n", CUHeader->DevMac[0], CUHeader->DevMac[1], CUHeader->DevMac[2], CUHeader->DevMac[3], CUHeader->DevMac[4], CUHeader->DevMac[5]); uint8_t ret = 1; - GateWay->ConfigPara.CommUnitArray[CommUnitIdx].CommErrCnt = 0; - GateWay->ConfigPara.CommUnitArray[CommUnitIdx].ConfigFlag = true; - GateWay->ConfigPara.CommUnitArray[CommUnitIdx].CailFlag = false; - GateWay->ConfigPara.CommUnitArray[CommUnitIdx].TimeSyncFlag = false; - GateWay->ConfigPara.CommUnitArray[CommUnitIdx].ContLaser = false; - GateWay->ConfigPara.CommUnitArray[CommUnitIdx].LaserOnOff = false; - GateWay->ConfigPara.CommUnitArray[CommUnitIdx].FPOTimeFlag = false; - GateWay->ConfigPara.CommUnitArray[CommUnitIdx].FPOTimeConfirmed = true; - WritePara((uint8_t *)&GateWay->ConfigPara, sizeof(GWConfigPara_t)); - CommUnitCmdSend(GateWay, CUHeader->DevMac, COMM_UNIT_CMD_REG, &ret, 1, Response); - return CommUnitIdx; - } + CommUnitPara_t *pCU = &GateWay->ConfigPara.CommUnitArray[CommUnitIdx]; + bool ParaChanged = (pCU->CommErrCnt != 0 || pCU->ConfigFlag != true || pCU->CailFlag != false || + pCU->TimeSyncFlag != false || pCU->ContLaser != false || pCU->LaserOnOff != false || + pCU->FPOTimeFlag != false || pCU->FPOTimeConfirmed != true); + pCU->CommErrCnt = 0; + pCU->ConfigFlag = true; + pCU->CailFlag = false; + pCU->TimeSyncFlag = false; + pCU->ContLaser = false; + pCU->LaserOnOff = false; + pCU->FPOTimeFlag = false; + pCU->FPOTimeConfirmed = true; + /*先发应答再写Flash:WritePara需擦除2个扇区并写入约7KB参数(耗时百毫秒级), + 若先写Flash,应答延迟过大,会超出传感器注册请求后的接收窗口导致其收不到回应; + 参数无变化时跳过写入,避免传感器反复重注册时频繁擦写Flash*/ + CommUnitCmdSend(GateWay, CUHeader->DevMac, COMM_UNIT_CMD_REG, &ret, 1, Response); + if(ParaChanged) + WritePara((uint8_t *)&GateWay->ConfigPara, sizeof(GWConfigPara_t)); + return CommUnitIdx; + } } for(int i = 0; i < COMMUNIT_NUM_MAX; i++) { if(GateWay->ConfigPara.CommUnitArray[i].RegFlag == false) { CommUnitIdx = i; @@ -668,11 +676,11 @@ int CommUnitAnalyze(GateWayPara GateWay, uint8_t *rData, uint16_t rLen, SendData GateWay->ConfigPara.CommUnitArray[i].CurFPOTimeStart = LW_DEV_FPO_TIME_START_DEFAULT; GateWay->ConfigPara.CommUnitArray[i].CurFPOTime = LW_DEV_FPO_TIME_DEFAULT; GateWay->ConfigPara.CommUnitArray[i].FPOTimeFlag = false; - GateWay->ConfigPara.CommUnitArray[i].FPOTimeConfirmed = true; - WritePara((uint8_t *)&GateWay->ConfigPara, sizeof(GWConfigPara_t)); + GateWay->ConfigPara.CommUnitArray[i].FPOTimeConfirmed = true; uint8_t ret = 1; + //先发应答再写Flash,避免WritePara耗时导致应答延迟过大(原因同上) CommUnitCmdSend(GateWay, CUHeader->DevMac, COMM_UNIT_CMD_REG, &ret, 1, Response); - + WritePara((uint8_t *)&GateWay->ConfigPara, sizeof(GWConfigPara_t)); if(GateWay->MuchRegFlag) { //网关已注册,向服务发送新设备添加指令 PayLoadLen = (GateWay->ConfigPara.CommUnitArray[i].SensorN * 6) + 2; MegData = rt_malloc(PayLoadLen + 3); @@ -1440,3 +1448,4 @@ int AuchRevCallBack(GateWayPara GateWay, uint8_t *rData, uint16_t rLen) { return _NetRevCallBack(GateWay, rData, rLen, &GateWay->AuchRegFlag); } + diff --git a/Project/GateWay/source/User/Src/main.c b/Project/GateWay/source/User/Src/main.c index f4cf97f..4f351fd 100644 --- a/Project/GateWay/source/User/Src/main.c +++ b/Project/GateWay/source/User/Src/main.c @@ -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*/ + /*升级完成后Boot置AppFlag=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(); diff --git a/Project/GateWay/source/User/Src/spiflash.c b/Project/GateWay/source/User/Src/spiflash.c index e9326d8..1f21e00 100644 --- a/Project/GateWay/source/User/Src/spiflash.c +++ b/Project/GateWay/source/User/Src/spiflash.c @@ -9,6 +9,28 @@ uint8_t flashtestbuff[SPIFLASH_SIZE]; #endif +/*SPI Flash操作互斥保护:ReadPara/WritePara/AddLog/SavLogNum会被多个线程 + (Lora/RS485/Cat1/Eth/调试/主线程)并发调用,无保护时SPI事务交错会写坏参数镜像 + (传感器注册风暴与网关向服务器注册并发写Flash即触发此问题)。 + RT-Thread互斥量支持同线程递归持有(AddLog内调SavLogNum不会死锁)*/ +static rt_mutex_t FlashMutex = RT_NULL; +static void FlashLock(void) +{ + if(FlashMutex == RT_NULL) { + rt_enter_critical(); + if(FlashMutex == RT_NULL) + FlashMutex = rt_mutex_create("flsmux", RT_IPC_FLAG_PRIO); + rt_exit_critical(); + } + if(FlashMutex != RT_NULL) + rt_mutex_take(FlashMutex, RT_WAITING_FOREVER); +} +static void FlashUnlock(void) +{ + if(FlashMutex != RT_NULL) + rt_mutex_release(FlashMutex); +} + static uint8_t SpiFlashReadByte(void) { return Spi1SendReceive(0); @@ -107,7 +129,7 @@ void SpiFlashWriteAnyLengthData(uint8_t* wData, uint32_t wAddr, uint16_t wLen) uint16_t FirstPageRemainSpace = SPIFLASH_PAGESIZE - (WriteAddr % SPIFLASH_PAGESIZE); // //判断是否跨扇区,跨扇区需要先擦除下一扇区 - CheckDelSecter(wAddr, wLen); +// CheckDelSecter(wAddr, wLen); if(wLen < FirstPageRemainSpace) { FirstPageWriteSize = wLen; //计算首页写入长度 @@ -260,7 +282,8 @@ void LogInit(void) void SavLogNum(uint32_t LogNum) { LogNumSav_t CLogSav; - + + FlashLock(); if(LogNum == 0) { LogNumIdx = 0; SetLogNum(0); @@ -268,21 +291,21 @@ void SavLogNum(uint32_t LogNum) SetFirstLogStartAddr(LOG_SAV_START_SECTOR * SPIFLASH_SECTORSIZE); SetLastLogEndAddr(LOG_SAV_START_SECTOR * SPIFLASH_SECTORSIZE); SpiFlashEraseSector(LOG_INFO_START_SECTOR * SPIFLASH_SECTORSIZE); + FlashUnlock(); return; } - SetLogNum(LogNum); CLogSav.Flag = LOG_INFO_SAV_FLAG; CLogSav.LogCnt = LogNum; CLogSav.FirstLogStartAddr = ReadFirstLogStartAddr(); CLogSav.LastLogEndAddr = ReadLastLogEndAddr(); - if(LogNumIdx == 256) { SpiFlashEraseSector(LOG_INFO_START_SECTOR * SPIFLASH_SECTORSIZE); LogNumIdx = 0; } SpiFlashWriteData((uint8_t *)&CLogSav, LogNumIdx * sizeof(LogNumSav_t) + LOG_INFO_START_SECTOR * SPIFLASH_SECTORSIZE, sizeof(LogNumSav_t)); LogNumIdx++; + FlashUnlock(); } /***************************************************************************************** @@ -363,6 +386,7 @@ void AddLog(uint8_t *wData, uint32_t wLen) // uint32_t SectorN; LogHeader LogH; + FlashLock(); LogH = (LogHeader)LogBuffTemp; LogNum = ReadLogNum(); @@ -390,6 +414,7 @@ void AddLog(uint8_t *wData, uint32_t wLen) SpiFlashWriteAnyLengthData(LogBuffTemp, LLEndAddr, LogSize); SavLogNum(LogNum); + FlashUnlock(); //Debug_Printf("Add LOG: %d,endaddr = %d\r\n", LogNum, ReadLastLogEndAddr()); } @@ -468,8 +493,10 @@ int ReadLog(LogHeader Header, uint8_t **rData, uint32_t LogIdx) * 返 回 值: 无 *****************************************************************************************/ int ReadPara(uint8_t *Para, uint32_t ParaLen) -{ - SpiFlashReadAnyLengthData(Para, GATEWEY_PARA_SAV_ADDR, ParaLen); //读取片尾数据 +{ + FlashLock(); + SpiFlashReadAnyLengthData(Para, GATEWEY_PARA_SAV_ADDR, ParaLen); + FlashUnlock(); uint16_t Check = CRC_Modbus(CRC16_BASE, Para, ParaLen - 2); GWConfigPara CfgPara = (GWConfigPara)Para; if(CfgPara->crc16 != Check) @@ -485,14 +512,29 @@ int ReadPara(uint8_t *Para, uint32_t ParaLen) * 返 回 值: 无 *****************************************************************************************/ int WritePara(uint8_t *Para, uint32_t ParaLen) -{ - SpiFlashEraseSector(GATEWEY_PARA_SAV_ADDR); - +{ + FlashLock(); + /*参数区只能占用扇区0~1(8KB),扇区2为日志信息区(LogNumArray)。 + 参数越界会擦除日志簿记数据,此处直接拒绝写入*/ + if(GATEWEY_PARA_SAV_ADDR + ParaLen > LOG_INFO_START_SECTOR * SPIFLASH_SECTORSIZE) { + FlashUnlock(); + return -1; + } uint16_t Check = CRC_Modbus(CRC16_BASE, Para, ParaLen - 2); GWConfigPara CfgPara = (GWConfigPara)Para; CfgPara->crc16 = Check; - SpiFlashWriteAnyLengthData(Para, GATEWEY_PARA_SAV_ADDR, ParaLen); //读取片尾数据 + /*擦除参数区覆盖的所有扇区(当前GWConfigPara_t约7.5KB,跨扇区0和1)。 + 原实现只擦扇区0,跨扇区部分依赖SpiFlashWriteAnyLengthData内部的 + CheckDelSecter擦除,该函数会顺带改写日志系统的LastLogEndAddr等簿记 + 变量并可能误擦日志信息区,现已移除该调用,扇区擦除由本函数自行完成*/ + uint32_t SectorCnt = (GATEWEY_PARA_SAV_ADDR + ParaLen + SPIFLASH_SECTORSIZE - 1) / SPIFLASH_SECTORSIZE; + for(uint32_t i = 0; i < SectorCnt; i++) { + SpiFlashEraseSector((GATEWEY_PARA_SAV_SECTOR + i) * SPIFLASH_SECTORSIZE); + } + SpiFlashWriteAnyLengthData(Para, GATEWEY_PARA_SAV_ADDR, ParaLen); + FlashUnlock(); return 0; } /* USER CODE END */ +