一、背景
学习,之后公司可能会用Bootloader进行升级,因为百度上的,我找到的都是挺混乱的,我就自己试成功了就贴出来分享顺带做做笔记了。
(介于部分开发者刚接触,说明一下 Bootloader 一般叫引导/启动程序,app就是通常编写的业务代码 一般叫应用程序)
二、实现思路
我用的是STM32F103VETx芯片
Bootloader其实就是一段启动程序,你可以理解为两份代码,分别放在了用户FALSH区域的不同位置上。一般bootloader的位置在前,App在Bootloader的后面。
芯片启动后,首先运行的就是Bootloader部分的代码,它可以用来做一些硬件的初始化,当初始化完成之后跳转到对应的应用程序(APP)中去。
我们可以将用户flash区域分为两个区,一个是Bootloader程序区(0x0800 0000 - 0x0800 2000 )大小为8K Bytes,剩下的为APP程序区(0x0800 2000 - 0x0808 0000)大小为504K Byte。
STM32F103VETx总共有512K Bytes的falsh大小
芯片上电时先运行(Bootloader)启动程序,然后跳转到(APP)应用程序区执行应用程序。
三、Bootloader程序
新建Bootloader.c和Bootloader.h
Bootloader.c
#include "Bootloader.h"
#include "stdint.h"
#include "usart.h"
#include "string.h"
__asm void MSR_MSP(uint32_t addr)
{
MSR MSP, r0
BX r14;
}
void iap_load_app(void)
{
APP_FUNC jump2app;//定义一个函数指针
/* 栈顶地址是否合法(这里sram大小为8k) */
if(((*(uint32_t *)APP_ADDR)&0x2FFFE000) == 0x20000000)
{
/* 设置栈指针 */
MSR_MSP(APP_ADDR);
/* 获取复位地址 */
jump2app=(APP_FUNC)*(volatile uint32_t *)(APP_ADDR+4);
/* 设置栈指针 */
__set_MSP(*(volatile uint32_t *)APP_ADDR);
#ifdef BOOTLOADER_LOG
HAL_UART_Transmit(&huart1,(uint8_t*)"Bootloader end load app\r\n",(uint16_t)strlen("Bootloader end load app\r\n"),0xf);
#endif
/* 跳转之前关闭相应的中断 */
CLOSE_ALL_INT();
/* 跳转至APP */
jump2app();
}
#ifdef BOOTLOADER_LOG
else
{
HAL_UART_Transmit(&huart1,(uint8_t*)"APP Not Found!\n",(uint16_t)strlen("APP Not Found!\n"),0xf);
}
#endif
}
提一嘴,这里关闭全部中断是最为保险的,怕打断跳转过程。后面的APP的main里面也要开启全部中断,否则跟中断的函数全部无效
Bootloader.h
#ifndef _BOOTLOADER_H_
#define _BOOTLOADER_H_
#include "main.h"
#define APP_ADDR 0x08002000 //应用程序首地址定义
/*选择性开启相应的LOG信息*/
#define BOOTLOADER_LOG
#define CLOSE_ALL_INT() __set_PRIMASK(1) //关闭所有中断
typedef void (*APP_FUNC)(); //函数指针类型定义
void iap_load_app(void); //跳转函数
#endif
请注意,要自己按照芯片来修改APP的首地址!!!只是我这里是0x08002000而已,你们可以跟我一样,但还请注意不同系列芯片的起始地址问题!!!
代码上就完成了相应的编写,下面是要修改Keil的下载的FLASH地址与长度
在前面说了Bootloader程序区(0x0800 0000 - 0x0800 2000 )大小为8K Bytes,所以这里是0x0800 0000 是起始而0x2000是长度,0x0800 0000 + 0x2000 = 0x0800 2000
如果你想顺便编译出相应Bootloader的Bin文件,则设置
fromelf.exe --bin -o "STM32F103VET_Bootloader.bin" "#L"-----------可以自己改名字
然后在main.c里面写
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
#ifdef BOOTLOADER_LOG
MX_USART1_UART_Init();
#endif
/* USER CODE BEGIN 2 */
iap_load_app(); //跳转到APP的首地址
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
如果你是第一次编写的话,建议在下载的设置里,选中全片擦除。第二次之后你在设置成
Erase Sectors的选项即可,这里是为了确保程序的正确性,不被之前的代码受影响
然后编译下载,到这里就是弄好了Bootloader的部分了
然后你可以在串口看到这个,但是不要慌,我们先去整APP那部分的程序
这里我用的是usart自带的发送函数,并没有使用printf调用,为了减少相应的步骤
四、APP程序
新建好一个工程后,我们先修改下载的地址。
这部分就是APP的FLASH区域 (0x0800 2000 - 0x0808 0000)
下载的部分选这个,APP程序区域不要用全片擦除,会把Bootloader代码擦掉的!!!!!
然后在设置中添加全局宏(注意下一行开头的头号,要一并复制过去)
,USER_VECT_TAB_ADDRESS
之后全局搜索VECT_TAB_OFFSET
找到这里后,请改后面的0x00000000U改成0x00002000U,这个是用于NVIC向量表偏移的宏定义,因为前0x2000被我们用于Bootloader了,所以这里要偏移0x00002000U。不然后面你用中断相关的操作都是无效/错误的。
然后再main里面写
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
__set_PRIMASK(0);
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Transmit(&huart1,(uint8_t*)"app ok\r\n",(uint16_t)strlen("app ok\r\n"),0xf);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
请注意这里要打开全部中断!!!
编译、下载,然后打开串口工具
可以看到
这样就完成了!!!!!
如果其中完成后查看串口没有任何信息打印的话,请在STM32CubeMx里面确认是否开启了USART1或波特率是否正确。
如果都没有问题了,可以试试中断相关的操作,这里贴了一份之前用中断接收USART1数据的文章
有相关问题可以下面评论,我时常会用到CSDN的,下面的链接可能有些人看不懂,我双休的时候整一整