最近刚接触到STM32L452RET6(STM32L4xx系列)芯片,使用LL库配置(LL库更接近硬件层,直接操作寄存器。)可以先使用STM32CubeMX软件生成一个基础工程,再进一步添加配置。以下是学习了2天IAP后的总结,其实并没有想象中的那么难懂;
实现通过MCU的UART接收来自电脑发送的bin文件进行IAP程序更新。
我选用的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的单向实验;
和之前的操作类似,设置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
