目錄
概述
一、功能描述?
1、BootLoader部分:
2、APP部分:
二、BootLoader程序制作
1、分區定義
2、 主函數
3、SD卡升級文件檢測和更新
4、程序跳轉
三、APP程序制作
四、工程配置(默認KEIL5)
五、運行測試?
結束語
概述
????????IAP(In Application Programming)即在應用中編程,允許在應用程序運行時更新或切換固件。STM32通過修改MSP(主堆棧指針)和PC(程序計數器)實現從不同地址啟動,包括Flash或RAM地址。默認情況下,嵌入式程序以連續二進制形式燒錄到STM32的可尋址Flash區域。若Flash容量足夠存儲多個完整程序,每個程序獨立且完整,上電后可通過修改MSP值選擇不同程序入口,從而實現多固件切換或升級。
?????????BootLoader(引導加載程序)是嵌入式系統或計算機啟動時運行的一段小型程序,負責初始化硬件、加載操作系統內核并將其控制權移交。它是系統從關機狀態到操作系統完全運行之間的橋梁。
????????所以,固件升級的基本思路是將stm32的flash劃分為若干個區域,其中包括BootLoader區域和APP區等,將各自的程序寫到對應的flash區域里,本文將采用讀取SD卡的文件來更新固件,只重實踐不講原理。
一、功能描述?
????????使用STM32的SDIO總線驅動microSD卡實現SD卡升級程序。將FLASH分為2個部分。
分區介紹:
????????本文使用stm32f407zgt6,flash是1M,sector是不固定的(在讀寫擦除flash時需要特別注意),詳細看下圖STM32F4系列的FLASH結構。
????????BootLoader整個代碼編譯下來有36K左右,所以使用0x08000000~0x0000FFFF,剩下的FLASH全部用來存放APP,使用0x08010000~0x080F0000。
區域 | 起始地址 | 區域大小 | 功能 |
---|---|---|---|
BOOT | 0x08000000 | 0x00010000(64k) | 存放BootLoader程序 |
APP | 0x08010000 | 0x000F0000(960k) | 存放產品主程序 |
tips:?選擇以上分區方式的好處是在上電瞬間可以觸發升級,只要SD卡里面根目錄有正確的程序都可以更新固件,所以就沒必要用到SETTING區域在存更新觸發標志位了。
1、BootLoader部分:
????????運行時首先會初始化SD卡,如果初始化成功并且檢測到存在SD卡,就查找根目錄是否有firmware.bin文件,如果有就升級,沒有就直接進入APP,檢測不到SD卡也會直接進入APP。
2、APP部分:
????????該部分需要在程序起始設置中斷向量跳轉指針,剩下的就是自己寫的主程序。
二、BootLoader程序制作
????????主要包含了YMODEM協議的驅動代碼。
1、分區定義
#define FLASH_SECTOR_SIZE 1024
#define FLASH_SECTOR_NUM 1024 // 1024K
#define FLASH_START_ADDR ((uint32_t)0x8000000)
#define FLASH_END_ADDR ((uint32_t)(0x8000000 + FLASH_SECTOR_NUM * FLASH_SECTOR_SIZE))//flash sector addr
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) //sector0 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) //sector1 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) //sector2 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) //sector3 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) //sector4 addr, 64 Kbytes
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) //sector5 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) //sector6 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) //sector7 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) //sector8 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) //sector9 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) //sector10 addr,128 Kbytes
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) //sector11 addr,128 Kbytes #define BOOT_SECTOR_ADDR 0x08000000
#define BOOT_SECTOR_SIZE 0x10000
#define APP_SECTOR_ADDR 0x08010000 // APP sector start address
#define APP_SECTOR_SIZE 0xF0000 // APP sector size #define BOOT_ERASE_SECTORS_NUM 4 // 64k
#define APP_ERASE_SECTORS_NUM 8 // 64k+128k+128k+128k+128k+128k+128k+128ktypedef enum
{NONE,START_PROGRAM,UPDATE_PROGRAM,UPDATE_SUCCESS,
} process_status;
2、 主函數
????????這部分包含了升級的所有狀態,該框架可以說對比上一篇文章是完全不變的,主要是SD卡初始化沒問題就成功一大半了,具體看代碼。
static void iap_process(void)
{switch (bootupdate_process) {case NONE:break;case START_PROGRAM:printf("start app...\r\n");delay_ms(50);if ((((*(vu32*)(APP_SECTOR_ADDR+4))&0xFF000000)==0x08000000)&&(!iap_load_app(APP_SECTOR_ADDR))) {printf("no program\r\n");delay_ms(1000);}printf("start app failed\r\n");break;case UPDATE_PROGRAM:if(SD_Init()==0){printf("檢測到SD卡\r\n");show_sdcard_info(); //打印SD卡相關信息sd_update();}else{printf("沒檢測到SD卡\r\n");}bootupdate_process = UPDATE_SUCCESS;break;case UPDATE_SUCCESS:bootupdate_process = START_PROGRAM;
// NVIC_SystemReset();break;default:break;}
}int main(void)
{ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置系統中斷優先級分組2delay_init(168); //初始化延時函數uart_init(115200); //初始化串口波特率為115200LED_Init(); //初始化LED bootupdate_process=UPDATE_PROGRAM;while(1){delay_ms(200); LED0=!LED0;iap_process();}
}
3、SD卡升級文件檢測和更新
? ? ? ? 關于SD卡和FATFS文件系統的移植我是參考正點原子的,主要是將malloc用全局變量代替,其它需要注意的是SD卡的扇區需要選擇512字節。
本來是存放在堆區的,現改為存放在棧區
#define?? ?_USE_LFN?? ?2?? ??? ?/* 0 to 3 */
????????掛載SD卡,判斷sd卡根目錄下是否有firmware.bin文件,有的話就獲取一下該固件占用了多少個扇區,對應去擦除扇區,然后就可以復制固件到APP起始地址了。不存在該固件的話就直接退出進入APP。
void sd_update(void)
{u8 file_buffer[512]={0}; uint32_t flash_addr = APP_SECTOR_ADDR;unsigned long total_bytes_read = 0;UINT bytes_read;fres=f_mount(&fs[0],"0:",1); //掛載FLASH. if(fres==FR_OK) //FLASH磁盤,FAT文件系統正常{printf("Flash disk OK!\r\n");}// 判斷sd卡根目錄下是否有firmware.bin文件fres = f_stat("0:/firmware.bin", &fno);if(fres == FR_OK){printf("0:/firmware.bin”文件信息:\n");printf("》文件大小: %ld(字節)\n", fno.fsize);printf("sector_num=%d\r\n",get_mcu_flash_sector(flash_addr+(u32)fno.fsize)/8-4+1);mcu_flash_erase(flash_addr, get_mcu_flash_sector(flash_addr+(u32)fno.fsize)/8-4+1);}else{printf("firmware.bin not found!\r\n");}if(fres == FR_OK) {// 文件存在printf("firmware.bin found, size: %lu bytes\r\n", fno.fsize);printf("開始更新固件...\r\n");// 打開固件文件fres = f_open(&firmware_file, "0:/firmware.bin", FA_READ);if(fres == FR_OK) {// 循環讀取文件內容while (total_bytes_read < fno.fsize){// 讀取數據塊到緩沖區fres = f_read(&firmware_file, file_buffer, 512, &bytes_read);if (fres != FR_OK || bytes_read == 0){// 讀取出錯或到達文件末尾printf("讀取文件失敗或文件已結束,錯誤碼: %d\r\n", fres);break;}// 寫入到FLASHprintf("正在寫入地址 0x%08X,大小: %u 字節\r\n", flash_addr, bytes_read);mcu_flash_write(flash_addr, file_buffer, bytes_read);// 更新計數器和地址total_bytes_read += bytes_read;flash_addr += bytes_read;// 顯示進度printf("更新進度: %lu/%lu bytes\r\n", total_bytes_read, fno.fsize);}// 關閉文件f_close(&firmware_file);if (total_bytes_read == fno.fsize) {printf("固件更新完成! 共寫入 %lu 字節\r\n", total_bytes_read);} else {printf("固件更新未完成! 已寫入 %lu/%lu 字節\r\n", total_bytes_read, fno.fsize);}}} else {// 文件不存在printf("firmware.bin not found!\r\n");}
}
4、程序跳轉
????????跳轉這部分網上也很多,基本沒什么區別,語句很少,建議每一步都去查一下大概意思。
tips:本文的開發環境有128k ram,所以下列語句需要判斷跳轉地址是否超出。
if (((*(__IO uint32_t*)appxaddr) & 0x2FFE0000 ) == 0x20000000)
詳細參考以下文章:
關于STM32單片機IAP升級中if(((*(__IO uint32_t*)ulAddr_App) & 0x2FFE0000) == 0x20000000)語句的理解-CSDN博客
iapfun jump2app;uint8_t iap_load_app(uint32_t app_addr)
{uint32_t jump_addr;if (((*(__IO uint32_t*)app_addr) & 0x2FFE0000 ) == 0x20000000) { jump_addr = *(__IO uint32_t*) (app_addr + 4); jump2app = (iapfun)jump_addr; __set_MSP(*(__IO uint32_t*)app_addr); jump2app();return 1;} return 0;
}
三、APP程序制作
????????這部分只需要設置一下flash的偏移量。
NVIC_SetVectorTable (NVIC_VectTab_FLASH, 0x08010000); //向量重定向
四、工程配置(默認KEIL5)
BootLoader部分:0x08000000~0x0800FFFF
?
APP部分:0x08010000~0x080F0000
五、運行測試?
(1)首先將firmware.bin固件存放到SD卡的根目錄。? ? ??
(2)沒插上SD卡的情況,直接進入APP程序。
(3)上電前插入SD卡后啟動的情況,可以看到檢測到SD卡并且打印SD卡的基礎信息,找到firmware.bin文件后就開始按512字節寫入APP的起始地址,寫完后進入新的APP程序。之后需要拔出SD卡,不然每次上電都會執行更新程序步驟。?
?
結束語
????????以上SD卡升級stm32單片機功能已實現,思路和我前面寫的文章一模一樣,思路理解了就能一直套娃了,所以便一股腦移植SD卡了,建議大家在移植的時候對照著文檔或者SDK直接移植過來,當然用HAL庫更好。后面大家看到的也希望可以得到大家的指點或者互動,感謝各位亦菲彥祖們了。
完整代碼下載地址:SD卡升級stm32固件資源-CSDN下載