一.概述
1.要實現串口IAP升級,首先要編寫一個bootloader程序,然后再寫支持IAP的app程序;
2.keil下bootloader的程序rom和ram設置
3.app程序要用bin文件
注:本文以STM32H743舉例,其他stm32單片機IAP升級原理類似。
二.實現
1.bootloder程序實現
(1)基本知識
?????stm32的flash地址起始于0x0800 0000,結束地址是0x0800 0000加上芯片實際的flash大小,不同的芯片flash大小不同。
?????RAM起始地址是0x2000 0000,結束地址是0x2000 0000加上芯片的RAM大小。不同的芯片RAM也不同。
(2)STM32H743 bootloader設置
說明:由于 STM32H7 的扇區大小固定為 128KB 一個,最小擦除單位也是以扇區為單位的,因此,至少分配一個扇區給 Bootloader,否則后續擦寫 APP 的時候,可能把 Bootloader 也給擦了!所以,我們使用 128K(0X20000)字節來存放 Bootloader。
(3)代碼實現
int main(void)
{
u8 t;
u16 wait_s = 0;
u8 key;
u32 applenth=0; ????????//接收到的app代碼長度
u8 clearflag=0; ?
u32 Buffer[4];
Cache_Enable(); ??????????????//打開L1-Cache
HAL_Init(); ???????? //初始化HAL庫
Stm32_Clock_Init(160,5,2,4); ? ????//設置時鐘,400Mhz
delay_init(400); //延時初始化
uart_init(115200); //串口初始化
LED_Init(); //初始化LED
KEY_Init(); //初始化按鍵
SDRAM_Init(); ????????????//初始化SDRAM
while(1)
{
? if(USART_RX_STA&0x8000)
{
????????USART_RX_STA=0; ??? //清空 ??
applenth=USART_RX_CNT; ???// ?applenth
USART_RX_CNT=0;
printf("用戶程序接收完成!\r\n");
printf("代碼長度:%dBytes\r\n",applenth);
}
else
{
??if(wait_s >= 500)
{
??wait_s = 0;
??printf("等待用法發送程序... ?\r\n");
}
}
t++;
wait_s ++;
delay_ms(10);
if(t==30)
{
LED0_Toggle;
t=0;
if(clearflag)
{
clearflag--;
}
} ?? ?
key=KEY_Scan(0);
if(key==WKUP_PRES) //WK_UP按鍵按下
{
if(applenth)
{
printf("開始更新固件...\r\n");
? if(((*(vu32*)(0x24001000+4))&0xFF000000)==0x08000000)//判斷是否為0X08XXXXXX.
{ ?
iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新FLASH代碼 ??#define FLASH_APP1_ADDR 0x08020000
printf("固件更新完成!\r\n");
}else
{
printf("非FLASH應用程序!\r\n");
}
? }else
{
printf("沒有可以更新的固件!\r\n");
}
clearflag=7;//標志更新了顯示,并且設置7*300ms后清除顯示 ?
}
if(key==KEY1_PRES) //KEY1按下
{
if(applenth)
{ ?
printf("固件清除完成!\r\n"); ???
applenth=0;
}else ?
{
printf("沒有可以清除的固件!\r\n");
}
clearflag=7;//標志更新了顯示,并且設置7*300ms后清除顯示 ?
}
if(key==KEY2_PRES) //KEY2按下
{
printf("開始執行FLASH用戶代碼!!\r\n");
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判斷是否為0X08XXXXXX.
{ ?
iap_load_app(FLASH_APP1_ADDR);//執行FLASH APP代碼 ?
}else
{
printf("非FLASH應用程序,無法執行!\r\n"); ??
} ?
clearflag=7;//標志更新了顯示,并且設置7*300ms后清除顯示 ??
}
} ?????
}
(4)代碼功能邏輯:
需要先按下 KEY_UP 按鍵,將串口接收到的 APP 程序存放到 STM32 的內部 FLASH;
再按 KEY2 既可以執行這個 FLASH APP 程序。
通過 KEY1 按鍵,可以手動清除串口接收到的APP 程序。
DS0 用于指示程序運行狀態。
2.app程序及邏輯
(1)IAP流程邏輯
(2)keil設置
(3)代碼實現
int main(void)
{
??SCB->VTOR = FLASH_BASE|0x20000;//設置偏移量 ?FLASH_BASE : ?(uint32_t)0x08000000
RTC_TimeTypeDef RTC_TimeStruct;
??RTC_DateTypeDef RTC_DateStruct;
??u8 tbuf[40];
u8 t=0;
HAL_Init(); ???????? //初始化HAL庫
Stm32_Clock_Init(160,5,2,4); ? ????//設置時鐘,400Mhz
delay_init(400); ????//延時初始化
uart_init(115200); //串口初始化
usmart_dev.init(200); ???? //初始化USMART
????printf("RTC APP running\r\n");
LED_Init(); //初始化LED
KEY_Init(); //初始化按鍵
SDRAM_Init(); ?????????????????? //初始化SDRAM
????RTC_Init(); ??????????????????? //初始化RTC
????RTC_Set_WakeUp(RTC_WAKEUPCLOCK_CK_SPRE_16BITS,0); //配置WAKE UP中斷,1秒鐘中斷一次 ?
????while(1)
????{
t++;
if((t%100)==0) //每100ms更新一次顯示數據
{
????????????HAL_RTC_GetTime(&RTC_Handler,&RTC_TimeStruct,RTC_FORMAT_BIN);
sprintf((char*)tbuf,"Time:%02d:%02d:%02d",RTC_TimeStruct.Hours,RTC_TimeStruct.Minutes,RTC_TimeStruct.Seconds);
??????printf("RTC time=%s\r\n",tbuf);
????????????HAL_RTC_GetDate(&RTC_Handler,&RTC_DateStruct,RTC_FORMAT_BIN);
sprintf((char*)tbuf,"Date:20%02d-%02d-%02d",RTC_DateStruct.Year,RTC_DateStruct.Month,RTC_DateStruct.Date);
printf("RTC date=%s\r\n",tbuf);
sprintf((char*)tbuf,"Week:%d",RTC_DateStruct.WeekDay);
printf("RTC week=%s\r\n",tbuf);
}
if((t%20)==0)LED0_Toggle; //每200ms,翻轉一次LED0
????????delay_ms(10);
} ?
}
說明:重點是程序開始要有語句:SCB->VTOR = FLASH_BASE|0x20000;后面代碼和普通app程序無區別
3.app生成bin文件
通過在 User 選項卡,設置編譯后調用 fromelf.exe,根據.axf 文件生成.bin 文件,用于
IAP 更新。?
具體見之前文章:
STM32多種開發環境及生成hex及bin文件介紹_stm32 cubeide fromelf-CSDN博客文章瀏覽閱讀758次,點贊29次,收藏23次。STM32多種開發環境及生成hex及bin文件介紹keil5 STM32CubeIDE EWARM如果不考慮商業應用keil的比較好,如果考慮商業付費用STM32CubeIDE更好一些,畢竟是免費的。_stm32 cubeide fromelfhttps://blog.csdn.net/xieliru/article/details/139849283?spm=1001.2014.3001.5501
4.串口升級app
(1)首先用keil把bootloader程序用燒寫器燒寫到單片機中
(2)通過串口IAP升級app程序