stm32 IAP 程序编写心得

前言

大家好这里是小白,因为实在受不了一些网上代码和某些开发板例程的荼毒,决定开个blog吐槽一下学习和工作中踩过的一些雷和遇到的一些坑。博主目前主要是做硬件和嵌入式设计的,主要使用的STM32F1XX和STM32F4XX并配合Inter的FPGA做一些程控和信号处理,具体行业就不多说了,有兴趣可以点进我的头像看一哈,或者私信我交流一些设计心得。

IAP简介及原理

IAP是在线应用编程(In Application Programming)的简称,简单来说IAP的目的就是实现在线刷程序。在刚开始学习STM32的时候,我们只能通过J-LINK或者ST-LINK实现程序烧写,随着学习的深入,我们也可能会接触到使用ST官方的BOOTLOADER GUI进行程序下载,但是上述的这几种方式在烧写程序时要么需要使用特殊的下载工具,要么需要跳片改变STM32 boot0脚的状态并使用专用的软件,并不适合生产批量烧写,因此就有了IAP这种下载方式。
IAP的原理在STM32资料手册和很多博主都有介绍,有需要的朋友可以参考下面两个资料:
STM32+IAP实现原理: https://blog.csdn.net/wzy15965343032/article/details/88545225.
正点原子STM32探索者开发板IAP: https://www.bilibili.com/video/BV1Rx411R75t?p=91.
理论上IAP可以实现包括USART,CAN,SPI,IIC等任意一种通讯方式实现在线编程。
废话不多说直接说正题,由于工作需要,最近我也在编写基于USART的IAP实现方案,参考了网上一些资料后总觉得不太理想,因此分享一下我在编写IAP时遇到的一些问题以及解决方案。

IAP设计过程

目前我在网上找到两个相关的IAP设计例程,都是基于ymodem协议的,一个就是正点原子的IAP串口实现方案,另一个不知道算不算官方的例程,看函数介绍写的是MCD Application Team。第二个例
第一个例程我就不放了,大家可去正点原子官网下载。第二个例程采用的是超级终端控制的,但是需要上位机添加bin文件(这里我没有找到相应的上位机,理论上可以使用老司机常用的那个SSCOM42,只不过字符输入需要手动点击发送),我会在文章末尾放上下载链接,大家可根据需求自行下载。
IAP设计过程本质上就是编写两段(或多段)代码,一段是IAP代码,另一段是实际的APP应用程序代码,编写完成后将IAP烧写入STM32FLASH的最前端,再将APP应用程序编译后生成bin文件。然后将IAP程序烧录至FLASH中,随后根据需要将要使用的应用程序的bin文件通过USART(通讯)的方式发送给单片机,并存入单片机的FLASH。注意这里的应用程序存放要偏移一定的地址,偏移大小是设置的IAP程序的大小,否则会擦除掉IAP程序导致跳转失败或者IAP仅能使用一次。
比如我使用的是stm32f407单片机,该单片机一共12个SECTOR共1MB的FLASH存储空间,我一般是这样分配的,SECTOR 0用于存放IAP代码,SECTOR 1-SECTOR 4用于存放APP应用程序代码(如果不够可以往后扩展),SECTOR5-SECTOR 10用于存放需掉电保存的系统参数,最后SECTOR11存放一些关键的系统标志(这个后面会提到)。

IAP空间配置
APP应用程序我这里使用原子的UCOSII的移植例程,通过点亮两个LED灯检查IAP程序是否烧录进去,APP应用程序需要设置FLASH地址,并且需要设置APP应用程序的中断向量偏移。

APP代码空间配置
中断向量偏移
最后再选择MDK自带的fromelf应用程序(在KEIL的安装路径下面,点击后面的小文件夹添加),生成bin文件就可以使用上位机更新程序了。
在这里插入图片描述
这里注意 --bin - o…是手动添加的,OBJ后面的文件名是你的项目output文件名。如我的项目output名称是UCOS-1,所以我这里应该填:
D:\Keil5\ARM\ARMCC\bin\fromelf.exe --bin -o …\OBJ\UCOS-1.bin …\OBJ\UCOS-1.axf
以上工作完成就可以使用上位机更新APP应用程序了,首先将IAP程序烧录进单片机,打开串口助手,将你的电路板与电脑连接,选择打开文件,将编译好的bin文件添加,点击发送文件,APP程序就写入了相应的FLASH中。
串口烧写APP程序

