STM32CubeIDE IAP Bootloder
新程序的复位中断向量起始地址为 0X08000004+N+M),跳转至新写入 程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程 序,随后跳转至新程序的 main 函数,如图标号②和③所示,同样 main 函数为一个死循环,并 且注意到此时 STM32F4 的 FLASH,在不同位置上,共有两个中断向量表。在 main 函数执行过程中,如果 CPU 得到一个中断请求
·
STM32CubeIDE IAP Bootloder
IAP介绍看: 关于ISP,IAP,ICP(SWD/JTAG)
一、IAP
IAP(In-Application Programming)是用户自己的程序在运行过程中对 User Flash 部分的区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信接口对产品中的固件程序进行更新升级。
STM32F4 正常的程序运行流程:
STM32F4 的内部闪存(FLASH)地址起始于 0x08000000,一般情况下,程序文件就从此 地址开始写入。
手册:
STM32F4 是基于 Cortex-M4 内核的微控制器,其内部通过一张“中断向 量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程 序完成启动,而这张“中断向量表”的起始地址是 0x08000004,当中断来临,STM32F4 的内 部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量 执行中断服务程序。
STM32F4 在复位后,先从 0X08000004 地址取出复位中断向量的地址,并 跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们 的 main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过 程中,如果收到中断请求(发生重中断),此时 STM32F4 强制将 PC 指针指回中断向量表处, 如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中 断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。
加IAP后的运行流程:
STM32F4 复位后,还是从 0X08000004 地址取出复位中断向量的 地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到 IAP 的 main 函数, 如图标号①所示;在执行完 IAP 以后(即将新的 APP 代码写入 STM32F4 的 FLASH,灰底部分。新程序的复位中断向量起始地址为 0X08000004+N+M),跳转至新写入 程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程 序,随后跳转至新程序的 main 函数,如图标号②和③所示,同样 main 函数为一个死循环,并 且注意到此时 STM32F4 的 FLASH,在不同位置上,共有两个中断向量表。
在 main 函数执行过程中,如果 CPU 得到一个中断请求,PC 指针仍强制跳转到地址 0X08000004 中断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据我们设 置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完 中断服务程序后,程序返回 main 函数继续运行,如图标号⑥所示。
二、工程示例
1、bootloder程序:
功能:LED1闪3次后保持亮跳转到app程序,如果判断app程序首地址的值不对(APP为下载或者跳转地址不对)LED2亮。
led.h :
#include "main.h"
#define LED1_ON HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET)
#define LED1_OFF HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET)
#define LED1_TURN HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin)
#define LED2_ON HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET)
#define LED2_OFF HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET)
#define LED2_TURN HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin)
#define LED3_ON HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET)
#define LED3_OFF HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET)
#define LED3_TURN HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin)
bootloder程序:
main.c的main函数while:
while (1){
//更新固件之类的代码写在跳转到APP之前
for(uint8_t i=0;i<6;i++){ //LED1闪3次
LED1_TURN;
HAL_Delay(500);
}
LED1_ON; //LED1亮
iap_load_app(ApplicationAddress); //跳转
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
led.h文件同上
iap.h文件:
#include "stm32f4xx.h"
#define ApplicationAddress 0x800C000 //应用程序起始地址(存放在FLASH):48K
void iap_load_app(uint32_t appAddr); //跳转app执行
iap.c文件:
#include "stm32f4xx.h"
#include "iap.h"
#include "led.h"
typedef void ( * pFunction ) ( void ); //定义一个函数类型的参数.
//ulAddr_App:用户代码起始地址.
void iap_load_app(uint32_t app_addr){
if (((*(__IO uint32_t*)app_addr) & 0x2FFE0000 ) == 0x20020000){
uint32_t JumpAddress = *(__IO uint32_t*) (app_addr + 4);
pFunction JumpToApplication = (pFunction) JumpAddress;
//HAL_RCC_DeInit(); //重置RCC时钟配置为默认复位状态。 (这句不能要。网上有说必须要这句,加上跳转会无法运行)
__set_MSP(*(__IO uint32_t*) app_addr); //设置主堆栈指针
/*在RTOS工程,这条语句很重要,设置为特权级模式,使用MSP指针*/
__set_CONTROL(O);
JumpToApplication();
}else{ //跳转地址的入口不对(判断栈顶地址)
LED2_ON;
}
}
HAL_RCC_DeInit();这个网上会说要,不能加上,加上跳转后会无法运行。
上面红色的部分要随着,bootloder程序要跳转的应用程序修改 否则无法跳转!这一句:
if (((*(__IO uint32_t*)app_addr) & 0x2FFE0000 ) == 0x20020000)
通过判断栈顶地址值是否正确来判断是否已经下载bootloder需要跳转的用户应用程序。因为用户程序的启动文件开始会初始化栈空间,如果栈顶地址正确,说明用户程序已经下载。
编译后查看程序文件大小:
编译后文件大小4.66K。为了方便修改bootloder不给它极限空间(给的大小要是512byte的倍数即0.5K的倍数),给它flash大小给大一点给48K。代码上接写48K。
FLash起始地址0x8000000,加上48*1024byte(49152byte=C000byte),就是0x800C000下一个应用程序的地址。
#define ApplicationAddress 0x800C000 //应用程序起始地址(存放在FLASH):48K
修改内部flash链接文件(STM32F446ZETX_FLASH.ld):
改为(只给48K空间):
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 48K
}
再次编译。
2、app应用程序:
功能:闪烁LED3。
main函数:
while (1){
LED3_TURN;
HAL_Delay(500);
}
编译:
修改内部flash链接文件(STM32F446ZETX_FLASH.ld):
改为:app起始地址为0x800C000,由bootloder程序指定的,大小FLASH共512K,Bootloder使用48K。512-48=464K。
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x800C000, LENGTH = 464K
}
再编译。
打开STM32CubeProgrammer,打开我们的app程序,可以你看见0地址上的栈顶地址,需要根据这个填写bootloder红色处的地址:
if (((*(__IO uint32_t*)app_addr) & 0x2FFE0000 ) == 0x20020000)
或者使用其它编辑器查看,这里用Notepad++的HEX-Editor查看:
Notepad++是显示的byte数据,STM32CubeProgrammer显示的是Uint32数据,这里转换一下得到的一样0x20020000。
也可以在编译的map中查找
_estack = (ORIGIN (RAM) + LENGTH (RAM))
APP程序还得修改 中断偏移量地址 不然APP是无法启动的。
修改的地方是在文件夹Core - > Src文件夹 -> system_stm32f4xx.c文件,其中有个宏定义 VECT_TAB_OFFSET,这个值默认是 0x00,修改为:
添加宏定义 #define USER_VECT_TAB_ADDRESS并且修改VECT_TAB_OFFSET的值为 0xC000,这里必须为0x200的整数倍即512的整数倍。
分析:
#define VECT_TAB_BASE_ADDRESS FLASH_BASE /*!< Vector Table base address field.
MCU Flash的基地址。在这个头文件里定义了相关地址:
#define VECT_TAB_OFFSET 0x0000C000U /*!< Vector Table base offset field.
这个app程序起始地址相对FLASH基地址的偏移。
当添加了宏定义:
#define USER_VECT_TAB_ADDRESS
就会在system_stm32f4xx.c里面设置app程序基址地址:
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
编译程序
三、下载程序
擦除全片Flash,擦除错误多试两次:
不擦除可能会出现:
下载bootloder程序:
或者:
复位运行bootloder程序,发现LED1闪烁后LED2亮了,说明要跳转的app程序没有无效(判断的0xC000栈顶地址)。
下载APP应用程序:
选择APP应用程序,修改烧写的地址(根据程序中要跳转的地址进行修改),下载:
复位程序运行:
LED1闪烁后,LED2没亮说明app应用程序下载成功,跳转之后LED3闪烁,LED1、LED2熄灭。bootloder成功。
更多推荐
所有评论(0)