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,可以通过直接修改宏定义进行中断向量表的偏移
例如我把APP1应用程序1起始地址定义在了0x08020000,所以把VECT_TAB_OFFSET修改成0x20000的偏移量,同时修改下图红色标记作为程序起始地址,橙色标记加上红色标记是FLSAH总容量,我现在需要偏移0x20000,就改成下图所示:
接着进行设置,在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生成文件名保持一致,不然在编译时会报错:
接着写一个简单的程序闪烁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