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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值