APP程序的生成步骤:
①设置APP程序的起始地址和存储空间大小
② 设置中断向量表偏移量
设置SCB->VTOR的值即可。
③ 设置MDK编译后运行fromelf.exe,生成.bin文件.
通过在MDK User选项卡,设置编译后调用fromelf.exe,根据.axf文件生
成.bin文件,用于IAP更新。
bootloader作用:
1.通过某种串口下载.bin文件 2.写入flash的某个区域
3.跳转执行
查看 SYSTEM–usart.c
u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.
查看定义(usart.h)得到buf的宽度
#define USART_REC_LEN 120*1024 //定义最大接收字节数 120K(1024BYTE(字节)=1KB)
查看 IAP–iap.c–iap.h
#define FLASH_APP1_ADDR 0x08010000 //第一个应用程序起始地址(存放在FLASH),保留0X08000000~0X0800FFFF的空间为Bootloader使用(共64KB)
void iap_load_app(u32 appxaddr); //跳转到APP程序执行
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen); //在指定地址开始,写入bin
Bootloader使用的存储空间大小可以根据程序运行后的大小来定。
iap.c里面的iap_write_appbin
iapfun jump2app;
u32 iapbuf[512]; //2K字节缓存,4x512=2K
//appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
u32 t;
u16 i=0;
u32 temp;
u32 fwaddr=appxaddr;//当前写入的地址
u8 *dfu=appbuf;
for(t=0;t4)
{
temp=(u32)dfu[3]<<24;
temp|=(u32)dfu[2]<<16;
temp|=(u32)dfu[1]<<8;
temp|=(u32)dfu[0];
dfu+=4;//偏移4个字节 u8转换为u32
iapbuf[i++]=temp;
if(i==512)
{
i=0;
STMFLASH_Write(fwaddr,iapbuf,512);
fwaddr+=2048;//偏移2048 512*4=2048
}
}
if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.
}
iap.c iap_load_ap
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
}
}
main.c
while(1)
{
if(USART_RX_CNT)
{
if(oldcount==USART_RX_CNT)//新周期内,没有收到任何数据,认为本次数据接收完成.
{
applenth=USART_RX_CNT;
oldcount=0;
USART_RX_CNT=0;
printf("用户程序接收完成!\r\n");
printf("代码长度:%dBytes\r\n",applenth);
}else oldcount=USART_RX_CNT;
}
t++;
delay_ms(10);
if(t==30)
{
LED0=!LED0;
t=0;
if(clearflag)
{
clearflag--;
if(clearflag==0)LCD_Fill(30,210,240,210+16,WHITE);//清除显示
}
}
key=KEY_Scan(0);
if(key==WKUP_PRES) //WK_UP按键按下
{
if(applenth)
{
printf("开始更新固件...\r\n");
LCD_ShowString(30,210,200,16,16,"Copying APP2FLASH...");
if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新FLASH代码
LCD_ShowString(30,210,200,16,16,"Copy APP Successed!!");
printf("固件更新完成!\r\n");
}else
{
LCD_ShowString(30,210,200,16,16,"Illegal FLASH APP! ");
printf("非FLASH应用程序!\r\n");
}
}else
{
printf("没有可以更新的固件!\r\n");
LCD_ShowString(30,210,200,16,16,"No APP!");
}
clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示
}
if(key==KEY1_PRES) //KEY1按下
{
if(applenth)
{
printf("固件清除完成!\r\n");
LCD_ShowString(30,210,200,16,16,"APP Erase Successed!");
applenth=0;
}else
{
printf("没有可以清除的固件!\r\n");
LCD_ShowString(30,210,200,16,16,"No APP!");
}
clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示
}
if(key==KEY2_PRES) //KEY2按下
{
printf("开始执行FLASH用户代码!!\r\n");
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}else
{
printf("非FLASH应用程序,无法执行!\r\n");
LCD_ShowString(30,210,200,16,16,"Illegal FLASH APP!");
}
clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示
}
if(key==KEY0_PRES) //KEY0按下
{
printf("开始执行SRAM用户代码!!\r\n");
if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判断是否为0X20XXXXXX.
{
iap_load_app(0X20001000);//SRAM地址
}else
{
printf("非SRAM应用程序,无法执行!\r\n");
LCD_ShowString(30,210,200,16,16,"Illegal SRAM APP!");
}
clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示
}
}
}
②中断向量表的偏移量设置方法
APP存放在FLASH中的设置方法
SCB->VTOR = FLASH_BASE | 0x10000;
APP存放在SRAM中的设置方法
SCB->VTOR = SRAM_BASE | 0x1000;
③设置MDK编译后运行fromelf.exe,生成.bin文件
B:\MDK5\ARM\ARMCC\bin\fromelf.exe –bin -o ..\OBJ\RTC.bin ..\OBJ\RTC.axf
B:\MDK5\ARM\ARMCC\bin\fromelf.exe,keil的具体地址根据安装路径。
其他的修改可以在output查看,比如这个程序里就是RTC。
打开
C:\Users\84329\Desktop\实验15 RTC实时时钟实验_FLASH APP版本\OBJ 可以查找有bin文件
程序下载 先将bootloard下载至开发板
打开串口调试助手 波特率 设为460800 打开文件RTC.bin 发送文件
实验现象:
本实验开机的时候先显示提示信息,然后等待串口输入接收APP程序(无校验,一次性接收),在串口接收
到APP程序之后,即可执行IAP。如果是SRAM APP,通过按下KEY0即可执行这个收到的SRAM APP程序。如果
是FLASH APP,则需要先按下KEY_UP按键,将串口接收到的APP程序存放到STM32的FLASH,之后再按KEY1即
可以执行这个FLASH APP程序。通过KEY2按键,可以手动清除串口接收到的APP程序。