【STM32】STM32 IAP BootLoader

1.IAP简介

IAP,即在线应用编程,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后,可以方便地通过遗留的通信口对产品中的固件程序进行更新升级。

通常在实现IAP功能时,即用户程序运行中作自身的更新操作, 需要在设计固件程序时编写
两个项目代码, 第一个项目程序不执行正常的功能操作, 而只是通过某种通信方式(如 USB、 USART)接收程序或数据, 执行对第二部分代码的更新; 第二个项目代码才是真正的功能代码。

这两部分项目代码都同时烧录在 User Flash 中, 当芯片上电后, 首先是第一个项目代码开始运行, 它作如下操作:

1.检查是否需要对第二部分代码进行更新

2.检查不需要更新则转到4

3.执行更新操作

4.跳转到第二部分代码执行

第一部分代码必须通过其它手段, 如 JTAG 或 ISP 烧入; 第二部分代码可以使用第一部分代码 IAP 功能烧入, 也可以和第一部分代码一起烧入, 以后需要程序更新时再通过第一部分 IAP 代码更新。
第一个项目代码称为bootloader,第二个项目代码称为APP程序。

他们存放在 STM32 FLASH 的不同地址范围, 一般从最低地址区开始存放 Bootloader, 紧跟其后的就是 APP 程序(注意, 如果 FLASH 容量足够,是可以设计很多 APP 程序)。

2.STM32正常的程序运行流程

STM32的内部flash的地址起始于0x08000000,STM32内部通过一张"中断向量表"来响应中断

image

程序启动后:

首先从中断向量表中取出复位中断向量执行复位中断程序完成启动, 这张“中断向量表” 的起始地址是 0x08000004, 当中断来临, STM32 的内部硬件机制亦会自动将 PC 指针定位到“中断向量表” 处, 并根据中断源取出对应的中断向量执行中断服务程序。

在上图中, STM32 在复位后, 先从 0X08000004 地址取出复位中断向量的地址, 并跳转到复位中断服务程序, 如图标号①所示; 在复位中断服务程序执行完之后, 会跳转到们的 main 函数, 如图标号②所示; 而我们的 main 函数一般都是一个死循环, 在 main 函数执行过程中, 如果收到中断请求(发生重中断) ,此时 STM32 强制将 PC 指针指回中断向量表处, 如图标号③所示; 然后, 根据中断源进入相应的中断服务程序, 如图标号④所示; 在执行完中断服务程序以后,程序再次返回 main 函数执行, 如图标号⑤所示。

3.加入IAP后程序的运行流程

image

在该流程图中, STM32 复位后, 还是从 0X08000004 地址取出复位中断向量的地址, 并跳转到复位中断服务程序, 在运行完复位中断服务程序之后跳转到IAP 的 main 函数, 如图标号①所示, 此部分同前面 STM32 正常流程图一样; 在执行完 IAP 以后(即将新的 APP 代码写入 STM32 的 FLASH, 灰底部分。 新程序的复位中断向量起始地址为 0X08000004+N+M) , 跳转至新写入程序的复位向量表, 取出新程序的复位中断向量的地址, 并跳转执行新程序的复位中断服务程序, 随后跳转至新程序的 main 函数, 如图标号②和③所示, 同样 main 函数为一个死循环, 到此时 STM32 的 FLASH, 在不同位置上, 共有两个中断向量表。

在 main 函数执行过程中, 如果 CPU 得到一个中断请求, PC 指针仍强制跳转到地址 0X08000004 中断向量表处, 而不是新程序的中断向量表, 如图标号④所示; 程序再根据我们设置的中断向量表偏移量, 跳转到对应中断源新的中断服务程序中, 如图标号⑤所示; 在执行完中断服务程序后, 程序返回 main 函数继续运行, 如图标号⑥所示。

通过以上两个过程的分析, 我们知道 IAP 程序必须满足两个要求:
1.新程序必须在IAP程序之后的某个偏移量为x的地址开始
2.必须将新程序的中断向量表相应的移动,移动的偏移量为x;

我们有 2 个 APP 程序, 一个为 FLASH 的 APP, 程序在 FLASH 中运行, 另外一个位 SRAM 的 APP, 程序运行在 SRAM 中, 上图虽然是针对 FLASH APP来说的, 但是在 SRAM 里面运行的过程和 FLASH 基本一致, 只是需要设置向量表的地址为 SRAM 的地址。

3.1 中断向量表的偏移量设置方法

系统启动的时候,会首先调用SystemInit函数初始化时钟系统,同时完成中断向量表的设置,代码如下

/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif

VTOR寄存器存放的是中断向量表的起始地址。默认情况下VECT_TAB_SRAM是没有定义的,所以执行SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
对于flash app,设置为FLASH_BASE+偏移量0x10000(具体flash app的地址空间配置见下章),所以需要再FLASH APP的main函数最开头处添加如下代码实现中断向量表的起始地址的重设

image

当使用SRAM APP的时候,我们设置起始地址为:SRAM_BASE + 0x1000,同样添加到SRAM APP的main函数开始处,添加如下:

image

以上就完成了中断向量表偏移量的设置,如果此处设置和后面程序空间的配置不匹配,就可能会出现跳转不到正确程序起始地址导致app运行不起来的情况。

4.应用程序空间配置

我的主控芯片是STM32F4ZGT6,有1024KB的Flash和192KB的SRAM。

