feat(boot): 升级改造为A/B分区(新固件先下B区整包校验后搬运A区)

分区: Boot 32KB | A区0x8000(29扇区232KB,运行) | B区0x42000(29扇区232KB,暂存) | 参数区0x7C000不变
流程: 仅擦B区下载→CRC32(B)通过→逐扇区搬运B→A→CRC32(A)通过→擦B区→置APP_BOOT_UPGRADE_FLAG复位
防变砖: 下载超时/CRC失败/AppSize非法均恢复APP_START_FLAG复位回老App; 断电续传靠重启后B区CRC判定
其他: GetCrc32/AppWriteFlash/EraseFlash基址参数化, FlashWrite尾字节0xFF补齐, 0x82增加状态守卫
协议与boot_para_t结构不变, 主机升级工具无需修改
This commit is contained in:
2026-09-23 11:16:47 +08:00
parent ffaf7a55a6
commit 48ed25015d
4 changed files with 140 additions and 38 deletions
+6 -6
View File
@@ -402,7 +402,7 @@ void BSP_Init(void)
/*****************************************************************************************
* Flash操作
****************************************************************************************/
void FlashWrite(uint32_t Addr, uint32_t *wData, uint8_t wLen)
void FlashWrite(uint32_t Addr, uint32_t *wData, int wLen)
{
EFM_Unlock();
EFM_FlashCmd(Enable);
@@ -415,7 +415,7 @@ void FlashWrite(uint32_t Addr, uint32_t *wData, uint8_t wLen)
EFM_Lock();
}
static void FlashRead(uint32_t u32Addr, uint32_t *rData, uint8_t rLen)
static void FlashRead(uint32_t u32Addr, uint32_t *rData, int rLen)
{
for(int i = 0; i < rLen / 4; i++) {
rData[i] = *((volatile uint32_t*)(u32Addr + i * 4));
@@ -449,15 +449,15 @@ void WriteBootPara(BootPara wData)
EFM_Lock();
}
void EraseFlash(void)
void EraseFlash(uint32_t BaseAddr, uint32_t SectorCnt)
{
EFM_Unlock();
EFM_FlashCmd(Enable);
while(Set != EFM_GetFlagStatus(EFM_FLAG_RDY)) { ; }
/* 只擦APP区, 不擦参数区(BOOT_PARA_ADDRESS) */
for(int Addr = APP_ADDRESS; Addr < BOOT_PARA_ADDRESS; Addr += FLASH_SECTOR_SIZE) {
EFM_SectorErase(Addr);
/*从BaseAddr起连续擦除SectorCnt个扇区(A/B区通用), 不碰参数区*/
for(uint32_t i = 0; i < SectorCnt; i++) {
EFM_SectorErase(BaseAddr + i * FLASH_SECTOR_SIZE);
FeedDog();
}
EFM_Lock();
+119 -22
View File
@@ -40,14 +40,14 @@ void InitCrc32Table(uint32_t* Crc32Tbl)
/*****************************************************************************************
* 函数名称: GetCrc32
* 功能描述: 计算APP区CRC32校验值
* 功能描述: 计算指定区域CRC32校验值
*****************************************************************************************/
uint32_t GetCrc32(int dwLen)
uint32_t GetCrc32(uint32_t BaseAddr, int dwLen)
{
uint32_t Data;
uint32_t dwCrc32Data = 0xFFFFFFFF;
for(int dwCnt = 0; dwCnt < dwLen; dwCnt++) {
Data = *((volatile uint8_t*)(APP_ADDRESS + dwCnt));
Data = *((volatile uint8_t*)(BaseAddr + dwCnt));
uint32_t dwTbl = (dwCrc32Data ^ Data) & 0xFF;
dwCrc32Data = ((dwCrc32Data >> 8) & 0xFFFFFF) ^ pdwCrc32Tbl[dwTbl];
}
@@ -110,11 +110,58 @@ static bool JumpToApp(uint32_t AppAddr)
/*****************************************************************************************
* 函数名称: AppWriteFlash
* 功能描述: 写APP数据到Flash (每包200字节)
* 功能描述: 写升级数据到Flash指定分区 (每包200字节, 尾部不足4字节按0xFF补齐)
* 参 数: BaseAddr 分区基址(B区), AddrOffset 分区内偏移, wData 数据, wLen 字节数
* 返 回 值: 无
*****************************************************************************************/
void AppWriteFlash(uint32_t AddrOffset, uint8_t *wData, int wLen)
void AppWriteFlash(uint32_t BaseAddr, uint32_t AddrOffset, uint8_t *wData, int wLen)
{
FlashWrite(APP_ADDRESS + AddrOffset, (uint32_t *)wData, wLen / 4);
int FullWords = wLen / 4;
int Remain = wLen % 4;
uint32_t Word;
FlashWrite(BaseAddr + AddrOffset, (uint32_t *)wData, FullWords);
if(Remain > 0) { /*末包尾字节补0xFF组字写入, 保证与AppSize逐字节CRC一致*/
Word = 0xFFFFFFFF;
memcpy(&Word, &wData[FullWords * 4], Remain);
FlashWrite(BaseAddr + AddrOffset + FullWords * 4, &Word, 1);
}
}
static uint32_t s_CopyBuf[FLASH_SECTOR_SIZE / 4]; /*搬运缓冲(8KB)*/
/*****************************************************************************************
* 函数名称: CopyAppBtoA
* 功能描述: 将B区已校验固件搬运到A区(逐扇区: 擦A→读B→写A), 完成后整包校验A区
* 参 数: 无
* 返 回 值: true=搬运并校验通过
*****************************************************************************************/
static bool CopyAppBtoA(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;
for(Sec = 0; Sec < FullSectors; Sec++) {
Base = APP_A_ADDRESS + Sec * FLASH_SECTOR_SIZE;
EraseFlash(Base, 1);
for(uint32_t i = 0; i < SecPerBank; i++) {
s_CopyBuf[i] = *((volatile uint32_t *)(APP_B_ADDRESS + Sec * FLASH_SECTOR_SIZE + i * 4));
}
FlashWrite(Base, s_CopyBuf, SecPerBank);
}
if(RemainWords > 0) {
Base = APP_A_ADDRESS + FullSectors * FLASH_SECTOR_SIZE;
EraseFlash(Base, 1);
for(uint32_t i = 0; i < RemainWords; i++) {
s_CopyBuf[i] = *((volatile uint32_t *)(APP_B_ADDRESS + FullSectors * FLASH_SECTOR_SIZE + i * 4));
}
FlashWrite(Base, s_CopyBuf, RemainWords);
}
/*整包校验A区*/
return (GetCrc32(APP_A_ADDRESS, BL.Para.AppSize) == BL.Para.Crc32Check);
}
/*****************************************************************************************
@@ -188,6 +235,10 @@ bool AnalyzeData(uint8_t *rData, uint16_t rLen)
return false;
}
UDReq UpData = (UDReq)&rData[sizeof(FrameHeader_t)];
/*固件大小合法性检查: 为0或超出B区容量时拒绝升级*/
if(UpData->AppSize == 0 || UpData->AppSize > APP_BANK_SIZE) {
return false;
}
BL.Para.AppFlag = 0;
BL.Para.UpdateFlag = APP_UPDATE_FLAG;
BL.Para.Crc32Check = UpData->AppCrc32;
@@ -204,12 +255,16 @@ bool AnalyzeData(uint8_t *rData, uint16_t rLen)
if(BL.UpDateFlag == false) {
return false;
}
/*状态守卫: 仅下载状态接收数据, 防止搬运/复位阶段迟到包破坏B区*/
if(BL.Status != BL_GET_DATA && BL.Status != BL_WAIT_REV) {
return false;
}
DownRes DownData = (DownRes)&rData[sizeof(FrameHeader_t)];
if(BL.AppPackageIdx != DownData->PackageIndex) {
return false;
}
/*写数据到Flash*/
AppWriteFlash(BL.AppPackageIdx * 200, &rData[sizeof(FrameHeader_t) + sizeof(DownResponse_t)], DownData->DataLen);
AppWriteFlash(APP_B_ADDRESS, BL.AppPackageIdx * 200, &rData[sizeof(FrameHeader_t) + sizeof(DownResponse_t)], DownData->DataLen);
BL.AppPackageIdx++;
BL.RevErrCnt = 0;
@@ -218,23 +273,22 @@ bool AnalyzeData(uint8_t *rData, uint16_t rLen)
BL.BootLoader1mSDlyCnt = 5;
}
else {
/*全部数据处理完成, 校验CRC32*/
uint32_t crc32 = GetCrc32(BL.Para.AppSize);
/*全部数据处理完成: 整包校验B区CRC32(此时尚未触碰A区, 原App完好)*/
uint32_t crc32 = GetCrc32(APP_B_ADDRESS, BL.Para.AppSize);
if(crc32 == BL.Para.Crc32Check) {
/*置升级完成标记(每次升级均置位):Boot本身不擦除外部Flash,
App启动时检测到此标记后擦除外部参数分区并恢复APP_START_FLAG*/
BL.Para.AppFlag = APP_BOOT_UPGRADE_FLAG;
BL.Para.UpdateFlag = 0;
WriteBootPara(&BL.Para);
BL.BootLoader1mSDlyCnt = 100;
BL.Status = BL_RESET;
/*B区校验通过: 关闭接收, 进入搬运阶段(B→A), 搬运并校验A区后才置升级完成标记*/
BL.UpDateFlag = false;
BL.CopyRetryCnt = 0;
BL.Status = BL_COPY_TO_A;
}
else {
BL.Para.AppFlag = 0;
/*B区校验失败: A区未动, 恢复老App启动标志并复位, 主机可重新发起升级*/
BL.Para.AppFlag = APP_START_FLAG;
BL.Para.UpdateFlag = 0;
WriteBootPara(&BL.Para);
BL.UpDateFlag = false;
BL.Status = BL_IDLE;
BL.BootLoader1mSDlyCnt = 100;
BL.Status = BL_RESET;
}
}
return true;
@@ -359,9 +413,50 @@ void BootLoaderLoopHandler(void)
}
break;
case BL_COPY_TO_A:
/*搬运B区已校验镜像到A区, 完成后整包校验A区*/
if(CopyAppBtoA()) {
/*搬运校验通过: 立即擦除B区作废镜像, 防止下次升级时B区残留被误判为断电续传而跳过下载*/
EraseFlash(APP_B_ADDRESS, APP_BANK_SECTORS);
/*置升级完成标记(每次升级均置位), App启动时擦除外部参数分区并恢复APP_START_FLAG*/
BL.Para.AppFlag = APP_BOOT_UPGRADE_FLAG;
BL.Para.UpdateFlag = 0;
WriteBootPara(&BL.Para);
BL.BootLoader1mSDlyCnt = 100;
BL.Status = BL_RESET;
}
else {
/*搬运后A区校验失败(B区仍有效): 重试一次搬运*/
if(BL.CopyRetryCnt++ < 1) {
break;
}
/*重试仍失败: 清标志停留等待主机重新升级*/
BL.Para.AppFlag = 0;
BL.Para.UpdateFlag = 0;
WriteBootPara(&BL.Para);
BL.Status = BL_IDLE;
}
break;
case BL_ERASE_FLASH:
FeedDog();
EraseFlash();
/*固件大小非法: 无法下载, 恢复老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;
}
/*先校验B区: 已有完整有效镜像则直接搬运(断电续传: 下载完未搬运/搬运中断电)*/
if(GetCrc32(APP_B_ADDRESS, BL.Para.AppSize) == BL.Para.Crc32Check) {
BL.CopyRetryCnt = 0;
BL.Status = BL_COPY_TO_A;
break;
}
/*B区无效: 仅擦除B区(A区保留原App), 逐包下载到B区*/
EraseFlash(APP_B_ADDRESS, APP_BANK_SECTORS);
FeedDog();
BL.Status = BL_GET_DATA;
BL.BootLoader1mSDlyCnt = 200;
@@ -392,12 +487,14 @@ void BootLoaderLoopHandler(void)
BL.Status = BL_GET_DATA;
}
else {
BL.Status = BL_IDLE;
BL.Para.AppFlag = 0;
BL.RevErrCnt = 0;
/*下载超时: A区未动, 恢复老App启动标志并复位(设备回到可运行状态)*/
BL.Para.AppFlag = APP_START_FLAG;
BL.Para.UpdateFlag = 0;
WriteBootPara(&BL.Para);
BL.RevErrCnt = 0;
BL.UpDateFlag = false;
BL.BootLoader1mSDlyCnt = 100;
BL.Status = BL_RESET;
}
break;
}