知识点
- flash写入
- 跳转到升级程序
- 中断向量表偏移
- keil设置
flash写入
- 内存大小端。我觉得叫高低端更好记,stm32内存中,数据低位存放在地址低端,所以是小端。按字节写入的时候按照bin文件字节顺序写入,如果按字或半字写入,则按小端排好后写入flash。
- flash擦除。flash写入前需要先擦除,这里按页擦除,需要知道一页多少字节。我用的是stm32f103vet6,一页2K字节
HAL库页擦除示例:
uint32_t PageError;
FLASH_EraseInitTypeDef my_erase_app;
my_erase_app.TypeErase = FLASH_TYPEERASE_PAGES;
my_erase_app.PageAddress = erase_addr;
my_erase_app.NbPages = 1;
HAL_FLASH_Unlock();
HAL_FLASHEx_Erase(&my_erase_app, &PageError);
HAL_FLASH_Lock();
- flash写入。擦除后写入flash,HAL库写flash示例:
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, erase_addr, dat);
HAL_FLASH_Lock();
- 其他:flash在解锁后擦除,擦除后可以在擦除范围内连续写入,操作后上锁
跳转到目标程序
示例:
(*((void(*)()) (*((volatile uint32_t*)(APP_ADDR+4)))))();
说明:
- APP_ADDR+4,升级程序地址加4即复位向量位置,先把APP_ADDR+4强转为地址:
(volatile uint32_t*)(APP_ADDR+4);
- 引用即读出flash中的数据,即复位向量地址:
*((volatile uint32_t*)(APP_ADDR+4));
- 把复位向量地址强转为函数指针:
(void(*)())(*((volatile uint32_t*)(APP_ADDR+4)));
- 引用该函数指针:
(*((void(*)())(*((volatile uint32_t*)(APP_ADDR+4)))))();
- 好了,如果flash中APP_ADDR+4位置数据有效就可以跳过去了
- 其他:堆栈栈顶检查,跳过去用户程序前可以先做一些检查,如APP_ADDR处,即升级程序写入的flash地址,开始四个字节存放的是栈顶地址,栈顶地址=0x2000 0000 + (rwdata+zidata),官方IAP程序这样写:
if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
JumpToApplication = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
JumpToApplication();
}
注意0x2FFE0000 这个值,加64K(0x10000)的SRAM的话,刚好等于0x2FFF0000。这个值可以根据自己芯片的SRAM大小修改到合适值,SRAM比64K小的话不修改也可以
中断向量表偏移
在更新程序中偏移中断向量表
SCB->VTOR = APP_ADDR;
KEIL设置
根据自己的应用分别设置BootLoader程序和用户程序的起始位置和大小: 对用户程序,生成bin文件命令(如果找不到fromelf.exe指定绝对路径试试,我的在这个位置:C:\Keil_v5\ARM\ARMCC\bin):
fromelf --bin -o "$L@L.bin" "#L"
总结
两个程序,BootLoader程序通过接口把要升级的用户程序bin文件写入flash,然后跳过去执行,最后在用户程序设置中断向量表
实验程序链接(需积分)
|