本系列將從升級流程、boot代碼編寫、APP代碼編寫以及固件打包來介紹,硬件選用STM32F407ZGT6(手里只有),來完成這系列教程。
前言
開發(fā)STM32固件升級并編寫B(tài)ootloader時,需要注意以下幾個關鍵點:
-
熟悉硬件和數(shù)據(jù)手冊:在開發(fā)過程中,確保充分理解STM32微控制器的特性和功能。閱讀相關數(shù)據(jù)手冊,了解其內存布局、外設接口以及其他重要信息。
-
選擇合適的通信接口:根據(jù)項目需求選擇合適的通信接口進行固件升級,如串口、I2C、SPI、USB等。確保所選接口可以與外部設備(如PC)正常通信。(后續(xù)會使用CAN UART)
-
定義固件升級協(xié)議:設計一個簡單且可靠的通信協(xié)議,用于在Bootloader和外部設備之間傳輸數(shù)據(jù)。協(xié)議應包括命令、地址、數(shù)據(jù)長度、數(shù)據(jù)包校驗等信息。
-
保留足夠的Bootloader空間:為Bootloader預留足夠的程序存儲空間。Bootloader的大小可能會隨著功能的增加而增大,因此預留一定的余量非常重要。
-
安全和魯棒性:確保Bootloader代碼具有良好的異常處理和錯誤檢測能力。避免因意外情況導致的設備損壞或不可恢復狀態(tài)。
-
可擴展性:在設計Bootloader時考慮到未來可能的功能擴展。保持代碼結構清晰,易于維護和升級。
-
測試與驗證:在實際硬件上對Bootloader進行充分測試,確保其下載、擦除、寫入等操作的正確性和穩(wěn)定性。
遵循以上關鍵點,在開發(fā)過程中保持耐心和細致,能有效編寫出一個可靠、高效的STM32 Bootloader。
編寫B(tài)ootloader程序
在編寫前我需要確定幾個地方:
-
bootloader
- 確定bootloader存放地址 0x08000000
- 配置bootloader中斷向量表
- 實現(xiàn)串口或USB等通信接口 UART
- 編寫flash擦除、編程函數(shù)
- 確定應用程序存放地址 0x08000000 + Boot_size + PARAM_SIZE
- 跳轉到應用程序入口 跳轉指令
-
注意事項
- 確認芯片型號和數(shù)據(jù)手冊,了解芯片的Flash大小和布局
- 確定應用程序和bootloader的存放地址
- 確定bootloader的觸發(fā)方式,如按鍵觸發(fā)、超時觸發(fā)等
- bootloader需要配置中斷向量表,以便跳轉到應用程序時正確執(zhí)行
- bootloader需要實現(xiàn)串口或USB等通信接口,以便與上位機進行通信
- bootloader需要編寫flash擦除、編程函數(shù),以便將應用程序下載到Flash中
- bootloader需要檢查應用程序的合法性,如校驗和、簽名等
- bootloader需要跳轉到應用程序入口,啟動應用程序
在MCU中,bootloader主要作用引導進入APP1程序,檢測升級標注位是否需要將備份APP2覆蓋到APP1中(升級新程序)或者接收升級包進行升級(可以是CAN、UART通信或者讀取SD卡獲取升級包)。bootloader程序盡量保持簡潔,不需要用到資源統(tǒng)統(tǒng)去掉(很容易出現(xiàn)問題,往往初始化的外設資源,最好都去初始化(保留原來的狀態(tài))),保證小巧、穩(wěn)定和擴展性。
需要實現(xiàn)的功能:flash擦除接口和通信接口,主要這兩個,還需要添加狀態(tài)和標注位。
Flash讀寫和擦除
Flash 功能接口
u32 STMFLASH_ReadWord(u32 faddr)
{
return *(vu32*)faddr;
}
uint16_t STMFLASH_GetFlashSector(u32 addr)
{
if(addr<ADDR_FLASH_SECTOR_1)return FLASH_Sector_0;
else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_Sector_1;
else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_Sector_2;
else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_Sector_3;
else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_Sector_4;
else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_Sector_5;
else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_Sector_6;
else if(addr<ADDR_FLASH_SECTOR_8)return FLASH_Sector_7;
else if(addr<ADDR_FLASH_SECTOR_9)return FLASH_Sector_8;
else if(addr<ADDR_FLASH_SECTOR_10)return FLASH_Sector_9;
else if(addr<ADDR_FLASH_SECTOR_11)return FLASH_Sector_10;
return FLASH_Sector_11;
}
void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)
{
FLASH_Status status = FLASH_COMPLETE;
u32 addrx=0;
u32 endaddr=0;
if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //非法地址
FLASH_Unlock(); //解鎖
FLASH_DataCacheCmd(DISABLE);//FLASH擦除期間,必須禁止數(shù)據(jù)緩存
addrx=WriteAddr; //寫入的起始地址
endaddr=WriteAddr+NumToWrite*4; //寫入的結束地址
if(status==FLASH_COMPLETE)
{
while(WriteAddr<endaddr)//寫數(shù)據(jù)
{
if(FLASH_ProgramWord(WriteAddr,*pBuffer)!=FLASH_COMPLETE)//寫入數(shù)據(jù)
{
break; //寫入異常
}
WriteAddr+=4;
pBuffer++;
}
}
FLASH_DataCacheCmd(ENABLE); //FLASH擦除結束,開啟數(shù)據(jù)緩存
FLASH_Lock();//上鎖
}
void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead)
{
u32 i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i]=STMFLASH_ReadWord(ReadAddr);//讀取4個字節(jié).
ReadAddr+=4;//偏移4個字節(jié).
}
}
實現(xiàn)在SD卡尋找APP程序,進行升級。
FIL fnew; /* 文件對象 */
FRESULT res_sd = FR_OK; /* 文件操作結果 */
UINT fnum; /* 文件成功讀寫數(shù)量 */
BYTE ReadBuffer[512]={0}; /* 讀緩沖區(qū) */
u8 checknum = 0;
int writeSum = 0;
while(SD_Init())
{
}
exfuns_init();
f_mount(fs[0],"0:",1);
Flash_Erase(APP1_ADDRESS);
while(1)
{
t++;
res_sd = f_open(&fnew, "0:APP2.bin", FA_OPEN_EXISTING | FA_READ);
if(res_sd == FR_OK)//有升級文件
{
printf("open bootloader\r\n");
fnum = 1;
do
{
res_sd = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
if(res_sd == FR_OK)
{
printf("##");
if(fnum!=0)
STMFLASH_Write(APP1_ADDRESS+writeSum,(u32*)ReadBuffer,fnum/4);
writeSum += fnum;
for(int i = 0 ; i < fnum;i++)
{
checknum ^= ReadBuffer[i];
printf("0x%2x ",ReadBuffer[i]);
}
printf("\r\n");
memset(ReadBuffer,0,sizeof(ReadBuffer));
}
else
{
printf("read file failth(%d)\n", res_sd);
}
}while(fnum > 0);
/* 不再讀,關閉文件 */
f_close(&fnew);
u8 readBuf[4];
u8 readCheckCrc = 0;
for(int i = 0;i < writeSum ; i++)
{
STMFLASH_Read(APP1_ADDRESS+i*4,(u32*)readBuf,1);
for(int j = 0;j<4;j++)
readCheckCrc ^= readBuf[j];
}
printf("boot jump app\r\n");
if(readCheckCrc == checknum && readCheckCrc!=0)
{
printf("check sum success jump APP1\r\n");
//跳轉
jump_to_app();
}
else
{
printf("check sum Failth\r\n");
}
while(1);
}
else
{
printf("not found bootloader \r\n");
}
LED0=!LED0; //狀態(tài)燈 bootloader狀態(tài)
}
上面還需要進行優(yōu)化,需要添加一些標志位可以在一個扇區(qū)或者在備份寄存器標記,來做升級標記。文章來源:http://www.zghlxwxcb.cn/news/detail-435287.html
跳轉指令 重點
#define APP1_ADDRESS 0x8020000
__asm void start_app(uint32_t r0_msp, uint32_t r1_pc)
{
MOV SP, R0 //R0的數(shù)值 其實就是參數(shù)r0_msp
BX R1 //R1 其實就是r1_pc
}
void jump_to_app(void)
{
start_app((*(uint32_t *)(APP1_ADDRESS)),(*(uint32_t *)(APP1_ADDRESS + 4)));
}
在實際操作過程中,遇到跳轉,沒能正常運行APP。
我進行分析,排除問題,把問題范圍縮小,可能會出現(xiàn)的問題:1、Flash讀寫接口有問題 2、FAT32文件系統(tǒng)讀取數(shù)據(jù)有問題。
看前面的代碼,我都加了日志信息輸出,把文件系統(tǒng)讀到的數(shù)據(jù)輸出,和真實的bin文件內容對比,對比問題是正確,說明問題出現(xiàn)在Flash擦除。
看下面的截圖,左邊是都是FF沒做寫入前做擦除,右邊是擦除后再寫,是寫入成功的。
寫入前要先做擦除處理。文章來源地址http://www.zghlxwxcb.cn/news/detail-435287.html
到了這里,關于bootloader編寫——MCU固件升級系列2(STM32)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!