BootLoader IAP升级
一、说明
如图,可看出单片机的ROM开始地址为0x8000000,大小为0x80000(512K byte),RAM的开始地址为0x20000000,大小为0x24000(144K byte)
单片机上电从0x8000000+4进入中断向量表。
**可参考正点原子例程中串口IAP实验。
二、实现步骤
1、芯片内部FLASH验证
对接原厂API函数,验证内部FLASH烧写读取是否正常,测试FLASH为大端还是小端
例:
1)原厂内部API函数
FLASH_STS FLASH_ProgramWord(uint32_t Address, uint32_t Data)
可看出Address:是地址写入地址, Data是要写入的数据,为32位的。
2)原厂对接函数
宏定义
#define INTERNAL_FLASH_START_ADDR ((uint32_t)0x08000000) //起始地址
#define INTERNAL_FLASH_END_ADDR ((uint32_t)0x0807FFFF) //结束地址
#define INTERNAL_FLASH_PAGE_SIZE 2048
FLASH直接写数据
void Drv_InternalFlashWriteBufferNoCheck(uint32_t Addr, uint8_t *Buffer, uint32_t Length)
{
uint16_t i = 0, j = 0;
uint32_t Data = 0;
for(i=0;i<Length;i+=4)
{
j = i;
Data |= Buffer[j++];
Data |= Buffer[j++]<<8;
Data |= Buffer[j++]<<16;
Data |= Buffer[j++]<<24;
FLASH_ProgramWord(Addr,Data);
Data = 0;
Addr += 4;
}
}
FLASH读数据
void Drv_InternalFlashReadBuffer(uint32_t Addr, uint8_t *Buffer, uint16_t Length)
{
uint16_t i = 0;
for(i=0;i<Length;i++)
{
Buffer[i] = (*(volatile uint32_t*)(Addr+i));
}
}
FLASH带验证检查擦写函数
static uint8_t InternalFlashBuffer[INTERNAL_FLASH_PAGE_SIZE];
void Drv_InternalFlashWriteBuffer(uint32_t Addr, uint8_t *Buffer, uint32_t Length)
{
uint16_t Count = 0;
u32 OffAddr; //去掉0X08000000后的地址
u32 PagePos; //页地址
u32 PageOff; //页内偏移地址
u32 PageMain; //页内剩余地址
if(Addr < FLASH_BASE || Addr > INTERNAL_FLASH_END_ADDR)
{
return; //非法地址
}
FLASH_Unlock();
OffAddr = Addr - INTERNAL_FLASH_START_ADDR; //去掉0X08000000后的地址,实际偏移地址.
PagePos = OffAddr / INTERNAL_FLASH_PAGE_SIZE; //得到第几页开始写
PageOff = OffAddr % INTERNAL_FLASH_PAGE_SIZE; //得到不足一页字节长度
PageMain = INTERNAL_FLASH_PAGE_SIZE - PageOff; //得到剩余空间字节长度
if(Length <= PageMain)
{
PageMain = Length;
}
while(1)
{
Drv_InternalFlashReadBuffer(PagePos*INTERNAL_FLASH_PAGE_SIZE+INTERNAL_FLASH_START_ADDR, \
InternalFlashBuffer, INTERNAL_FLASH_PAGE_SIZE); //读出整个页的内容
for(Count=0; Count<PageMain; Count++)//校验数据
{
if(InternalFlashBuffer[PageOff+Count] != 0XFF)
{
break; //需要擦除
}
}
if(Count < PageMain) //需要擦除
{
FLASH_EraseOnePage(PagePos*INTERNAL_FLASH_PAGE_SIZE+INTERNAL_FLASH_START_ADDR); //擦除
for(Count=0; Count<PageMain; Count++) //复制
{
InternalFlashBuffer[PageOff+Count] = Buffer[Count];
}
Drv_InternalFlashWriteBufferNoCheck(PagePos*INTERNAL_FLASH_PAGE_SIZE+INTERNAL_FLASH_START_ADDR, \
InternalFlashBuffer,INTERNAL_FLASH_PAGE_SIZE);
}
else
{
Drv_InternalFlashWriteBufferNoCheck(Addr, Buffer, PageMain); //写已经擦除了的,直接写入扇区剩余区间
}
if(Length == PageMain)
{
break;//写入结束了
}
else
{
PagePos++; //扇区地址增1
PageOff = 0; //偏移位置为0
Buffer += PageMain; //指针偏移
Addr += PageMain; //写地址偏移
Length -= PageMain; //字节数递减
if(Length > INTERNAL_FLASH_PAGE_SIZE)
{
PageMain = INTERNAL_FLASH_PAGE_SIZE;
}
else
{
PageMain = Length;
}
}
}
FLASH_Lock();
}
2、分配单片机资源及配置参数
1)IAP
如图所示,分配0x10000(64K byte)内存空间给IAP烧写程序,烧录选择全部擦除模式。
2)APP
如图所示,分配0x70000(448K byte)内存空间给APP用,烧录选择部分擦除模式。
3、IAP升级相关函数
#define FLASH_APP_ADDR (FLASH_BASE | 0x10000) //APP程序起始地址
typedef void (*iapfun)(void); //定义一个函数类型的参数.
iapfun JumpApp;
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(uint32_t addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void Drv_IapLoadApp(uint32_t appxaddr)
{
if(((*(volatile uint32_t*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
JumpApp=(iapfun)*(volatile uint32_t*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(volatile uint32_t*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
JumpApp(); //跳转到APP.
}
}
4、程序实现
将APP的Bin档文件,在IAP中读取完烧录进我们分配好的APP地址中,接着调用跳转函数Drv_IapLoadApp(0x8010000),实现程序跳转。
在APP工程中,需要在程序执行前更改执行地址,
int main(void)
{
SCB->VTOR = FLASH_BASE | 0X10004; //地址偏移0x8010000+4
while(1)
{
;
}
}