From 48b789c0c49c8d13204b8166e73ae3c83c2ac397 Mon Sep 17 00:00:00 2001 From: YuanHongbin <975559679@qq.com> Date: Thu, 24 Sep 2026 17:14:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(ota):=20Boot=E6=94=AF=E6=8C=81=E5=A4=96?= =?UTF-8?q?=E9=83=A8Flash=E6=9A=82=E5=AD=98=E6=90=AC=E8=BF=90(BL=5FSTASH?= =?UTF-8?q?=5FTO=5FB=E7=8A=B6=E6=80=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - bsp.c/h: 外部SPI Flash只读驱动(SPI1, 与App同总线Mode3) - main.c/h: BL_STASH_TO_B状态: AppSize门禁 -> 外部Flash逐8KB搬运B区 -> CRC校验 -> 进BL_COPY_TO_A复用现有搬运链 - 搬运幂等: 搬运中断电重启后B区仍有效自动重搬 --- source/User/Inc/bsp.h | 24 +++++++++++- source/User/Inc/main.h | 2 + source/User/Src/bsp.c | 88 ++++++++++++++++++++++++++++++++++++++++++ source/User/Src/main.c | 64 ++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 1 deletion(-) diff --git a/source/User/Inc/bsp.h b/source/User/Inc/bsp.h index 89cfdc4..66cc637 100644 --- a/source/User/Inc/bsp.h +++ b/source/User/Inc/bsp.h @@ -206,7 +206,27 @@ typedef enum { #define APP_B_ADDRESS (APP_A_ADDRESS + APP_BANK_SIZE) //0x42000 B区: 升级暂存 #define APP_ADDRESS APP_A_ADDRESS //兼容旧名, 指向A区 #define BOOT_PARA_ADDRESS (APP_B_ADDRESS + APP_BANK_SIZE) //0x7C000 -#define BOOT_PARA_SIZE (FLASH_SECTOR_SIZE) +#define BOOT_PARA_SIZE (FLASH_SECTOR_SIZE) + +/*外部SPI Flash(与App共用SPI1: PC0-NSS/PC1-MISO/PC2-SCK/PC3-MOSI, Mode3) - Cat1远程升级固件暂存区*/ +#define EXTFLASH_NSS_PORT (PortC) +#define EXTFLASH_NSS_PIN (Pin00) +#define EXTFLASH_NSS_SET() PORT_SetBits(EXTFLASH_NSS_PORT, EXTFLASH_NSS_PIN) +#define EXTFLASH_NSS_CLR() PORT_ResetBits(EXTFLASH_NSS_PORT, EXTFLASH_NSS_PIN) +#define EXTFLASH_SCK_PORT (PortC) +#define EXTFLASH_SCK_PIN (Pin02) +#define EXTFLASH_SCK_FUNC (Func_Spi1_Sck) +#define EXTFLASH_MOSI_PORT (PortC) +#define EXTFLASH_MOSI_PIN (Pin03) +#define EXTFLASH_MOSI_FUNC (Func_Spi1_Mosi) +#define EXTFLASH_MISO_PORT (PortC) +#define EXTFLASH_MISO_PIN (Pin01) +#define EXTFLASH_MISO_FUNC (Func_Spi1_Miso) +#define EXTFLASH_UNIT (M4_SPI1) +#define EXTFLASH_UNIT_CLOCK (PWC_FCG1_PERIPH_SPI1) +#define EXTFLASH_CMD_READ 0x03 +#define SPIFLASH_SECTORSIZE 4096ul +#define FLASH_FW_STASH_SECTOR 4038 //暂存区起始扇区(与App spiflash.h一致) #define DEBUG 1 #define USE_SD_CARD 0 @@ -307,6 +327,8 @@ void Error_Handler(void); /*Flash操作*/ void FlashWrite(uint32_t Addr, uint32_t *wData, int wLen); +void ExtFlashInit(void); +void ExtFlashRead(uint32_t Addr, uint8_t *rData, uint16_t rLen); void ReadBootPara(BootPara rData); void WriteBootPara(BootPara wData); void EraseFlash(uint32_t BaseAddr, uint32_t SectorCnt); diff --git a/source/User/Inc/main.h b/source/User/Inc/main.h index 550e074..be8f3c6 100644 --- a/source/User/Inc/main.h +++ b/source/User/Inc/main.h @@ -7,6 +7,7 @@ #define APP_START_FLAG 0x55AA5A5A #define APP_UPDATE_FLAG 0xA5A5AA55 #define APP_BOOT_UPGRADE_FLAG 0x5A5A55A5 //升级完成标记:Boot升级成功后置位,App启动时据此擦除外部参数分区并恢复APP_START_FLAG(与App侧bsp.h定义保持一致) +#define APP_UPDATE_SPI_FLAG 0xA5A55A5A //Cat1远程升级: 固件已存外部SPI Flash暂存区, Boot据此搬运(与App侧bsp.h定义保持一致) typedef enum { UPDATE_CMD_NULL, @@ -53,6 +54,7 @@ typedef enum { BL_GET_DATA_TIMEOUT, BL_START_APP, BL_COPY_TO_A, //B区镜像搬运到A区阶段 + BL_STASH_TO_B, //外部SPI Flash暂存区搬运到B区阶段(Cat1远程升级) BL_RESET }BootStatus_m; diff --git a/source/User/Src/bsp.c b/source/User/Src/bsp.c index aa9d081..27590c3 100644 --- a/source/User/Src/bsp.c +++ b/source/User/Src/bsp.c @@ -393,6 +393,7 @@ void BSP_Init(void) SystemClockConfig(); GPIO_Config(); RS485Ch1_Config(500000); + ExtFlashInit(); /*外部SPI Flash: Cat1远程升级暂存区读取用*/ FeedDog(); SysTick_Init(1000); @@ -422,6 +423,93 @@ static void FlashRead(uint32_t u32Addr, uint32_t *rData, int rLen) } } +/*外部SPI Flash驱动: 供Cat1远程升级时读取暂存区固件(App负责写入暂存区)*/ +static uint16_t ExtSpiSendReceive(uint16_t sData) +{ + uint16_t retry = 0; + uint16_t rData; + + while(Reset == SPI_GetFlag(EXTFLASH_UNIT, SpiFlagSendBufferEmpty)) { + retry++; + if(retry > 0x4000) { + return 0; + } + } + SPI_SendData8(EXTFLASH_UNIT, sData); + retry = 0; + while(Reset == SPI_GetFlag(EXTFLASH_UNIT, SpiFlagReceiveBufferFull)) { + retry++; + if(retry > 0x4000) { + return 0; + } + } + rData = SPI_ReceiveData8(EXTFLASH_UNIT); + return rData; +} + +void ExtFlashInit(void) +{ + stc_spi_init_t stcSpiInit; + stc_port_init_t stcPortInit; + + MEM_ZERO_STRUCT(stcPortInit); + stcPortInit.enPinOType = Pin_OType_Cmos; + PORT_Init(EXTFLASH_SCK_PORT, EXTFLASH_SCK_PIN, &stcPortInit); + PORT_Init(EXTFLASH_MOSI_PORT, EXTFLASH_MOSI_PIN, &stcPortInit); + PORT_Init(EXTFLASH_MISO_PORT, EXTFLASH_MISO_PIN, &stcPortInit); + stcPortInit.enPinMode = Pin_Mode_Out; + stcPortInit.enPinOType = Pin_OType_Cmos; + stcPortInit.enPinDrv = Pin_Drv_H; + stcPortInit.enPullUp = Disable; + PORT_Init(EXTFLASH_NSS_PORT, EXTFLASH_NSS_PIN, &stcPortInit); + EXTFLASH_NSS_SET(); + + MEM_ZERO_STRUCT(stcSpiInit); + PWC_Fcg1PeriphClockCmd(EXTFLASH_UNIT_CLOCK, Enable); + PORT_SetFunc(EXTFLASH_SCK_PORT, EXTFLASH_SCK_PIN, EXTFLASH_SCK_FUNC, Disable); + PORT_SetFunc(EXTFLASH_MOSI_PORT, EXTFLASH_MOSI_PIN, EXTFLASH_MOSI_FUNC, Disable); + PORT_SetFunc(EXTFLASH_MISO_PORT, EXTFLASH_MISO_PIN, EXTFLASH_MISO_FUNC, Disable); + + stcSpiInit.enClkDiv = SpiClkDiv16; + stcSpiInit.enFrameNumber = SpiFrameNumber1; + stcSpiInit.enDataLength = SpiDataLengthBit8; + stcSpiInit.enFirstBitPosition = SpiFirstBitPositionMSB; + stcSpiInit.enSckPolarity = SpiSckIdleLevelHigh; + stcSpiInit.enSckPhase = SpiSckOddChangeEvenSample; + stcSpiInit.enReadBufferObject = SpiReadReceiverBuffer; + stcSpiInit.enWorkMode = SpiWorkMode4Line; + stcSpiInit.enTransMode = SpiTransFullDuplex; + stcSpiInit.enCommAutoSuspendEn = Disable; + stcSpiInit.enModeFaultErrorDetectEn = Disable; + stcSpiInit.enParitySelfDetectEn = Disable; + stcSpiInit.enParityEn = Disable; + stcSpiInit.enParity = SpiParityEven; + stcSpiInit.enMasterSlaveMode = SpiModeMaster; + stcSpiInit.stcDelayConfig.enSsSetupDelayOption = SpiSsSetupDelayCustomValue; + stcSpiInit.stcDelayConfig.enSsSetupDelayTime = SpiSsSetupDelaySck1; + stcSpiInit.stcDelayConfig.enSsHoldDelayOption = SpiSsHoldDelayCustomValue; + stcSpiInit.stcDelayConfig.enSsHoldDelayTime = SpiSsHoldDelaySck1; + stcSpiInit.stcDelayConfig.enSsIntervalTimeOption = SpiSsIntervalCustomValue; + stcSpiInit.stcDelayConfig.enSsIntervalTime = SpiSsIntervalSck6PlusPck2; + stcSpiInit.stcSsConfig.enSsValidBit = SpiSsValidChannel0; + stcSpiInit.stcSsConfig.enSs0Polarity = SpiSsLowValid; + SPI_Init(EXTFLASH_UNIT, &stcSpiInit); + SPI_Cmd(EXTFLASH_UNIT, Enable); +} + +void ExtFlashRead(uint32_t Addr, uint8_t *rData, uint16_t rLen) +{ + EXTFLASH_NSS_CLR(); + ExtSpiSendReceive(0x03); + ExtSpiSendReceive((Addr >> 16) & 0xFF); + ExtSpiSendReceive((Addr >> 8) & 0xFF); + ExtSpiSendReceive(Addr & 0xFF); + for(uint16_t i = 0; i < rLen; i++) { + rData[i] = ExtSpiSendReceive(0xFF); + } + EXTFLASH_NSS_SET(); +} + void ReadBootPara(BootPara rData) { FlashRead(BOOT_PARA_ADDRESS, (uint32_t *)rData, sizeof(BootPara_t)); diff --git a/source/User/Src/main.c b/source/User/Src/main.c index c038720..08401ed 100644 --- a/source/User/Src/main.c +++ b/source/User/Src/main.c @@ -164,6 +164,40 @@ static bool CopyAppBtoA(void) return (GetCrc32(APP_A_ADDRESS, BL.Para.AppSize) == BL.Para.Crc32Check); } +static uint32_t s_StashBuf[FLASH_SECTOR_SIZE / 4]; /*外部Flash读缓冲(8KB)*/ + +/***************************************************************************************** +* 函数名称: StashToB +* 功能描述: Cat1远程升级: 将外部SPI Flash暂存区固件搬运到B区(逐8KB: 读外部2×4KB→擦写B区1×8KB) +* 参 数: 无 +* 返 回 值: 无(搬运后由调用方CRC32校验B区) +*****************************************************************************************/ +static void StashToB(void) +{ + uint32_t TotalWords = (BL.Para.AppSize + 3) / 4; /*实际固件字数*/ + uint32_t SecPerBank = FLASH_SECTOR_SIZE / 4; + uint32_t FullSectors = TotalWords / SecPerBank; + uint32_t RemainWords = TotalWords % SecPerBank; + uint32_t Sec, Base, SrcAddr; + + for(Sec = 0; Sec < FullSectors; Sec++) { + Base = APP_B_ADDRESS + Sec * FLASH_SECTOR_SIZE; + SrcAddr = FLASH_FW_STASH_SECTOR * SPIFLASH_SECTORSIZE + Sec * FLASH_SECTOR_SIZE; + FeedDog(); + ExtFlashRead(SrcAddr, (uint8_t *)s_StashBuf, FLASH_SECTOR_SIZE); + EraseFlash(Base, 1); + FlashWrite(Base, s_StashBuf, SecPerBank); + } + if(RemainWords > 0) { + Base = APP_B_ADDRESS + FullSectors * FLASH_SECTOR_SIZE; + SrcAddr = FLASH_FW_STASH_SECTOR * SPIFLASH_SECTORSIZE + FullSectors * FLASH_SECTOR_SIZE; + FeedDog(); + ExtFlashRead(SrcAddr, (uint8_t *)s_StashBuf, RemainWords * 4); + EraseFlash(Base, 1); + FlashWrite(Base, s_StashBuf, RemainWords); + } +} + /***************************************************************************************** * 函数名称: SendResponseCmd * 功能描述: 发送响应帧 (0x7D + DevMac) @@ -438,6 +472,33 @@ void BootLoaderLoopHandler(void) } break; + case BL_STASH_TO_B: + FeedDog(); + /*固件大小非法: 无法搬运, 恢复老App(A区未动)并复位*/ + if(BL.Para.AppSize == 0 || BL.Para.AppSize > APP_BANK_SIZE) { + BL.Para.AppFlag = APP_START_FLAG; + BL.Para.UpdateFlag = 0; + WriteBootPara(&BL.Para); + BL.BootLoader1mSDlyCnt = 100; + BL.Status = BL_RESET; + break; + } + /*外部SPI Flash暂存区→B区(逐8KB), StashToB幂等: 搬运中断电重启后自动重做*/ + StashToB(); + /*B区整包校验: 通过进搬运, 失败清标志回老App*/ + if(GetCrc32(APP_B_ADDRESS, BL.Para.AppSize) == BL.Para.Crc32Check) { + BL.CopyRetryCnt = 0; + BL.Status = BL_COPY_TO_A; + } + else { + BL.Para.AppFlag = APP_START_FLAG; + BL.Para.UpdateFlag = 0; + WriteBootPara(&BL.Para); + BL.BootLoader1mSDlyCnt = 100; + BL.Status = BL_RESET; + } + break; + case BL_ERASE_FLASH: FeedDog(); /*固件大小非法: 无法下载, 恢复老App(A区未动)并复位*/ @@ -564,6 +625,9 @@ int32_t main(void) if(BL.Para.AppFlag == APP_START_FLAG || BL.Para.AppFlag == APP_BOOT_UPGRADE_FLAG) { BL.Status = BL_START_APP; } + else if(BL.Para.UpdateFlag == APP_UPDATE_SPI_FLAG) { + BL.Status = BL_STASH_TO_B; /*Cat1远程升级: 外部Flash暂存区→B区→A区*/ + } else if(BL.Para.UpdateFlag == APP_UPDATE_FLAG) { BL.Status = BL_ERASE_FLASH; }