STM32F103C8T6 IAP升级

0x00 前言

STM32有三种烧录程序的方式:烧录器下载、拉高boot1进行串口下载(ISP)还有通过在 Bootloader中进行flash擦除和写入(IAP)。

在进行开发之前,你需要了解一下Bootloader是什么。

Bootloader是嵌入式系统在加电后执行的第一段代码,也称为引导加载程序。它不属于操作系统内核,而是在操作系统内核或用户程序运行之前运行。

举个简单的例子。例如我的拓竹3D打印机,在开机之后,它的Bootloader会进行硬件自检、联网检查固件等操作,而一旦发现了有新版本的固件可以用,那么它就会提示你需要更新后才能使用,更新后它便会开始执行新的固件。

假设这台3D打印机的用到的芯片是STM32,那么它是如何实现这个操作的呢?

首先你的设备flash中,需要存储下一个Bootloader引导分区,然后是一个app应用分区。
当你的决定升级时,那么你就需要将app应用分区的内容擦除,然后用你的新固件去覆盖。
或者你决定不升级,那么在Bootloader程序结束后,直接进入到app应用分区之中。

那么你现在需要做的,就是编写一个Bootloader程序,然后再编写一个APP程序,并确保在bootloader程序结束后,能够准确的进入到app应用之中。你需要确保你的 STM32 flash够大,可以同时存下这两个程序。如果你有特殊需求,你甚至可以编写多个应用分区,以满足你不同的需求。

STM32的flash起始地址是0x0800 0000,八百万,很好记。意味着你每一次烧录程序,都是将程序从这个地址开始烧录;也代表着系统再运行的时候,每一次都是从这个地址开始加载程序。

至于flash结束地址,与你实际所使用的单片机有关系。本文选用了STM32F103C8T6开发板,最常见的小蓝板。它的flash大小是64kb,所以结束地址是0x0801 0000。

0x01 flash分区

做一件事情,最为重要的就是前期规划,不能想到什么做什么。

你需要将stm32的flash区间分为两段甚至多段。

我决定将STM32的 0x800 0000 - 0x800 2000 作为我们的Bootloader 地址段,这个段的大小大概是8KB,我之前写的Bootloader大小大概是5.6KB,分配8KB的原因为要为后续程序升级留有余地,否则牵一发而动全身,到时候重新修改flash分区则会导致很多地方需要修改。
在这里插入图片描述

然后我将剩下的flash分为两个大小一致的分区,打算放下两个app分区在里面。

在这里插入图片描述

这个是APP2在flash分区中所占用的地址段
在这里插入图片描述

0x02 Bootloader

在程序启动的时候,程序会从0x0800 0000地址加载代码,然后将其搬运至RAM(0x0200 0000)中以供执行。

所以我们的Bootloader自然是从0x0800 0000 开始。

在Bootloader中,主要需要实现两个功能:

  1. 选择需要进入的分区
  2. 进入选中的分区

在这里我选择了根据按键的状态进入选中的分区,如果按键1 按下,就进入APP1,如果按键2按下,就进入APP2。举个比较简单的例子,我用STM32F103C8T6写了两个游戏程序,但是如果要来回烧录程序来游玩不同的游戏就很麻烦,所以我用我现在的方式,实现了在开机后,按下按键1就执行游戏1的程序,按下按键2就执行另一个游戏2的程序。

你问我能不能通过编写多级菜单的方式来实现游戏数据的切换?我的回答当然是可以,但是你需要将两个游戏的工程合并到一个工程中,然后还要考虑到单片机的内存因素,编写很多内存管理的代码,略微麻烦,所以通过Bootloader来进行这个选择是比较好的方式。

那么主要靠下面的代码进入到APP1分区或APP2分区。

需要关中断和设置栈顶指针

#include "stm32f10x.h"
#include "key.h"

#define 	appxaddr 			 0x08000000
#define	 	FLASH_APP1_ADDR		 0x08002000
#define 	FLASH_APP2_ADDR 	 0x08006000

void iap_load_app(u32 appxaddr)
{
	SysTick->CTRL = 0X00;//禁止SysTick
	SysTick->LOAD = 0;
    SysTick->VAL = 0;
    __disable_irq();
    jump2app=(iapfun)*(vu32*)(appxaddr+4);
    MSR_MSP(*(vu32*)appxaddr);
    jump2app();	
}

int main()
{
	Key_Init();
	while(1)
	{
		if (Key1 == 1)
			iap_load_app(FLASH_APP1_ADDR);
		if (Key2 == 1)
			iap_load_app(FLASH_APP2_ADDR);
	}
	
	return 0;
}

0x02 APP设置

在APP中,需要修改的地方比较少,需要为你的工程设置flash起始地址和flash结束地址,这样如果你编译的固件超过了规定的大小,会弹出报错来提示flash不够用。

设置完工程后,需要将中断向量地址表偏移指定长度。例如APP1之前有一个0x2000大小的Bootloader,那么你的中断向量地址表就需要偏移0x2000个字节。例如APP2,在这个地址之前有Bootloader和APP1一共0x6000大小,那么就需要偏移0x6000个字节。

如下代码,只需要加一句代码就足够

#include "stm32f10x.h"
#include "delay.h"
#include "led.h"

int main()
{
    /* 中断向量表偏移 */
    SCB->VTOR = FLASH_BASE | 0x2000;  
	
	LED_Init();
	delay_init();

	while(1)
	{
		LED_ON;
		delay_ms(1000);
		LED_OFF;
		delay_ms(1000);
	}

	return 0;
}

0x03 升级程序编写

升级程序可以直接使用正点原子的IAP例程中的iap.c和stmflash.c,修改你的触发逻辑就可以了。

比如说 单机按键1就进入app1,长按按键1 就开始接收新程序并且切入到app1分区

还有 单机按键2就进入app2,长按按键2 就开始接收新程序并且切入到app2分区

0x04 固件烧录

烧录固件时需要注意固件烧录的起始地址,不要覆盖了之前的程序。并且一定要烧录二进制文件,不能烧录16进制文件,16进制的文件包含了一些地址信息可能会导致APP无法运行。

在Keil中需要将你的Bootloader程序设置地址为 0x0800 0000,然后 size 设置为 0x2000
将你的APP1程序设置地址为 0x0800 2000,然后 size 设置为 0x4000
将你的APP2程序设置地址为 0x0800 6000,然后 size 设置为 0x4000

需要烧录三次。也可以通过将bin文件合并的方式只烧录一次

写好了例程,需要的评论区留下邮箱

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IoT_H2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值