完整的IAP功能

以上就是IAP编程的完整过程,也是原子开发板例程中具有的功能,但是正当我想测试的时候发现一个问题,原子的例程是通过按键实现跳转的,而我设计的电路板,没有按键!!
然后我又看了看原子例程代码,想了想我的需求,发现这离我想要的IAP代码功能还差很多,于是我决定掏出我的陈年老代码重构一下改成IAP代码。
相比于原子的开发历程,我需要我的IAP程序具有以下几个功能:
1.在IAP程序中,通过特定串口指令进入IAP模式,然后再添加和发送APP应用程序的bin文件,防止发送的数据,也被写入FLASH。
2.可以实现APP应用程序跳转回IAP程序,实现程序的重新烧录,这一点也是通过特定的串口指令来实现的。
3.上电判断是否已有APP应用程序,如果已有程序,自动跳转APP程序运行。
4.由APP应用程序跳转回IAP后,IAP程序判断本次运行是重新上电还是由APP程序跳转而来,如果是由APP跳转而来,则用户需要重新烧录程序,先将已有的应用程序自动擦除,等待串口指令进入IAP模式。
具备了以上功能,IAP程序才算完整。

功能的实现与遇到的问题

以上功能并不难实现,有兴趣的朋友可以试着自己做一下,我这里来简单说一下我的做法。
1.首先针对第一点,设置一个串口指令标志位,只有收到了特定的指令(此处我设置的是十六进制A5 5A 31 42 0D),标志位置位,才可以开始接收bin文件,否则无论收到什么都显示指令错误。

if(USART_RX_CNT)
{
	if(RxDataCount == USART_RX_CNT)				//串口没有再收到新数据
	{
		RxDataLength = USART_RX_CNT;
		if(RxCmdFlag == 0 && RxDataLength == 5)	//接收IAP指令
		{
			if(USART_RX_BUF[0] == 0xA5 && USART_RX_BUF[1] == 0x5A && USART_RX_BUF[2] == 0x31 
										&& USART_RX_BUF[3] == 0x42 && USART_RX_BUF[4] == 0x0D)
			{
				RxCmdFlag = 1;					//接收到更新APP代码指令,标志位置位
				RxDataLength = 0;				//清空指令长度,防止影响后面计算APP代码大小
				printf("Ready to recieve app code,please add a binary file!\r\n"); //准备好接收bin文件,等待用户添加
			}
			else
			{
				CodeUpdateFlag = 0;
				AppCodeLength = 0;
				printf("Command Error!\r\n");	//未接收到IAP更新指令,其他任何串口发送数据都认为指令错误
			}
		}
		
		else if(RxCmdFlag == 1 && RxDataLength > 10)//接收IAP程序
		{
			CodeUpdateFlag = 1;					//代码更新标志位置位,用于应用程序代码接收完成后写FLASH
			RxCmdFlag = 0;
			AppCodeLength = USART_RX_CNT;
			printf("App code recieve complete!\r\n");
			printf("Code size:%dBytes\r\n",AppCodeLength);
		}
		
		else
		{
			RxDataLength = 0;
			printf("Not correct file or command!\r\n"); //如果代码大小不足10Bytes,认为没有正确添加bin文件
		}
		RxDataCount = 0;
		USART_RX_CNT = 0;
	}
	else 
	{
		RxDataCount = USART_RX_CNT;
	}
}

2.这个部分需要重点注意一下,我在测试跳转回IAP的时候,原本是在APP应用程序其中一个LED_TASK中做了个计数,计数10次之后自动跳转回IAP程序,到这里是没有问题的,但当我再次发送bin文件时就发生了问题。

void led0_task(void *pdata)
{
	u8 count = 0;
	while(1)
	{
		LED0=0;
		delay_ms(80);
		LED0=1;
		delay_ms(920);
		if(count > 10)
		{
			count = 0;
			printf("跳转回IAP程序!\r\n");
			delay_ms(10);
			iap_load_app(STM32_FLASH_BASE);
		}
		else
		{
			count ++;
		}
	}
}

