一、BootLoader程序设置APP应用程序的起始地址
1. 本例程设置的APP应用程序的起始地址为0x6000,即FLASH_APP1_ADDR= 0x08006000;点击Options for TargetàTarget选项卡,如图所示:
2. 更新APP固件代码如下:
3. 执行APP固件代码如下:
4. 特别注意以下两点:
#define STM32_FLASH_SIZE 256 //根据所选的STM32型号设置FLASH容量大小
Startup_stm32f10x_cl.s //启动配置文件与STM32型号必须一致,否则出现奇异错误
二、设置APP程序起始地址和存储空间大小
1. 随便打开一个之前的实例工程,点击Options for TargetàTarget选项卡,如图所示:
默认的条件下,图中IROM1的起始地址(Start)一般为0X08000000,大小(Size)为0X80000,即从0X08000000开始的512K空间为我们的程序存储(因为我们的STM32F103ZET6的FLASH大小是512K)。而图中,我们设置起始地址(Start)为0X08010000,即偏移量为0X10000(64K字节),因而,留给APP用的FLASH空间(Size)只有0X80000-0X10000=0X70000(448K字节)大小了。设置好Start和Szie,就完成APP程序的起始地址设置。
这里的64K字节,需要大家根据Bootloader程序大小进行选择,比如我们本章的Bootloader程序为22K左右,理论上我们只需要确保APP起始地址在Bootloader之后,并且偏移量为0X200的倍数即可(相关知识,请参考:http://www.openedv.com/posts/list/392.htm)。这里我们选择64K(0X10000)字节,留了一些余量,方便Bootloader以后的升级修改。
三、设置中断向量表的偏移量
在系统启动的时候,会首先调用systemInit函数初始化时钟系统,同时systemInit还完成了中断向量表的设置,我们可以打开systemInit函数,看看函数体的结尾处有这样几行代码:
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
/* Vector Table Relocation in Internal SRAM. */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
/* Vector Table Relocation in Internal FLASH. */
#endif
从代码可以理解,VTOR寄存器存放的是中断向量表的起始地址。默认的情况VECT_TAB_SRAM是没有定义,所以执行SCB->VTOR =FLASH_BASE | VECT_TAB_OFFSET;
对于FLASH APP,我们设置为FLASH_BASE+偏移量0x10000,所以我们可以在FLASH APP的main函数最开头处添加如下代码实现中断向量表的起始地址的重设:
SCB->VTOR= FLASH_BASE | 0x10000;
或者在NVIC_Configuration()函数中进行设置,如下所示
/* Setthe Vector Table base location at 0x08010000 */
NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x10000);
四、设置编译后运行fromelf.exe,生成.bin文件
通过在MDK点击Options for TargetàUser选项卡,在Run User Programs After Build/Rebuild 栏,勾选Run#1,并写入:C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe
--bin --output ..\RVMDK\Obj\STM3210C-EVAL.bin ..\RVMDK\Obj\STM3210C-EVAL.axf
如图所示:
注意事项:
对于大容量产品,其被划分为 256 页,每页 2K 字节。注意,小容量和中容量产品则每页只有 1K 字节。具体区别如下:
小容量产品主存储块1-32KB,每页1KB。系统存储器2KB。
中容量产品主存储块64-128KB,每页1KB。系统存储器2KB。
大容量产品主存储块256KB以上,每页2KB。系统存储器2KB。
互联型产品主存储块256KB以上,每页2KB。系统存储器18KB。
iap函数:由于STM32F103C8T6程序存储容量是64KB,每页的大小为1K字节即1024,所以iapbuf数组设置为u16 iapbuf[512];
代码如下:
#include "stmflash.h"
#include "iap.h"
iapfun jump2app;
u16 iapbuf[512];
//appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32appsize)
{
u16 t;
u16 i=0;
u16 temp;
u32 fwaddr=appxaddr;//当前写入的地址
u8 *dfu=appbuf;
for(t=0;t<appsize;t+=2)
{
temp=(u16)dfu[1]<<8;
temp+=(u16)dfu[0];
dfu+=2;//偏移2个字节
iapbuf[i++]=temp;
if(i==512)
{
i=0;
STMFLASH_Write(fwaddr,iapbuf,512);
fwaddr+=1024;//偏移1024
}
}
if(i)
STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.
}
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)
//检查栈顶地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4);
//用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr);
//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app();
//跳转到APP.
}
}