天天看点

STM32L4xx基于UART的IAP实现(实验详解)STM32L4xx基于UART的IAP实现(实验详解)

STM32L4xx基于UART的IAP实现(实验详解)

最近刚接触到STM32L452RET6(STM32L4xx系列)芯片,使用LL库配置(LL库更接近硬件层,直接操作寄存器。)可以先使用STM32CubeMX软件生成一个基础工程,再进一步添加配置。以下是学习了2天IAP后的总结,其实并没有想象中的那么难懂;

一.实现目标

实现通过MCU的UART接收来自电脑发送的bin文件进行IAP程序更新。

二.IAP单项进入APP 实现流程

我选用的STM32L452的FLASH有512K大小,地址在0x08000000~0x08080000之间。

1.先将FLASH划分成3块区域,一个IAP引导程序,两个APP应用程序:

一般IAP引导程序越小越好,给之后的APP应用程序留下更多空间,这里我不会太刻意节省空间。

将IAP引导程序起始地址分到0x08000000,APP1应用程序1起始地址分到0x08020000,APP2应用程序2起始地址分到0x08040000。

IAP引导程序有0x00000-0x20000,有(0x20000-0x00000)/1024=128K;

APP1应用程序1有0x20000-0x40000,有(0x40000-0x20000)/1024=128K;

APP2应用程序2有0x40000-0x80000,有(0x80000-0x40000)/1024=256K;

2.新建APP应用程序获得bin文件:

STM32L4xx系列芯片设置中断偏移时需要修改VECT_TAB_OFFSET,可以通过直接修改宏定义进行中断向量表的偏移

STM32L4xx基于UART的IAP实现(实验详解)STM32L4xx基于UART的IAP实现(实验详解)
STM32L4xx基于UART的IAP实现(实验详解)STM32L4xx基于UART的IAP实现(实验详解)

例如我把APP1应用程序1起始地址定义在了0x08020000,所以把VECT_TAB_OFFSET修改成0x20000的偏移量,同时修改下图红色标记作为程序起始地址,橙色标记加上红色标记是FLSAH总容量,我现在需要偏移0x20000,就改成下图所示:

STM32L4xx基于UART的IAP实现(实验详解)STM32L4xx基于UART的IAP实现(实验详解)

接着进行设置,在RUN中写入fromelf --bin --output F:\MyFile\Project\STM32L471VE\Project_STM32L471_IAPFLASH\OBJ\Project .bin F:\MyFile\Project\STM32L471VE\Project_STM32L471_IAPFLASH\OBJ\Project.axf,路径写成Output里面生成的路径,生成的文件名与Output生成文件名保持一致,不然在编译时会报错:

STM32L4xx基于UART的IAP实现(实验详解)STM32L4xx基于UART的IAP实现(实验详解)

接着写一个简单的程序闪烁LED灯的程序:

void main(void)//APP1应用程序1
{
	LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_5, LL_GPIO_MODE_OUTPUT);//LED初始化
	while(1)
	{
		LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
		LL_mDelay(200);
	}
}
           

编译通过后会在设置的路径里生成程序的bin文件;

3.编写IAP引导程序,此时就跟平常一样不用对Keil进行任何设置:

void IAPMain(void)
{
	u32	oldcount=0;
	u32	applenth=0;
	IAP_Usart_Init(9600);//UART初始化IAP
	while(1)
	{
		#if USE_DOG
			LL_WWDG_SetCounter(WWDG, 0X7E);//喂狗
		#endif
		if(USART3_RX_DATA_BUF_CNT)
		{
			if(oldcount == USART3_RX_DATA_BUF_CNT)//新周期内没有数据认为接收完毕
			{
				applenth = USART3_RX_DATA_BUF_CNT;
				oldcount = 0;
				USART3_RX_DATA_BUF_CNT = 0;
				if (((*(__IO uint32_t*)(SaveAddress + 0x00000004)) & 0xFF000000 ) == 0x08000000)//检查地址是否合法
					iap_write_appbin(AppAddress1, USART3_RxData, applenth);//更新程序,串口接收数据写入APP地址
				if (((*(__IO uint32_t*)(AppAddress1 + 0x00000004)) & 0xFF000000 ) == 0x08000000)//检查地址是否合法
					iap_load_app(AppAddress1);//跳转到APP地址
			}
			else
				oldcount = USART3_RX_DATA_BUF_CNT;
		}
		LL_mDelay(10);
	}
}
           