这里使用与IAP代码里使用的iap_load_app()函数是可以跳转回IAP代码的,但是当我再次发送应用程序bin文件时,并没有显示跳转APP程序,于是我挂了仿真器对IAP代码进行仿真,发现在我再次发送bin文件时,系统发生了错误进入了死循环。

void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

这个问题我试了好久,也没有发现明显的逻辑错误,就是找不到原因,于是我去网上查IAP的资料,发现大多数都是IAP功能的介绍,没有什么具体示例。经过长时间查找,我终于看到了一篇文章,原来是我在跳转回IAP程序之后没有复位RCC时钟和NVIC中断,导致时钟或中断发生错乱。所以在跳转回IAP程序后,配置时钟之前要添加RCC_DeInit()函数,在配置中断前要添加NVIC_DeInit (),先将时钟和中断关闭之后再重新配置,当然这里也可以使用stm32库函数里面的系统复位函数来让程序重新从头开始运行,我这里使用的就是这种方法(别问,问就是偷懒)。

if(Jump_IAP_FLAG)
		{
			Jump_IAP_FLAG = 0;
			delay_ms(10);
			if(((*(vu32*)(STM32_FLASH_BASE + 4)) & 0xFF000000) == 0x08000000)		//判断代码合法性
			{
				STMFLASH_Write(ADDR_CodeWriteFlag,IAPFlagBuf,2);					//写IAP跳转标志位
				delay_ms(100);
				printf("Start Running IAP code!\r\n");
				NVIC_SystemReset();				 //注意此处跳转不是使用iap_load_app,而是将系统复位
			}
			else 
			{
				printf("IAP code is illegal!\r\n");  
			}
		}

3.解决了上述两个问题,第三点和第四点就好实现了,正如前文所说,我在SECTOR11中的连续两个地址存放了两个标志量。

#define ADDR_CodeWriteFlag      ((u32)0x080E1000)	//IAP程序更新标志位存放地址,如果是0x55,说明已经有应用程序
#define ADDR_JumpToIAPFlag		((u32)0x080E1001)	//IAP程序跳转标志位,如果是0x55,说明是从APP跳转回IAP程序

在将bin文件写入FLASH之后跳转APP应用程序之前,连续从ADDR_CodeWriteFlag地址连续写入两个标志量0x55和0x00,跳转之后如果系统复位或者重新上电,在运行IAP程序时会首先读取ADDR_CodeWriteFlag地址是否等于0x55,如果等于则直接跳转APP程序运行,如果不相等,等待用户烧录程序再继续运行。

JumpToAPPFlag = FLASH_ReadWord(ADDR_CodeWriteFlag); //读取APP写入标志位,判断是否已有程序
if(JumpToAPPFlag != 0x55)			//判断是否已有APP程序,如果已有,跳转至APP程序运行
{}
else
{
	printf("There is an app code!\r\n");
	printf("Start running app code!\r\n");
	delay_ms(10);
	IAP_Load_AppCode(FLASH_APPCODE_ADDR);	//执行FLASH APP代码
}

同样由APP跳转回IAP之前,我会重新在上述地址连续写入0x00和0x55,这样跳转回IAP之后既不会再跳转回APP,也可以判定ADDR_JumpToIAPFlag位的值,如果该位等于0x55,说明是从APP程序跳转回来的,因此会先擦除存放APP代码的扇区,这里我是用的是SECTOR1-SECTOR4。

if(JumpToIAPFlag == 0x55)		//判断是否从APP程序跳转回来,如果是擦除已有APP程序
{
		STMFLASH_Erase();			
		JumpToIAPFlag = 0x00;		//清跳转标志位,防止不停擦除FLASH
}

以上就是我IAP编写的历程和一些心得,希望对大家有帮助吧,下面放上源码,有需要的朋友自取,STM32F1系的单片机也可以使用,但是要修改一下各系统函数的初始化配置(F1和F4在配置上还是有少许区别的,这个有需求的话我也可以给大家开个贴交流一下)。

附录:程序源码

CSDN: https://download.csdn.net/download/suzuzhizhen/15611524?spm=1001.2014.3001.5503.
GitHub:https://github.com/baiyiqing-LC/stm32f4xx_usart_IAP

  • 8
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值