IAP在编程升级
以STM32F103ZET6为例讲解,
FLASH 512KB,SRAM64KB.
让APP程序加载在FLASH里运行,在SRAM运行的先不讲解。
IAP执行流程
当加入 IAP 程序之后,程序运行流程如图。
APP程序的生成步骤
1.APP 程序起始地址设置方法
我们设置起始地址(Start)为 0X08010000,即偏移量为 0x10000(64K 字节,
即留给 BootLoader 的空间),因而,留给 APP 用的 FLASH 空间(Size)为 0x80000-
0x10000=0x70000(448K 字节)大小了。
注意:需要确保 APP 起始地址在 Bootloader 程序结束位置之后,并且偏移量为 0X200 的倍数即可
2.中断向量表的偏移量设置方法
VTOR 寄存器存放的是中断向量表的起始地址。默认的情况它由 BOOT 的启动模式决定,
对于 F103 来说就是指向 0x0800 0000 这个位置,也就是从默认的启动位置加载中断向量等信息,不过 ST 允许重定向这个位置,这样就可以从 Flash 区域的任意位置启动我们的代码了。
我们可以通过调用 sys.c 里面的 sys_nvic_set_vector_table 函数实现,该函数定义如下:
/**
* @brief 设置中断向量表偏移地址
* @param baseaddr: 基址
* @param offset: 偏移量
* @retval 无
*/
void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset)
{
/* 设置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留 */
SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00);
}
该函数用于设置中断向量偏移,baseaddr 为基地址(即 APP 程序首地址),Offset 为偏移
量,需要根据自己的实际情况进行设置。比如 FLASH APP 设置中断向量表偏移量为 0x10000,调用情况如下:
/* 设置中断向量表偏移量为 0X10000 */
sys_nvic_set_vector_table(FLASH_BASE,0x10000);
3.设置MDK编译后运行fromelf.exe,生成.bin
因为使用MDK编译器默认生成的代码是.hex文件,并不方便我们用作 IAP更新,我们希望生成的文件是.bin 文件,这样可以方便进行 IAP 升级,这里我们通过 MDK 自带的格式转换工具 fromelf.exe来实现.axf 文件到.bin 文件的转换。该工具在 MDK 的安装\ARM\ARMCC\bin 文件夹里面。
本实验,我们可以通过在 MDK 点击 Options for Target→User 选项卡,在 AfterBuild/Rebuild一栏中,勾选 Run #1,我们推荐使用相对地址,在勾选的同一行后的输入框并写入命令行:fromelf --bin -o …\Output@L.bin …\Output%L
设置生成编译结果文件名
MDK 生成.bin 文件设置方法
通过这一步设置,我们就可以在 MDK 编译成功之后,调用 fromelf.exe,…\Output%L 表
示当前编译的链接文件(…\是相对路径,表示上级目录,编译器默认从工程文件*.uvprojx 开始查找,根据我的工程文件 Output 的位置就能明白路径的含义),指令–bin –o …\Output@L.bin表示在 Output 目录下生成一个.bin 文件,@L 在 Keil 的下表示 Output 选项卡下的 Name ofExecutable 后面的字符串,即在 Output 文件夹下生成一个 atk_f103.bin 文件。在得到.bin 文件之后,我们只需要将这个 bin 文件传送给单片机,即可执行 IAP 升级。
实操
归纳一下,使用IAP的操作流程。
1.确定一下所写的bootload程序的大小 (bootload工程生成的.map文件查看)
2.根据bootload程序的大小,确定存放APP程序的flash地址。(bootload程序要预留合适的内存大小,且flash应用程序的起始地址要为0x200的整数倍)。
3.app程序里要设置下中断向量表偏移。设置存放app地址-0x0800 0000 等于多少,就要偏移多少。
4.将app程序生成.bin文件
5.将bootload程序下载进单片机
6.使用工具将app的.bin文件发送给bootload,然后bootload会将这些数据填充到第2步指定的flash地址。
7.按下按键跳转到app程序执行。
bootload代码讲解
main.c程序的代码
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/STMFLASH/stmflash.h"
#include "./IAP/iap.h"
int main(void)
{
uint8_t t;
uint8_t key;
uint32_t oldcount = 0; /* 老的串口接收数据值 */
uint32_t applenth = 0; /* 接收到的app代码长度 */
uint8_t clearflag = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "IAP TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "KEY_UP: Copy APP2FLASH!", RED);
lcd_show_string(30, 130, 200, 16, 16, "KEY1: Run FLASH APP", RED);
lcd_show_string(30, 150, 200, 16, 16, "KEY0: Run SRAM APP", RED);
while (1)
{
if (g_usart_rx_cnt)
{
if (oldcount == g_usart_rx_cnt) /* 新周期内,没有收到任何数据,认为本次数据接收完成 */
{
applenth = g_usart_rx_cnt;
oldcount = 0;
g_usart_rx_cnt = 0;
printf("用户程序接收完成!\r\n");
printf("代码长度:%dBytes\r\n", applenth);
}
else oldcount = g_usart_rx_cnt;
}
t++;
delay_ms(100);
if (t == 3)
{
LED0_TOGGLE();
t = 0;
if (clearflag)
{
clearflag--;
if (clearflag == 0)
{
lcd_fill(30, 190, 240, 210 + 16, WHITE); /* 清除显示 */
}
}
}
key = key_scan(0);
if (key == WKUP_PRES) /* WKUP按下,更新固件到FLASH */
{
if (applenth)
{
printf("开始更新固件...\r\n");
lcd_show_string(30, 190, 200, 16, 16, "Copying APP2FLASH...", BLUE);
if (((*(volatile uint32_t *)(0X20001000 + 4)) & 0xFF000000) == 0x08000000) /* 判断是否为0X08XXXXXX */
{
iap_write_appbin(FLASH_APP1_ADDR, g_usart_rx_buf, applenth); /* 更新FLASH代码 */
lcd_show_string(30, 190, 200, 16, 16, "Copy APP Successed!!", BLUE);
printf("固件更新完成!\r\n");
}
else
{
lcd_show_string(30, 190, 200, 16, 16, "Illegal FLASH APP! ", BLUE);
printf("非FLASH应用程序!\r\n");
}
}
else
{
printf("没有可以更新的固件!\r\n");
lcd_show_string(30, 190, 200, 16, 16, "No APP!", BLUE);
}
clearflag = 7; /* 标志更新了显示,并且设置7*300ms后清除显示 */
}
if (key == KEY1_PRES) /* KEY1按键按下, 运行FLASH APP代码 */
{
if (((*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000) /* 判断FLASH里面是否有APP,有的话执行 */
{
printf("开始执行FLASH用户代码!!\r\n\r\n");
delay_ms(10);
iap_load_app(FLASH_APP1_ADDR);/* 执行FLASH APP代码 */
}
else
{
printf("没有可以运行的固件!\r\n");
lcd_show_string(30, 190, 200, 16, 16, "No APP!", BLUE);
}
clearflag = 7; /* 标志更新了显示,并且设置7*300ms后清除显示 */
}
if (key == KEY0_PRES) /* KEY0按下 */
{
printf("开始执行SRAM用户代码!!\r\n\r\n");
delay_ms(10);
if (((*(volatile uint32_t *)(0x20001000 + 4)) & 0xFF000000) == 0x20000000) /* 判断是否为0X20XXXXXX */
{
iap_load_app(0x20001000); /* SRAM地址 */
}
else
{
printf("非SRAM应用程序,无法执行!\r\n");
lcd_show_string(30, 190, 200, 16, 16, "Illegal SRAM APP!", BLUE);
}
clearflag = 7; /* 标志更新了显示,并且设置7*300ms后清除显示 */
}
}
}
usart.c
讲解一下逻辑,当使用串口助手将,文本数据发送给单片机时,文本数据会从地址0X20001000(SRAM)存放,全局变量。当按下KEY_UP的时候。会根据0X20001000+4处地址进行判断是否为FLASH应用程序(该程序兼容SRAM应用程序)。如果是,那么将数据写入FLASH区,这里FLASH应用程序开始的存放地址为0X08010000。
这是因为这里的bootload程序大概为34.11KB,0x08010000-0x08000000 = 0x10000,为64KB。这样就避免了bootload程序被误改。
当按下KEY1时,判断下FLASH里面是否有APP代码,如果,就跳转过去执行。
重点函数iap_load_app()
因为FLASH应用程序的起始地址存放的是栈顶的地址,取出来看下,栈顶地址是不是合法(SRAM的起始地址)。合法的话,初始化一下MSP指针(让MSP指针指向栈顶),然后跳转去执行app程序。
/**
* @brief 跳转到应用程序段(执行APP)
* @param appxaddr : 应用程序的起始地址
* @retval 无
*/
void iap_load_app(uint32_t appxaddr)
{
if (((*(volatile uint32_t *)appxaddr) & 0x2FFE0000) == 0x20000000) /* 检查栈顶地址是否合法.可以放在内部SRAM共64KB(0x20000000) */
{
/* 用户代码区第二个字为程序开始地址(复位地址) */
jump2app = (iapfun) * (volatile uint32_t *)(appxaddr + 4);
/* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */
sys_msr_msp(*(volatile uint32_t *)appxaddr);
/* 跳转到APP */
jump2app();
}
}
APP代码讲解
app工程里得设置一下程序起始地址,这里程序起始地址为0X0810 000,距离0X0800 0000 为0X10000。大概64KB,这时因为bootload代码大小为32.11KB,64KB远大于32.11KB,防止bootload代码被误改。
设置了程序起始地址,也要设置对应的中断向量表偏移。这里设置偏移为0X10000,FLASH基地址为0X0800 0000.
0x080001dd
0x0801022d
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!