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