引言
我們在向一些非專業(yè)開發(fā)者發(fā)放開發(fā)板時,有時對方沒有調試器,開發(fā)者就無法下載體驗在后續(xù)發(fā)布支持新功能的固件。并且,對于非專業(yè)開發(fā)者的用戶來說,僅僅是體驗新功能,而不介入開發(fā)工作,專門搭建一套開發(fā)環(huán)境,性價比實在不高。即使有調試器硬件,在不同操作系統(tǒng)平臺上,還需要安裝專門的工具軟件配合工作,才能實現(xiàn)下載固件的功能,操作比較繁瑣。為了簡化下載固件的操作,本例使用MM32F5270微控制器,基于芯片自帶的USB外設,實現(xiàn)了一個基于U盤拖拽更新固件的解決方案。我期望實現(xiàn)的結果是:
將開發(fā)板同PC連接后,PC將識別出一個U盤
可將開發(fā)板的固件(bin 格式)文件拖拽存放至該U盤中
復位開發(fā)板后,開發(fā)板能夠執(zhí)行新的固件程序
相對于板載帶有U盤拖拽下載功能的daplink方案,本例節(jié)約了一個專門運行daplink程序的芯片,利用微控制器自帶的USB外設直接建立同PC的連接,更可有機會被實現(xiàn)成ROM,固化在芯片內部。
原理
MM32F5270片內集成了256KB Flash,但可通過QSPI接口外擴spiflash存儲設備,并可在外擴spiflash中執(zhí)行程序(XIP)。本例實際將運行bootloader的片內Flash當做BOOT ROM,而將可執(zhí)行程序的固件存放在對接的spiflash存儲介質中。當芯片上電后,通過一些外部控制手段(例如使用一個按鍵選擇啟動模式),先運行帶有USB功能的bootloader程序,由bootloader的程序將芯片模擬成U盤(U盤的物理存儲空間位于外擴spiflash的后半段)。PC可以看到模擬U盤中的現(xiàn)存文件,也可以向其中拖拽新文件。當拖拽新文件后,bootloader程序會繼續(xù)將新文件的程序內容復制到程序的執(zhí)行區(qū)域,覆蓋掉之前的程序,然后再跳轉到程序的執(zhí)行區(qū)域執(zhí)行程序。
這里專門使用一塊物理存儲區(qū)域(spiflash存儲器的后半段)的原因是,USB協(xié)議棧模擬U盤時,對文件系統(tǒng)的管理操作全部交由PC完成,這就意味著,PC向模擬U盤發(fā)送包含文件內容的數(shù)據(jù)包時,不一定是按照物理上的先后順序發(fā)送的,微控制器端難以通過數(shù)據(jù)包本身解析出數(shù)據(jù)包的先后順序。但是,bootloader向程序執(zhí)行區(qū)域復制可執(zhí)行程序的內容必須是順序且連續(xù)的。PC在同U盤的通信過程中,數(shù)據(jù)包之間的內容可能不連續(xù),但最終發(fā)送完成的文件內容一定是完整的。因此,這里使用使用了一個能包含整個固件文件大小的區(qū)域作為緩沖區(qū),先緩沖下來整個完整的文件,然后再將完整的文件內容按順序復制到程序的可執(zhí)行區(qū)域。
理想情況下,如果能用一塊RAM作為整個文件的緩沖區(qū),通過U盤拖拽下載過程將會非???。但本例中使用spiflash作為緩沖區(qū),雖然擦除spiflash并向其寫入數(shù)據(jù)需要花費更多的時間,但能容納更大的程序文件。
如果芯片內部集成足夠大的片內Flash,也可以不依賴外擴spiflash,對內部的Flash做分區(qū),分別作為bootloader、運行程序及文件緩存的存儲區(qū)。
實現(xiàn)
硬件相關
本例使用BIRD-F5270開發(fā)板,開發(fā)板基本情況如下:
晶振 12MHz
使用QSPI接口對接外擴spiflash存儲芯片F(xiàn)M25Q16(2MB Flash),引腳與MM32F5280片內合封所使用的引腳保持一致:
QSPI_NSS - PF6
QSPI_SCK - PG7
QSPI_D0 - PG6
QSPI_D1 - PF8
QSPI_D2 - PF10
QSPI_D3 - PG8
USB 引腳
USB_DP - PA12
USB_DM - PA11
按鍵引腳
SW1 - RESET
SW2 - BOOT0
SW3 - PA0
軟件相關
啟動到2nd bootloader
芯片上電復位后,默認是直接執(zhí)行到用戶程序中,但也可以通過指定的手段改為執(zhí)行bootloader,執(zhí)行U盤程序。本例使用一個按鍵,綁定到一個指定的GPIO輸入(BOOT引腳),芯片在啟動過程中判定啟動模式:默認啟動到用戶程序,當按鍵按下時,啟動到帶有U盤程序的bootloader。
MM32F5芯片自帶的BOOT0引腳雖然在微控制器復位后可作為普通GPIO使用,但可能會將芯片引導到芯片內部已經固化到ROM中的bootloader程序中,因此不適合作為2nd bootloader的BOOT引腳。BIRD-F5270開發(fā)板上除RESET和BOOT0 之外,還有PA0引腳實現(xiàn)按鍵(SW3)功能,可作為2nd bootloader中的BOOT引腳。
當 SW3 被按下時,PA0 為低電平,可執(zhí)行 U 盤任務,當 SW3 松開時,PA0 為高電平,可執(zhí)行跳轉程序的任務,具體實現(xiàn)代碼如下:
/*readgpiopinlevel,selectbootmode.*/ if(GPIO_ReadInDataBit(BOARD_BTN_PORT,BOARD_BTN_PIN)) { /*updateapp&run.*/ jump_to_app(); } else { /*runusbmsctask.*/ msc_task(); } /*cannotrunhere.*/ while(1) {}
基于USB外設模擬U盤
MM32F5270微控制器集成了USB外設,配合TinyUSB協(xié)議棧,可模擬U盤(MSC設備)。
當運行模擬U盤的程序時,需要指定一塊存儲區(qū)域作為U盤的存儲空間,在本例中,將外擴的spiflash存儲區(qū)域一分為二,前半部分作為執(zhí)行程序區(qū)域,存儲需要執(zhí)行的應用程序,后半部分作為U盤的物理存儲空間,將用于緩存用戶通過拖拽方式下載的固件。
注意,spiflash作為U盤的存儲介質時需要解決一個問題,spiflash的最小擦除塊大小是4KB,而U盤設備對存儲介質的擦除塊大小是512B,擦除塊大小不匹配。該問題有兩種解法:
每次修改spiflash前,先將包含目標操作空間的4KB大小的數(shù)據(jù)塊讀取數(shù)據(jù)到RAM中緩存,修改指定位置的數(shù)據(jù)內容后,擦除spiflash中對應的整塊,再將修改后的數(shù)據(jù)塊寫入到spiflash中。
每次修改spiflash前,先將包含目標操作空間的4KB大小的數(shù)據(jù)塊讀取數(shù)據(jù)到RAM中緩存,修改指定位置的數(shù)據(jù)內容后,暫不寫回到spiflash中。如果接下來要寫入的數(shù)據(jù)仍在該塊中,則繼續(xù)寫入RAM緩存。只有當將要新寫入的數(shù)據(jù)不在緩存的數(shù)據(jù)塊中時,才寫回最近緩存的數(shù)據(jù)塊,并讀出新的數(shù)據(jù)塊到RAM緩存中。另外,每次在寫操作之后的讀操作,也都會重新清空RAM緩存中的數(shù)據(jù)。
第一種方法簡單易實現(xiàn),但會頻繁擦寫spiflash,消耗存儲器件的壽命。第二種方法相當于實現(xiàn)了一個cache機制,操作行為較為復雜。當前暫時選擇第一種做法,先驗證原理,如果后續(xù)考慮將這個帶U盤功能的bootloader集成到芯片內部,到時也可以專門集成一塊以512B作為操作單元的dflash作為固件的緩存存儲設備。
運行TinyUSB協(xié)議棧的程序中,在執(zhí)行MSC任務時,會執(zhí)行對spiflash存儲設備進行讀寫操作的回調函數(shù),如下:
從spiflash讀數(shù)據(jù):
//CallbackinvokedwhenreceivedREAD10command. //Copydisk'sdatatobuffer(uptobufsize)andreturnnumberofcopiedbytes. int32_ttud_msc_read10_cb(uint8_tlun,uint32_tlba,uint32_toffset,void*buffer,uint32_tbufsize) { (void)lun; uint8_t*addr=(uint8_t*)(BOARD_MEM_BASE+lba*512+offset); memcpy(buffer,addr,bufsize); returnbufsize; }
向spiflash寫數(shù)據(jù):
staticuint8_tflash_buf[4096]; //CallbackinvokedwhenreceivedWRITE10command. //Processdatainbuffertodisk'sstorageandreturnnumberofwrittenbytes int32_ttud_msc_write10_cb(uint8_tlun,uint32_tlba,uint32_toffset,uint8_t*buffer,uint32_tbufsize) { (void)lun; /*readthe4Ksectorfromspiflashtolocalbuff.*/ uint8_t*flash_addr=(uint8_t*)((BOARD_MEM_BASE+lba*512)&0xFFFFF000); memcpy(flash_buf,flash_addr,4096); /*erasethesectorinspiflash.*/ BOARD_EraseSector4KB((uint32_t)flash_addr); /*writethenewdataintolocalbuff.*/ uint8_t*block=flash_buf+((lba*512)&0xFFF)+offset; memcpy(block,buffer,bufsize); /*writethedatafromlocalbufftospiflash.*/ BOARD_WriteSector4KB((uint32_t)flash_addr,flash_buf); returnbufsize; }
另外,還有一種做法是不需要真實存在的物理存儲空間作為U盤的存儲介質,而是實時解算PC發(fā)過來的要存儲在Flash上的數(shù)據(jù)包,解析其中哪些數(shù)據(jù)是文件系統(tǒng)相關的信息,哪些數(shù)據(jù)是文件本身內容。此時,可丟棄文件系統(tǒng)相關的信息,而將文件本身內容按順序存放在spiflash的指定區(qū)域。該方法不需要將spiflash空間一分為二,可提高對spiflash的空間利用率,但該實現(xiàn)該方法需要十分熟悉FAT文件系統(tǒng)格式,且萬一電腦未按照指定的序列發(fā)數(shù)數(shù)據(jù)時,該方法無法正確解算出文件內容,造成更新固件失敗,因此,在本例中未實現(xiàn)該功能。
判斷是否存在新固件
前文中實現(xiàn)的U盤功能,將包括文件系統(tǒng)信息的新固件儲存在了spiflash的后半段空間中,bootloader程序要讀取新固件中的內容也需要能夠操作文件系統(tǒng),本例中為bootloader程序性集成了嵌入式系統(tǒng)上常見的文件系統(tǒng)組件為 FatFs(http://elm-chan.org/fsw/ff/00index_e.html)
本項目中,僅需要 FatFs 實現(xiàn)文件的讀功能,不需要對文件進行寫操作,因此只需要在適配接口上實現(xiàn)讀操作即可, 配置文件ff_conf.h中配置宏選項 FF_FS_READONLY 的值為1。然后在diskio.c文件中適配文件系統(tǒng)對具體物理介質的讀操作函數(shù)disk_read()如下:
DRESULTdisk_read( BYTEpdrv,/*Physicaldrivenmubertoidentifythedrive*/ BYTE*buff,/*Databuffertostorereaddata*/ LBA_tsector,/*StartsectorinLBA*/ UINTcount/*Numberofsectorstoread*/ ) { (void)pdrv; BYTE*flash_addr=(BYTE*)(BOARD_MEM_BASE+sector*FF_MAX_SS); memcpy(buff,flash_addr,count*FF_MAX_SS); return0; }
如何在文件系統(tǒng)中的眾多文件中識別微控制器可執(zhí)行程序的固件呢?本例采用通過文件名來確定是否為固件。
通過 FatFs 嘗試打開一個有宏BOARD_APP_NAME指定名稱的文件,若能成功打開該文件,則說明有可用的固件文件存在。
if(FR_OK==f_open(&file,"0:/"BOARD_APP_NAME,FA_READ)) { ... }
為什么不能使用任意bin判斷文件作為固件呢?這里考慮到,當文件系統(tǒng)中有多個bin文件時,bootloader 沒有依據(jù)判斷哪一個固件為新固件(根據(jù)修改日期判斷是否可行,作為可移動存儲設備,會插在不同電腦上,如果不同電腦的時間不一致,則會出現(xiàn)判斷失誤的情況,因此不選擇使用修改日期作為新固件的判斷依據(jù)),指定固定文件名的方式判斷固件,最簡單有效,問題最少。
驗證固件的有效性
不少人都應該有這樣的經歷,在網上下載大文件如大型游戲時,往往下載頁面會提供一串 MD5 碼,這段 MD5 碼的作用是驗證下載的文件是否完整,當用戶在網上下載大文件完成后,可在 CMD 中使用 certutil -hashfile
MicrosoftWindows[版本10.0.22621.1848] (c) Microsoft Corporation。保留所有權利。 E:>certutil-hashfileproject.binMD5 MD5的project.bin哈希: 615ca0da12d496b7a8b1bd6c21876a97 CertUtil:-hashfile 命令成功完成。 E:>
在嵌入式領域,Arm的MbedTLS加解密算法庫中提供了MD5的計算方法,因此可使用MbedTLS計算MD5,有程序如下:
/*calcmd5.*/ mbedtls_md5_contextmd5_ctx; mbedtls_md5_init(&md5_ctx); mbedtls_md5_starts(&md5_ctx); while(1) { UINTbr=0; f_read(&file,app_buf,sizeof(app_buf),&br); mbedtls_md5_update(&md5_ctx,app_buf,br); if(br
除了 MD5 以外,還可以使用如 SHA1,SHA2 或者 SM3 等更高安全等級的散列值算法作為新固件的判斷依據(jù)。
復制固件到執(zhí)行區(qū)域
復制固件到執(zhí)行區(qū)域的做法較為簡單,就是使用 f_read() 函數(shù)讀取數(shù)據(jù),再寫入到spiflash的指定區(qū)域即可。
但除了復制固件文件內容本身,其文件的MD5值也要寫入到Flash中。應在寫新固件之前將之前的MD5值擦除,寫新固件完成之后,再將新MD5值寫入,如此一來,MD5除了作為檢查新固件的完整性的方法外,還可作為執(zhí)行區(qū)域固件完整性檢查的重要依據(jù)。
本例中,將MD5值存放在片內flash的末尾。
檢查新固件和復制固件到執(zhí)行區(qū)域的代碼如下:
/*checkmd5,themd5lengthis16byte.*/ if(memcmp((uint8_t*)BOARD_MD5_BASE,md5_val,sizeof(md5_val))!=0) { /*newfile,update.*/ BOARD_EraseSector4KB(BOARD_MD5_BASE); uint32_thas_write=0; while(1) { UINTbr=0; memset(app_buf,0xff,sizeof(app_buf)); f_read(&file,app_buf,sizeof(app_buf),&br); BOARD_EraseSector4KB(has_write); BOARD_WriteSector4KB(has_write,app_buf); has_write+=4096; if(br
需要使用完整的一個塊存放MD5值,MD5必須保證單獨擦除,單獨寫入。除了MD5以外,該區(qū)域還可以可根據(jù)需要存放一些固件相關的信息,比如說修改時間,固件大小等信息,充分利用空間。
跳轉執(zhí)行
跳轉程序需要做的事情包括:恢復系統(tǒng)時鐘,修改中斷向量表位置,復位棧指針,執(zhí)行應用程序的復位程序等。具體操作如下:
voidjump_to_app(void) { ... /*jumptoapp.*/ CLOCK_ResetToDefault(); vtor=(uint32_t*)BOARD_APP_BASE; __disable_irq(); SCB->VTOR=BOARD_APP_BASE; __ISB(); __DSB(); __set_MSP(vtor[0]); __set_PSP(vtor[0]); __enable_irq(); ((void(*)(void))vtor[1])(); }
用戶應用程序
為了配合bootloader正常工作,需要用戶在自己的應用程序需調整以下內容:
在Linker File中指定可執(zhí)行程序在分塊后的存儲區(qū)
避免誤配置GPIO和QSPI導致BOOT按鍵或外擴spiflash失效
本例實現(xiàn)的bootloader,spiflash作為應用程序的執(zhí)行區(qū)域,代碼段的起始位置位于spiflash的起始映射地址0x90000000,不是片內Flash的0x08000000,因此,需要調整Linker中對應的配置如下:
/*---------------------FlashConfiguration---------------------------------- ;FlashConfiguration ; *----------------------------------------------------------------------------*/ #define__ROM_BASE0x90000000 #define__ROM_SIZE0x00100000FlashBaseAddress<0x0-0xFFFFFFFF:8> ; FlashSize(inBytes)<0x0-0xFFFFFFFF:8> ;
若使用spiflash存儲應用程序,bootloader在執(zhí)行應用程序前就應該將 QSPI 接口和 GPIO 引腳配置好。在用戶應用程序中,如果需要操作QSPI或者bootloader中使用的GPIO,要特別注意避開。
總結
通過USB接口將BIRD-F5270連接電腦,保持SW3按鍵按下時,再按下并松開RESET按鍵,稍后會在PC上看到一個大小約為1MB的U盤,將新編譯好的名為 “project.bin” 的固件文件拖拽存放至該U盤中,然后保持SW3按鍵松開的狀態(tài)下再次按下并松開RESET按鍵,稍后即可發(fā)現(xiàn)微控制器已經在執(zhí)行新固件中的應用程序了。
作為下載新固件的 U 盤,還可以存放一些別的文件,供應用程序使用,例如一張圖片,一首音樂,或一段文字,但需要注意的是,應用程序對該文件系統(tǒng)只有讀操作的權限,不能進行寫操作(或者在bootloader中實現(xiàn)安全寫操作的接口,讓應用程序去調用相應的寫操作接口)
known issue
開發(fā)板通過 USB 連接電腦,保持 SW3 按鍵按下的狀態(tài),多次快速按下復位鍵,可能會造成文件系統(tǒng)損壞,文件丟失,需重新格式化才能正常使用,其原因是兩次按下復位鍵中間,電腦正在修改 Flash 中的內容,但復位操作打斷了寫操作,造成文件系統(tǒng)損壞。因此需避免快速多次按下復位鍵的操作。這里可考慮使用一個可編程的指示燈,指示芯片內部程序的工作狀態(tài):當拖拽固件文件到芯片時,指示燈快速閃爍;當復制新固件文件到程序運行區(qū)域時,指示燈保持常亮;當復制完畢后,指示燈熄滅。僅當指示燈熄滅后,方可重新復位芯片執(zhí)行新程序。
審核編輯:劉清
-
微控制器
+關注
關注
48文章
7454瀏覽量
150853 -
ROM
+關注
關注
4文章
562瀏覽量
85623 -
調試器
+關注
關注
1文章
300瀏覽量
23667 -
SPI Flash
+關注
關注
1文章
13瀏覽量
10328 -
QSPI接口
+關注
關注
0文章
14瀏覽量
3328
原文標題:定制帶U盤功能的bootloader實現(xiàn)拖拽下載固件
文章出處:【微信號:pzh_mcu,微信公眾號:痞子衡嵌入式】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論