Flash程序和SRAM程序和普通程序一样,只是IROM空间和IRAM空间需要特殊配置一下。

此处先介绍一下Keil MDK中IROM1、IROM2、IRAM1和IRAM2表示的意义:

1.IROM1:表示内部只读存储器区域1(Internal ROM 1)。这是一个用于存放程序代码的区域,通常是Flash内存的一部分。在Keil中,IROM1的配置包括起始地址和大小,用于指定代码存储的具体位置和空间大小。

2.IROM2:表示内部只读存储器区域2(Internal ROM 2)。这通常用于指定另一个代码存储区域,可能用于存放程序的第二部分或者其他不需要连续存储的代码段。在某些应用中,比如有多个Flash区域的微控制器,IROM2可以用来指定第二个Flash区域的配置。

3.IRAM1:表示内部随机访问存储器区域1(Internal RAM 1)。这是用于存放变量的RAM区域,通常是微控制器内部的SRAM。IRAM1的配置包括起始地址和大小,用于指定变量存储的具体位置和空间大小。

4.IRAM2:表示内部随机访问存储器区域2(Internal RAM 2)。这可能是微控制器内部的另一个RAM区域,用于存放特定的变量或者数据,以实现更快的访问速度或者隔离不同功能的内存需求。在一些高级的微控制器中,IRAM2可能指的是特定用途的内存区域,比如CCM(Core Coupled Memory)。

4.1 IAP-Bootloader空间配置

此处IROM1size设置成64k,明确一下范围

image

实际编译出来的bin为27k,预留出64k是足够的,还是看具体需求,只要确保APP起始地址在

image

BootLoader以后,而且偏移量为0x200的倍数即可(M3权威指南P104页有介绍)

4.2 Flash APP空间配置

image

IROM1从Flash 64K地址位置起始,预留960k,如果有两个以上的flash app还可以再行划分IRAM1预留了128k的内存空间(0x20000)

4.3 SRAM_APP空间配置

image

SRAM app将存储地址设置了SRAM上,即0x20001000存储起始,尺寸大小为0x19000

SRAM app的运行范围紧随存储地址后面,从0x2001A000,运行大小为0x6000

5.我的IAP BootLoader程序

5.1 IAP的main函数

主函数功能流程如下:

image

key0的功能是将RXBUF中的flash app数据拷贝到预设的flash地址:

iap_write_appbin(FLASH_APP1_ADDR,USART1_RX_BUF,applenth);//更新FLASH代码 

函数实现原型

//appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
	u32 t;
	u16 i=0;
	u32 temp;
	u32 fwaddr=appxaddr;//当前写入的地址
	u8 *dfu=appbuf;
	for(t=0;t<appsize;t+=4)
	{						   
		temp=(u32)dfu[3]<<24;   
		temp|=(u32)dfu[2]<<16;    
		temp|=(u32)dfu[1]<<8;
		temp|=(u32)dfu[0];	  
		dfu+=4;//偏移4个字节
		iapbuf[i++]=temp;	    
		if(i==512)
		{
			i=0; 
			STM32_FLASH_Write(fwaddr,iapbuf,512);
			fwaddr+=2048;//偏移2048  512*4=2048
		}
	} 
	if(i)STM32_FLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.  
}

key2的功能是执行flash app,就是直接跳转到预设的flash地址

if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{	 
    iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}else 
{
    printf("非FLASH应用程序,无法执行!\r\n");
    LCD_ShowString(30,210,200,16,16,"Illegal FLASH APP!");	   
}	

函数实现:

typedef  void (*iapfun)(void);		   
iapfun jump2app; 
u32 iapbuf[512]; 	//512字节缓存 

//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
	MSR MSP, r0 			//set Main Stack value
	BX r14
}

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

key3的功能是执行SRAM app代码,直接跳到SRAM的地址上即可:

if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判断是否为0X20XXXXXX.
{	 
    iap_load_app(0X20001000);//SRAM地址
}else 
{
    printf("非SRAM应用程序,无法执行!\r\n");
    LCD_ShowString(30,210,200,16,16,"Illegal SRAM APP!");	   
}		

5.2 USART驱动

usart采用中断接受,将接受到的数据存在缓存的buf里

#define USART1_REC_LEN		55*1024 //定义最大接收字节数 55K

u8 USART1_RX_BUF[USART1_REC_LEN] __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000. 


// usart的中断服务子函数
void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 r;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
	{
		r =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		if(USART1_RX_CNT<USART1_REC_LEN)
		{
			USART1_RX_BUF[USART1_RX_CNT]=r;
			USART1_RX_CNT++;			 									     
		}	 	
	} 
} 	


6.功能测试

使用串口工具,打开编译好的flash_app的bin文件,点击发送文件就可以编译好的bin文件烧录到板子中。

image

然后按下key1,key2就可以运行起flash app了。

7.Q&A:

1.我编译出的bin文件大小为64628大小,通过usart烧写flash app后,按下key1_up后,正常应该是显示

image

但是实际显示了Illegal FLASH APP!

image

猜测原因应该是数据没接收全,排查IAP程序发现,usart的buf大小设置最大为55k,而我的程序

image

为64k,所以将这个buf大小改为64k应该就可以了, 实测确实是这个原因

2.当flash app或sram app运行有问题的话,有限排查IROM和IRAM的配置,检查是否出现内存越界或冲突的情况

posted @ 2024-12-03 00:15  Emma1111  阅读(83)  评论(0编辑  收藏  举报