编写bootloader便于实现串口升级操作,在bootloader启动后判断是否需要升级,如果需要升级就进入升级流程,接收上位机发送的升级包,写入APP地址空间,然后跳转到APP地址执行。若不需要升级就直接跳转到APP地址执行。
首先需要两个工程,一个bootloader工程,一个app工程,对工程进行配置,给bootloader和app程序划分flash区域:
/* --------------------------------------------------------------
*0x08000000~0x0800EFFF (60k) : 存放bootloader程序
*0x0800F000~0x0801EFFF (64k) : 存放APP程序
*0x0801F000~0x0801FFFF (4k) : 存放用户配置
*------------------------------------------------------------- */
#define BOOTLOADER_ADDRESS_START (FLASH_BASE)
#define APPLICATION_ADDRESS_START (FLASH_BASE | 0xF000)
#define APPLICATION_ADDRESS_END (FLASH_BASE | 0x1EFFF)
- Target标签页:
- bootloder空间大小设置:
- app空间大小设置:
-
Linker标签页:勾选Linker-Use Memory Layout from Target Dialog,以便看到map中地址的改变。
可以看到bootloader和app工程的map文件中,RESET的地址已经变成之前设置的起始地址和大小了
-
接下来是代码处理,可以使用以下函数,传入程序起始地址
typedef void (*pFunction)(void); /*跳转函数类型声明 */ pFunction Jump_To_Application; /* 跳转函数 */ void JumpToAPP(uint32_t ApplicationAddress) { uint32_t JumpAddress = 0; /* 跳转地址 */ if(((*(uint32_t*)ApplicationAddress) & 0x2FFE0000)==0x20000000) /*检查栈顶地址是否合法.*/ { SCB->VTOR = FLASH_BASE | ApplicationAddress;/* 重定义中断地址向量表 */ JumpAddress = (*(uint32_t*)(ApplicationAddress + 4)); Jump_To_Application =(pFunction)JumpAddress; __set_MSP(*(uint32_t*)ApplicationAddress); Jump_To_Application(); } }
写到这里还不能实现跳转,还需要修改启动文件里的宏定义,从下图.S文件中可以看到stm32在执行main函数之前会先执行SystemInit函数,而SystemInit函数只是对向量表地址进行了一个简单的赋值,我们把APP工程里的VECT_TAB_OFFSET修改为APP程序起始地址0xF000U即可,bootloader不用改,因为bootloader本身就放在0x8000000开始的。
完成上面所有操作后,就可以在bootloader代码中直接调用JumpToAPP(0x800F000)实现跳转到APP代码执行。 -
生成hex文件和.bin文件
- Output标签页:勾选Create HEX File,生成可供烧录的hex文件。
- User标签页:勾选After Bulid/Rebuild下的Run #1,并且在后面的输入框中输入以下内容,生成.bin文件:fromelf --bin --output .\Objects\STM32G070RB_app.bin .\Objects\STM32G070RB_app.axf
(具体路径根据自己工程文件夹填写)
- C/C++标签页:Include paths里需要引入需要的头文件,就跟makefile的-I参数一样,告诉编译器头文件去哪里找。
No pains, no gains.