书接上回,上篇文章已经成功的将 STM32 内部FLASH虚拟成优盘进行文件存储了。
【将 STM32 内部Flash虚拟成优盘】https://blog.csdn.net/qq_44810226/article/details/127508789
然后我们开始固件升级流程:
从上图可以看出,固件存储的位置是不知道的,不确定的,但是一定在U盘存储的区域内。
我们可以通过给bin文件加上一些标志,来在Flash中判断是否有固件存在。如下图所示,给bin文件开头添加 固件名称、软件版本、硬件版本、起始地址、校验位、长度等数据。
因为FAT系统存储的特性,每个文件都会在扇区的开头存储,我们设置的扇区大小为0x200,所以我们可以在U盘开始的地址来遍历,搜索每个地址的前几个字符是否为我们想要固件的名称。
/*************************************************************
** Function name: FindBinFileAddr
** Descriptions: 在指定地址查找是否有固件存在
** Input parameters: None
** Output parameters: None
** Returned value: 固件地址 或者 0(没有找到)
** Remarks: None
*************************************************************/
uint32_t FindBinFileAddr(void){
unsigned char tempBuff[14] = {0};
for(uint16_t i=40;i<BLOCKNUM;i++){
memcpy(tempBuff, (char *)(MASS_STORAGE_CLASS_START_ADDR+(i*STORAGE_BLK_SIZ)), sizeof(tempBuff)-1);
if (strcmp(tempBuff, BIN_START_FLAG) == 0){
SEGGER_RTT_printf(0,"Find Fw StartName in[%x] USB-Device Sector %d-> %s\r\n",(MASS_STORAGE_CLASS_START_ADDR+(i*STORAGE_BLK_SIZ)),i,tempBuff);
return (MASS_STORAGE_CLASS_START_ADDR+(i*STORAGE_BLK_SIZ));
}
}
SEGGER_RTT_printf(0,"Not Find Fw !!!\r\n");
return 0;
}
这里还可以先判断一下固件的其他描述参数,判断固件是否有效,也可以在后面判断。
找到固件之后就可以进行正常升级了。
找到合适的固件之后,判断固件是否有效:通过名称、CRC、版本号等
这里我将升级用到的参数都做到了一个 结构体中,方便后面写代码。PUpdateFw_Struct 是这个结构体的指针。
UPDATE_Frameware_INIT(gFWUpdate,0x08050600,0x08019000,50,STMFLASH_ReadByte,STMFLASH_Write,2048);
.c文件
/*************************************************************
** Function name: FWInit
** Descriptions: 固件升级 初始化
** Input parameters: None
** Output parameters: None
** Returned value: None
** Remarks: None
*************************************************************/
uint8_t FWUpdateInit(PUpdateFw_Struct fw){
uint16_t checkCRC = 0;
memcpy(fw->info_fwName,(uint8_t *)(fw->fwAddr),FW_NAME_LENGTH);
memcpy(fw->info_fwVersion,(uint8_t *)(fw->fwAddr+FW_NAME_LENGTH),FW_VERSION_LENGTH);
memcpy(&fw->info_fwLength,(uint32_t *)(fw->fwAddr+FW_NAME_LENGTH+FW_VERSION_LENGTH),4);
memcpy(fw->info_boardVersion,(uint8_t *)(fw->fwAddr+FW_NAME_LENGTH+FW_VERSION_LENGTH+4),FW_VERSION_LENGTH);
memcpy(&fw->info_crc,(uint8_t *)(fw->fwAddr+FW_NAME_LENGTH+2*FW_VERSION_LENGTH+4+4),sizeof(uint16_t));
memcpy(fw->info_binEndFlag,(uint8_t *)(fw->fwAddr+48+fw->info_fwLength),BIN_END_FLAG_LENGTH);
checkCRC = usMBCRC16((unsigned char *)(fw->fwAddr+48),fw->info_fwLength);
if (strcmp(fw->info_fwName, BIN_START_FLAG) != 0){
SEGGER_RTT_printf(0,"fw name is err ! not a effective firmware ... \r\n");
return 0;
}
if (strcmp(fw->info_binEndFlag, BIN_END_FLAG) != 0){
SEGGER_RTT_printf(0,"fw end name is err ! not a effective firmware ... \r\n");
return 0;
}
if (checkCRC != fw->info_crc){
SEGGER_RTT_printf(0,"crc check is err ! not a effective firmware ... \r\n");
return 0;
}
SEGGER_RTT_printf(0," Firmware effective : name-> %s \r\n",fw->info_fwName);
SEGGER_RTT_printf(0," Version-> %d.%d.%d.%d\r\n",fw->info_fwVersion[0],fw->info_fwVersion[2],fw->info_fwVersion[4],fw->info_fwVersion[6]);
SEGGER_RTT_printf(0," FileLength-> %x\r\n",fw->info_fwLength);
SEGGER_RTT_printf(0," BoardVersion-> %d.%d.%d.%d\r\n",fw->info_boardVersion[0],fw->info_boardVersion[2],fw->info_boardVersion[4],fw->info_boardVersion[6]);
// crc 检测
SEGGER_RTT_printf(0," FileCRC-> %x\r\n",fw->info_crc);
SEGGER_RTT_printf(0," CheckCRC-> %x \r\n",checkCRC);
SEGGER_RTT_printf(0," FileEndName-> [%p] %s\r\n",(uint8_t *)(fw->fwAddr+48+fw->info_fwLength),fw->info_binEndFlag);
// 返回是否init成功 如果失败则代表不是一个正常的固件 不可以进行升级
return 1;
}
void (*JumpToApplication)(void);
uint32_t gJumpAddress;
typedef void (*pFunction)(void);
/*************************************************************
** Function name: FWStartUpdate
** Descriptions: 开始升级
** Input parameters: None
** Output parameters: None
** Returned value: None
** Remarks: None
*************************************************************/
void FWStartUpdate(PUpdateFw_Struct fw){
fw->WriteByte(fw->appAddr,(uint32_t *)(fw->fwAddr+48),fw->info_fwLength/8);
SEGGER_RTT_printf(0,"Write FW ok \r\n");
uint32_t erase[2] = {0};
// 破坏APP2 固件存储的地址
fw->WriteByte(fw->fwAddr,erase,1);
SEGGER_RTT_printf(0,"Eares app2 fw ok \r\n");
// 破坏fat结构 上电重新初始化
// fw->WriteByte(MASS_STORAGE_CLASS_START_ADDR,erase,1);
// 跳转到APP
/* Test if user code is programmed starting from address 0x0800C000 */
// 检查栈顶是否合法,确保栈顶落在0x2000 0000 - 0x2001 0000 之间,刚好在stm32f1的RAM范围内
if (((*(__IO uint32_t *)fw->appAddr) & 0x2FFE0000) == 0x20000000)
{
SEGGER_RTT_printf(0,"Check ok %x %x %x \r\n",fw->appAddr,(*(__IO uint32_t *)fw->appAddr),((*(__IO uint32_t *)fw->appAddr) & 0x2FFE0000));
// 检查reset入口是否正确
// if (((*(uint32_t*)(STM32_APP_BASE + 4)) & 0x0fff0000 ) == 0x08020000 )
/* Jump to user application */
gJumpAddress = *(__IO uint32_t *)(fw->appAddr + 4);
JumpToApplication = (pFunction)gJumpAddress;
/* Reset of all peripherals */
HAL_DeInit();
/* Set interrupt vector to app code */
SCB->VTOR = fw->appAddr;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t *)fw->appAddr);
__disable_irq();
JumpToApplication();
}else{
SEGGER_RTT_printf(0,"Check err %x %x %x \r\n",fw->appAddr,(*(__IO uint32_t *)fw->appAddr),((*(__IO uint32_t *)fw->appAddr) & 0x2FFE0000));
}
}
.h文件
// 写入前先检查 是不是所有位置都可以写入:即是不是0xff 如果不是则擦除所在位置
struct SUpdateFw_Struct {
// 固件存放地址
uint32_t fwAddr;
// 写入地址:APP1地址
uint32_t appAddr;
// APP1所在区块
uint16_t appSector;
// 读取函数
uint8_t (*ReadOneByte)(uint32_t addr);
// 写入函数
void (*WriteByte)(uint32_t addr,uint32_t *data, uint32_t num);
// Flash Sector 大小
uint16_t sectorSize;
// 固件Info
char info_fwName[FW_NAME_LENGTH+1];
// 固件版本信息
char info_fwVersion[FW_VERSION_LENGTH+1];
// 固件长度
uint32_t info_fwLength;
char info_boardVersion[FW_VERSION_LENGTH+1];
// info中的起始地址
uint32_t info_startAddr;
// 固件Info
uint16_t info_crc;
char info_binEndFlag[BIN_END_FLAG_LENGTH+1];
};
typedef struct SUpdateFw_Struct UpdateFw_Struct;
typedef UpdateFw_Struct *PUpdateFw_Struct;
// 固件存放地址、写入地址:APP1地址、 APP1所在区块、 读取函数、 写入函数、 Flash Sector 大小
#define UPDATE_Frameware_INIT(xname,xfwAddr,xappAddr,xappSector,xReadOneByte,xWriteByte,xsectorSize) \
UpdateFw_Struct xname = { \
.fwAddr = xfwAddr, \
.appAddr = xappAddr, \
.appSector = xappSector, \
.ReadOneByte = xReadOneByte, \
.WriteByte = xWriteByte, \
.sectorSize = xsectorSize, \
.info_fwName = {0}, \
.info_fwVersion = {0}, \
.info_fwLength = 0, \
.info_boardVersion = {0}, \
.info_startAddr = 0, \
.info_crc = 0, \
.info_binEndFlag = {0}, \
};
这里程序参考意义不大,可能指针会有点难看。我将整个代码放在csdn中,需要的同学可以下载学习。