STM32F103X的bootloader制作步骤,HAL库、用cubeMX。实现flash的跳转

一、因为要做模块的升级所以需要用到bootloader。先捋清楚一下各步骤:

1.划分flash区间。我的模块是stm32f103RB,flash大小是128k字节(0x2000)。我打算将flash分为三个部分:bootloader(0x8000000~0x8004000)、应用程序代码(0x8004000~0x8012000)、待升级的IAP程序(0x8012000~0x8020000)。

2.划分FRAM区间,总共0x2000字节,bootloader要记录是否需要IAP所以需要占用一些FRAM空间(0x0000~0x0100),应用程序占用(0x0100~0x2000)。

3.bootloader先实现跳转到flash应用程序app的目标位置。(IAP先不考虑)

4.跳转后成功执行app。

二、先用cubeMX生成bootloader:

1.选择芯片

2.选上需要的功能,首先是必须的RCC、SYS、IWDG。因为要用到FRAM所以需要用到I2C2。

3.对照原理图核对管脚是否正确,并且增加LED灯用来辅助调试boot

FRAM-WP、LED3、LED4、LED5都设置为output。其他默认

4.配置时钟为72M

5.详细配置,默认即可

6.工程设置

然后生成代码即可。在代码中cubeMX只是初始化了配置,但是并没有使用,比如开启了看门狗,但是并未喂狗,需要自行喂狗。因为bootloader的特殊性,并不需要喂狗,也并不需要有什么内容是放在while(1)中的,因为bootloader只运行一次,主要就是执行跳转指令。

7.生成代码后打开工程,修改工程配置

因为bootloader大小设置为0x4000大小,所以size设置一下

然后就是debug设置为J-LINK,SWD

创建代码后如何验证程序可用呢?我在while(1)里加了延时和翻转小灯的状态,这样每循环两次小灯就闪烁一次。在这里我是一次成功的。当然,需要喂狗。

那么如何跳转到flash目标地址呢?

app_iap.c

#include "app_iap.h"

__asm void MSR_MSP ( uint32_t ulAddr ) 
{
    MSR MSP, r0 			                   //set Main Stack value
    BX r14
}

//跳转到应用程序段
//ulAddr_App:用户代码起始地址.
void IAP_ExecuteApp ( uint32_t ulAddr_App )
{
	pIapFun_TypeDef pJump2App; 
	
	if ( ( ( * ( __IO uint32_t * ) ulAddr_App ) & 0x2FFE0000 ) == 0x20000000 )	  //检查栈顶地址是否合法.
	{ 
		pJump2App = ( pIapFun_TypeDef ) * ( __IO uint32_t * ) ( ulAddr_App + 4 );	//用户代码区第二个字为程序开始地址(复位地址)		
		MSR_MSP( * ( __IO uint32_t * ) ulAddr_App ); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		pJump2App ();								                                    	//跳转到APP.
	}
}		

app_iap.h

/*****************************************************************************
 * FileName:        app_iap.h  
 * Processor:       Stm32F103
 * Company:         TY
 * Overview:		    IAP程序升级
 * Version:         1.5
 *****************************************************************************/
#ifndef _APP_IAP_H_
#define _APP_IAP_H_

#include "stm32f1xx_hal.h"

/* 类型定义 ------------------------------------------------------------------*/
/************************** IAP 数据类型定义********************************/
typedef  void ( * pIapFun_TypeDef ) ( void ); //定义一个函数类型的参数.

#define USER_ADDR_APP		  0x08004000 	// 应用程序的起始地址
#define USER_SIZE_APP		  0xE000    	// 应用程序的大小

#define USER_ADDR_UPDATE	  0x08012000 	// 存放升级文件的起始地址
#define USER_SIZE_UPDATE	  0xE000    	// 升级程序的大小

extern void IAP_ExecuteApp ( uint32_t ulAddr_App );

#endif

加上这两个文件,然后在主函数中调用IAP_ExecuteApp(USER_ADDR_APP);

为了验证这个bootloader好用,我们在这个bootloader的小灯初始化时,让LED3常亮,LED4、LED5长灭。

让应用程序初始化小灯时LED3为灭,并且LED5常量,LED4闪烁。这样如果成功的话,那么理论上LED3亮一下,随后熄灭,LED4闪烁,LED5常亮。

当然,要设置一下应用程序的配置,修改起始地址和大小

之后重新编译,烧录,但是结果并不对,LED3几乎是常亮,间歇短暂熄灭的时候LED4、LED5也短暂闪烁一下,随后熄灭。

找了很久的原因,以为是触发了看门狗,或者是没有关闭中断。但其实都不是,是因为向量表没有更改

在应用程序的中的这个文件VECT_TAB_OFFSET默认是0x00000000U,但是我们应该更改为起始地址中0x08004000的0x00004000U。

编译后,就可以正常跳转了。但是根据同事的意见,还是要在跳转指令前关闭一下全局中断才好。

__disable_irq();   // 关闭总中断
__enable_irq();    // 开启总中断

于是我在跳转指令前加了关闭总中断,但又出现跳转失败的情况。通过debug发现可以跳转到应用程序成功,但是还是会在初始化的过程中死机,所以我在应用程序中的第一句加上打开总中断,这时跳转成功并且应用程序运行正常了。

搞不清楚这个原因,时间有限暂且不研究了,不过这是一个值得尝试的地方。我还是暂且不用这两句话吧。

到此,bootloader的跳转功能实现了,接下来要实现程序升级。

发布了26 篇原创文章 · 获赞 0 · 访问量 3000

猜你喜欢

转载自blog.csdn.net/nianzhu2937/article/details/102742483