通常boot的功能时用来引导app区程序运行的,对于小的项目这仅仅是一个跳转程序。当然还有一个重要的功能,就是用作程序区域的更新升级。所以下面的方案就只针对引导程序区和更新程序区而设计,仅用一个main 文件实现该功能。
上代码
#include "stm32f10x.h"
#define APP_CNT (100)
/* 基地址和分区大小 */
#define ADDR_START (0x08000000)
#define SIZE_FLASH (256*1024)
#define SIZE_BOOT (10*1024)
#define SIZE_APP (100*1024)
#define SIZE_COPY (100*1024)
#define SIZE_DATA (44*1024)
#define SIZE_REG (2*1024)
/* 分区起始地址 */
#define ADDR_BOOT (ADDR_START) //起始 地址
#define ADDR_APP (ADDR_START+SIZE_BOOT) //app 地址
#define ADDR_COPY (ADDR_START+SIZE_BOOT+SIZE_APP) //备份 地址
#define ADDR_DATA (ADDR_START+SIZE_BOOT+SIZE_APP+SIZE_COPY) //数据 地址
#define ADDR_REG (ADDR_START+SIZE_BOOT+SIZE_APP+SIZE_COPY+SIZE_DATA) //三元组地址
#define ADDR_END (ADDR_START+SIZE_FLASH) //结束 地址
/* BKP区 */
#define RTC_ENABLE (0X5679)
#define RTC_DISABLE (0X3265)
//BKP_DR可以有20个且均为16位 这里使用2个
#define ADDR_RTC BKP_DR1
#define ADDR_UPDATE BKP_DR2
#define PWR_BackupAccessCmd_ENABLE() {RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE); PWR_BackupAccessCmd(ENABLE);}//开启备份区域写入权限 和 配置时钟
#define PWR_BackupAccessCmd_DISABLE() {PWR_BackupAccessCmd(DISABLE);} //关闭备份区域写入权限
/* 写BKP寄存器 */
void PWR_BackupAccessCmd_WRITE(uint16_t BKP_DR, uint16_t DataA){
PWR_BackupAccessCmd_ENABLE();//开启备份区域写入权限
BKP_WriteBackupRegister(BKP_DR, DataA);
PWR_BackupAccessCmd_DISABLE();//关闭备份区域写入权限
}
//=========================复制校验
void copy(void)
{
u32 i = 0;
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPTERR | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
for(;i < APP_CNT;i++) FLASH_ErasePage(ADDR_APP+(i*1024)); /* 擦除 */
for (i = 0; i < SIZE_APP / 4; i++) FLASH_ProgramWord(ADDR_APP+4 * i, *((u32 *)(ADDR_COPY + 4 * i))); /* 写入 */
FLASH_Lock();
}
int copy_check(void)
{
u32 i = 0;
u32 * sour = (u32 *)ADDR_COPY;
u32 * neww = (u32 *)ADDR_APP;
for (i = 0; i < SIZE_APP / 4; i++,sour++,neww++)
if(*sour != *neww)
return -1;
/* 清除标志位 */
PWR_BackupAccessCmd_WRITE(ADDR_UPDATE,RTC_DISABLE);
return 1;
}
//======================APP跳转
__asm void MSR_MSP(u32 addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
typedef void (*iapfun)(void); //定义一个函数类型的参数.
void iap_load_app(u32 appxaddr)
{
iapfun jump2app; //
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
}
}
//======================主函数
int main(void)
{
int i = 0;
if(BKP_ReadBackupRegister(ADDR_RTC) == RTC_ENABLE&&BKP_ReadBackupRegister(ADDR_UPDATE) == RTC_ENABLE){ //检测更新
do copy(); //执行更新
while(copy_check() < 0&&i++ < 10); //检测更新结果
}
iap_load_app(ADDR_APP); //跳转app
while(1){}; //死锁
}
说明:使用BKP的1区(与本题无关可以去除)和2区,分别用作RTC标志和更新标志。在检查到更新标志时进行更新复制和校验,完成后清除标志和跳转程序。