基于STM32的IAP升级程序

255 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

介绍

IAP(In Application Programming)即在应用编程,IAP 是用户自己的程序在运行过程中对User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。 通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一个项目代码开始运行。 简单来说,就是程序首先运行在boot升级区,检测是否有升级程序,当有升级程序的时候,用户将升级的固件通过某种通信接口(如串口)传输到开发板的某个位置中,之后放在APP的位置。当没有升级程序,直接运行APP中的程序。

功能介绍

将最新的产品固件通过串口上传到开发板中,按下按键1,开发板将固件更新到设置的功能程序存放地址,按下按键2,执行功能程序。

20220526162110.png

硬件设计

使用的MCU为STM32F103 使用元器件为两个按键和两个LED灯

软件设计

软件设计流程如下

  1. 编写需要升级的产品功能程序
  2. 设置产品功能程序在keil 5中Target的程序的Flash起始地址和大小,并且程序中设置中断向量表的偏移量
  3. 生成产品功能程序的固件bin文件
  4. 设置IAP升级程序存放的起始地址和大小
  5. 串口接收产品功能程序
  6. 将最新的升级程序放入到功能程序的Flash位置
  7. 跳转到功能程序的Flash地址处

具体实现

设计功能程序

主要为两处:

  1. 在main()函数中最开始处,添加
SCB->VTOR = FLASH_BASE|0x9C00; 
  1. 在keil 5中Target的程序的Flash起始地址和大小

20220526121427.png

==生成bin文件==:我们通过在 MDK 点击 Options for Target→User 选项卡,在 After Build/Rebuild 栏, 勾选 Run #1,并写入:D:\32\Keil 5\ARM\ARMCC\bin\fromelf.exe --bin -o ..\OBJ\LED.bin ..\OBJ\LED.axf

20220526155147.png

编译后,就会生成.bin文件了。

IAP实现程序

  1. IAP写入程序 将暂存在RAM中的程序存放到Flash的对应地址中 appxaddr:应用程序的起始地址 appbuf:应用程序CODE. appsize:应用程序大小(字节).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
	u16 t;
	u16 i=0;
	u16 temp;
	u32 fwaddr=appxaddr;//当前写入的地址
	u8 *dfu=appbuf;
	for(t=0;t<appsize;t+=2)
	{						    
		temp=(u16)dfu[1]<<8;
		temp+=(u16)dfu[0];	  
		dfu+=2;//偏移2个字节
		iapbuf[i++]=temp;	    
		if(i==1024)
		{
			i=0;
			STMFLASH_Write(fwaddr,iapbuf,1024);	
			fwaddr+=2048;//偏移2048  16=2*8.所以要乘以2.
		}
	}
	if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.  
}
  1. 运行地址偏移程序 程序转移到指定地址运行 跳转到应用程序段 appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
	if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)	//检查栈顶地址是否合法.
	{ 
		jump2app=(iapfun)*(vu32*)(appxaddr+4);		//用户代码区第二个字为程序开始地址(复位地址)		
		MSR_MSP(*(vu32*)appxaddr);					//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		jump2app();									//跳转到APP.
	}
}		 

主程序

通过按键对接收到的升级程序进行更新,或者直接转入到功能程序地址。

	while(1)
	{
	 	if(USART_RX_CNT)
		{ 
			if(oldcount==USART_RX_CNT)
			{
				applenth=USART_RX_CNT;
				oldcount=0;
				USART_RX_CNT=0;
				printf("代码长度:%dBytes\r\n",applenth);
			}else oldcount=USART_RX_CNT;			
		}
		if(key==WKUP_PRES)
		{
			if(applenth)
			{
				printf("开始更新固件...\r\n");	
 				if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
				{	 
					iap_write_appbin(FLASH_APP2_ADDR,USART_RX_BUF,applenth);//更新FLASH代码 
					printf("固件更新完成!\r\n");	
				}else 
				{
	   printf("固件更新失败!\r\n");
					
				}
 			}else 
			{
				printf("没有可以更新的固件!\r\n");
			}							 
		} 
		if(key==KEY1_PRES)
		{
			printf("开始执行FLASH用户代码!!\r\n");
			if(((*(vu32*)(FLASH_APP2_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
			{	 
				iap_load_app(FLASH_APP2_ADDR);//执行FLASH APP代码
			}else 
			{
				printf("非FLASH应用程序,无法执行!\r\n");
			}									 
		}		 
	}