历史上的今天
今天是:2025年08月19日(星期二)
2018年08月19日 | STM32 BootLoader升级固件
2018-08-19 来源:eefocus
关于Bootloader,从书上的文字描述,很难理解这个名词是什么,有什么用。这次用到了,算是有了更进一步的认识。
一、知识点
1、BootLoader就是单片机启动时候运行的一段小程序,这段程序负责单片机固件的更新,也就是单片机选择性的自己给自己下程序。可以更新,也可以不更新,更新的话,BootLoader更新完程序后,跳转到新程序运行;不更新的话,BootLoader直接跳转到原来的程序去运行。
2、BootLoader更新完程序后并不擦除自己,下次启动后依然先运行BootLoader程序,又可以选择性的更新或者不更新程序,所以BootLoader就是用来管理单片机程序的更新。
3、在实际的单片机工程项目中,如果加入了BootLoader功能,就可以给单片机日后升级程序留出一个接口,方便日后单片机程序更新。当然,这就需要创建两个工程项目,一个为BootLoader工程,一个为APP工程。
4、BootLoader工程生成的.hex或者.bin文件通常下载到ROM或Flash中的首地址,这样可以保证上电后先运行BootLoader程序。而APP工程生成的.hex或者.bin文件则下载到ROM或Flash中BootLoader后面的地址中。也就是说,存在ROM/Flash中的内容是分为两部分的。
5、要实现在同一个ROM/Flash中保存两段程序,并且保证不能相互覆盖,则需要在下载程序时指定地址。如在Keil下,可以进行如下的调整。
6、实际上,在STM32系列的单片机中,Flash本身就是分扇区的,一个扇区16KB的样子,具体可以查看手册。那么就可以用从第一个扇区的首地址开始下载BootLoader的程序,而从第二个扇区的起始地址开始下载APP程序。如下为STM32F4系列芯片的Flash模块。
7、单片机上电之后开始执行BootLoader程序,这是单片机会检测用户是否有升级应用程序(APP)的请求,具体表现有很多种,例如检测内存卡,Nand Flash中是否包含升级文件,串口/I2C/SPI等外设接口是否传来升级文件。据说还有使用GSM来升级的。
8、所谓的升级,就是将ROM/Flash中存储APP程序的扇区内容擦除并写入新文件。例如一次固件升级的过程可以是:1、单片机上电执行BootLoader,2、BootLoader查找升级文件,3、若找到文件,擦除Flash中的部分扇区(存APP的),4、在擦除的扇区写入升级的文件,5、写入完成,读取数据检验是否出错,6、若数据一致,升级成功,删除升级文件,7、BootLoader程序跳转到APP程序执行。删除升级文件是为了下次上电后不再进行升级。
9、所谓的跳转,可以理解程序指针的改变,变为指向APP程序扇区的起始地址。
二、部分代码
1、主函数
int main(void)
{
HAL_Init();//STM32初始化
SystemClock_Config();//时钟配置
System_GPIOInit();//IO口配置
#ifdef BOOTLOAD_DISPLAY_ENABLE
SystemColorInit();//显示屏配置
#endif
System_LoadUpdateFile();//升级函数
while (1)
{
}
}
2、升级函数
void System_LoadUpdateFile(void)
{
uint8_t res;
if(bNandFlash_Error)//如果NandFlash错误,串口打印错误信息,跳转到用户程序
{
d_printf("NandFlash_Error jump\n");
BootLoad_Jump();//跳转函数
return;
}
if(bNo_FileSystem)//如果没有文件系统,串口打印错误信息,跳转到用户程序
{
d_printf("no file system jump\n");
BootLoad_Jump();//跳转函数
return;
}
if(f_open(&File, (char *)UPDATE_FILE_PATH, FA_READ)==FR_OK)//如果存在升级文件,开始执行升级
{
d_printf("update\n");
if(BootLoad_Program())//是否写入成功
{
f_close(&File);//关闭升级文件
res=f_unlink((char *)UPDATE_FILE_PATH);//删除升级文件
d_printfhex(res);d_printf("\n");
res=f_unlink((char *)UPDATE_DIR_PATH);//删除升级目录
d_printfhex(res);d_printf("\n");
BootLoad_Jump();//跳转函数
}
else
{
HAL_FLASH_Lock();//锁定Flash
d_printf("update fail\n");
f_close(&File);//关闭升级文件
BootLoad_Jump();//跳转函数
}
}
else
{
d_printf("jump\n");
f_close(&File);
BootLoad_Jump();
}
}
3、重写Flash函数
uint8_t BootLoad_Program(void)
{
uint32_t BaseAddress=APPLICATION_ADDRESS;//APP地址
uint32_t i,br,datacnt=0;
uint8_t data8;
GlobalPtr32=(uint32_t *)BootBuff;
HAL_FLASH_Unlock();//解锁Flash
if(BootLoad_Erase()==false)//擦除Flash
{
return false;
}
d_printf("size:");d_printfhex32(File.fsize);d_printf("\n");
while(1)
{
f_read(&File,BootBuff,8192,(void *)&br);//读取升级文件
for (i=0;i<(br>>2);i++)
{
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BaseAddress, GlobalPtr32[i]) == HAL_OK)//写入升级文件
{
BaseAddress = BaseAddress +4;
}
else
{
d_printf("program err\n");
return false;
}
}
datacnt+=br;
if(datacnt>=File.fsize)//写入完成
{
break;
}
}
d_printf("verify\n"); //验证Flash中的内容与升级文件是否一致
f_lseek(&File,0); //若一致代表升级成功
datacnt=0; //若不一致代表升级失败
BaseAddress=APPLICATION_ADDRESS;
while(1)
{
f_read(&File,BootBuff,8192,(void *)&br);
for (i=0;i
{
data8 = *(__IO uint8_t*)BaseAddress;
if (data8 != BootBuff[i])
{
d_printf("error!\n");
return false;
}
BaseAddress ++;
}
datacnt+=br;
if(datacnt>=File.fsize)
{
break;
}
}
HAL_FLASH_Lock();//锁定Flash
return true;
}
4、跳转函数(从BootLoader中跳转到APP的main函数)
void BootLoad_Jump(void)
{
/* Check Vector Table: Test if user code is programmed starting from address
"APPLICATION_ADDRESS" */
d_printfhex32((*(__IO uint32_t*)APPLICATION_ADDRESS));d_printf("\n");
if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS +4);
d_printfhex32(JumpAddress);d_printf("\n");
HAL_Delay(100);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
Jump_To_Application();
}
}
史海拾趣
|
linux 下 CY7C68013 的 slavefifo 驱动 linux 下 CY7C68013 的 slavefifo 驱动 在 linux 下 自己编写 68013 驱动 1:在驱动程序调试中发现 写函数 成功执行了,68013 也 收到了数据,但是相应的硬件并不出现 68013 slavefifo 模式下应该出现的结果。 2:但是 68013 slavef ...… 查看全部问答> |
|
首先是IoAttachDevice 这一个函数 是不是已经被 过滤设备 绑定 硬件设备 ?不用再用 IoAttachDeviceToDeviceStacksaSafe 和 IoGetDeviceObjectPointer 了 如果了解错误 请高手把下边的流程排列下 实在想法没路。感激不尽。 生成过滤设备 过滤 ...… 查看全部问答> |
|
这两天在看S3C2410 Eboot里的am29lv800.c文件,这个文件实现对 AMD29LV800BB芯片的初始化、擦除、读、写等操作。 我的开发板使用的NorFlash是SST39VF1601,配套的Eboot里没有找到类似的初始化这块芯片的文件,生成的Eboot 直接烧到NorFlash中不能 ...… 查看全部问答> |
|
请教ARM9 CPU (以S3C2440为例)的启动过程及存储器映射的分析,感恩! 晚辈是新手上路,能否请教各位前辈大侠一个问题: ARM9 CPU (以S3C2440为例)的启动过程及存储器映射的分析,感恩! 或者推荐一下何处有详尽的参考资料,感谢! … 查看全部问答> |
|
各位: CPU为PXA270,采集电压芯片为WM9712 ,通过AC-LINK采集电压 一共有两处用到: 1、触摸屏, 2、电池当前电压 问题: 正常情 ...… 查看全部问答> |
|
请高手指点error C2440: '=' : cannot convert from 'void *' to 'unsigned char *' pNew = LocalReAlloc ( pPtr, dwSize, & ...… 查看全部问答> |
|
大家好,当我使用SOPC的IP核生成了一个工程文件(暂时把这个顶层叫vip吧)之后,想要将vip模块作为我一个子模块例化一下。可是这样做了之后编译不能通过,报错如下:Error (10613): VHDL syntax error at video_conver_top.vhd(153): experienced u ...… 查看全部问答> |