大致逻辑就是串口初始化,等到bin文件通过UART接收完成后,把接收的数据写到AppAddress1下,再进行IAP跳转到指定地址,AppAddress1就是0x08020000;编译后,下载程序,刚开始灯时熄灭的,再通过电脑串口工具把之前获得的APP1应用程序1的bin文件通过UART发送给MCU,成功进行IAP跳转到APP1后灯会闪烁起来,这是一个IAP->APP的单向实验;

三.IAP单项进入APP1,APP1和APP2相互切换 实现流程

和之前的操作类似,设置Keil,main函数改成如下,通过改变#if来区分LED的快闪和慢闪程序,还有不同的跳转地址,编译后获得两个bin文件,还是下载IAP引导程序,通过UART发送bin文件进行跳转:

void main(void)
{
	u32	oldcount=0;
	u32	applenth=0;
	IAP_Usart_Init(9600);//初始化IAP
	LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_5, LL_GPIO_MODE_OUTPUT);//LED初始化
	while(1)
	{
		LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
		#if 1  //偏移0x20000时
		LL_mDelay(200);
		if(USART3_RX_DATA_BUF_CNT)
		{
			if(oldcount == USART3_RX_DATA_BUF_CNT)//新周期内没有数据认为接收完毕
			{
				applenth = USART3_RX_DATA_BUF_CNT;
				oldcount = 0;
				USART3_RX_DATA_BUF_CNT = 0;
				if (((*(__IO uint32_t*)(SaveAddress + 0x00000004)) & 0xFF000000 ) == 0x08000000)//检查地址是否合法
					iap_write_appbin(AppAddress2, USART3_RxData, applenth);//更新程序,串口接收数据写入APP地址
				if (((*(__IO uint32_t*)(AppAddress2 + 0x00000004)) & 0xFF000000 ) == 0x08000000)//检查地址是否合法
					iap_load_app(AppAddress2);//跳转到APP地址
			}
			else
				oldcount = USART3_RX_DATA_BUF_CNT;
		}
		#else	//偏移0x40000时
		LL_mDelay(1000);
		if(USART3_RX_DATA_BUF_CNT)
		{
			if(oldcount == USART3_RX_DATA_BUF_CNT)//新周期内没有数据认为接收完毕
			{
				applenth = USART3_RX_DATA_BUF_CNT;
				oldcount = 0;
				USART3_RX_DATA_BUF_CNT = 0;
				if (((*(__IO uint32_t*)(SaveAddress + 0x00000004)) & 0xFF000000 ) == 0x08000000)//检查地址是否合法
					iap_write_appbin(AppAddress1, USART3_RxData, applenth);//更新程序,串口接收数据写入APP地址
				if (((*(__IO uint32_t*)(AppAddress1 + 0x00000004)) & 0xFF000000 ) == 0x08000000)//检查地址是否合法
					iap_load_app(AppAddress1);//跳转到APP地址
			}
			else
				oldcount = USART3_RX_DATA_BUF_CNT;
		}
		#endif
	}
}
           

上面的程序和IAP引导程序类似,就是串口读数据,写FLASH,再跳转。实现IAP->APP1,再APP1->APP2,再从APP2->APP1。

只会再在UART上连接上Bluetooth、RFID或者NB-Lot就可以实现远程无线更新固件了。

这是我上传的IAP.c和IAP.h文件

https://download.csdn.net/download/qq_39735462/11952466