1 前言
DFU用來做IAP是很方便的,可以直接通過USB來對(duì)APP進(jìn)行升級(jí),因此,掌握DFU的制作還是挺有好處,特別是使用CubeMx工具可以快速制作,本文將基于STM3240G-EVL評(píng)估板來一步一步實(shí)現(xiàn)一個(gè)DFU的IAP工程。
2 制作CubeMx工程
新建一個(gè)STM32F407IGHx工程:Pinout:
Peripherals:RCC->High Speed Clock(HSE):Crystal/Ceramic ResonatorSYS->Debug:Serial WireUSB_OTG_FS->Mode:Device_Only
MiddleWaresUSB_DEVICE->Class For FS IP:Download Firmware Update Class(DFU)
再配置PG15腳為GPIO_Input模式。
Clock Configuration:
圖1 時(shí)鐘樹設(shè)置
如上圖,STM3240G-EVAL評(píng)估板使用的是25M HSE。Configuration:NVIC中將USB中斷優(yōu)先級(jí)調(diào)為5,PG15的標(biāo)簽設(shè)置為USER_BTN,此外還需要設(shè)置中間件USB DFU參數(shù),如下圖:
圖2 USB DFU參數(shù)設(shè)置
如上圖,紅色框內(nèi)為需要修改的代碼,0x0800C000為需要為用戶程序APP燒錄的起始地址,字符串“@Internal Flash /0x08000000/03016Ka,01016Kg,01064Kg,07128Kg”實(shí)際為USB DFU類的interface字符串描述符,在USB DFU標(biāo)準(zhǔn)文件中有提到可選接口可以使用一個(gè)對(duì)應(yīng)的接口字符串來表示此可選接口對(duì)應(yīng)的目標(biāo)設(shè)備的存儲(chǔ)塊信息,但如何具體規(guī)定的,DFU標(biāo)準(zhǔn)(DFU_1.1)并沒有要求,是開放的,如下:
圖3 DFU標(biāo)準(zhǔn)對(duì)接口字符串定義的描述
由此可見,接口字符串定義是可以自由定義的,那么在這里,由于使用到ST工具軟件DfuSe Demo(v3.0.5),那么這個(gè)工具與USB DFU設(shè)備就有一個(gè)自定義的接口字符串定義,用來表示當(dāng)前MCU內(nèi)部的FLASH組織結(jié)構(gòu)。
接下來我們來看看MCU內(nèi)部FLASH的組織,由于這里的MCU是STM32F407IGHx,找到其參考文檔,并查看其內(nèi)部FLASH組織結(jié)構(gòu):
圖4 STM32F407內(nèi)部FLASH的組織結(jié)構(gòu)
如上圖,STM32F407內(nèi)部FLASH包含4個(gè)16K扇區(qū)+1個(gè)64K扇區(qū)+7個(gè)128K扇區(qū),并且起始地址為0x0800 0000,所以它對(duì)應(yīng)的接口字符串表示為: “@Internal Flash /0x08000000/03016Ka,01016Kg,01064Kg,07128Kg”, Internal Flash為在工具軟件顯示的名稱,0x08000000為起始地址,03016Ka表示3個(gè)16K大小只讀的扇區(qū),01064Kg表示1個(gè)64K大小的可讀寫扇區(qū),07*128Kg表示7個(gè)128K大小的可讀寫扇區(qū),后綴a表示只讀,后綴g表示可讀寫。這個(gè)就是工具軟件DfuSe Demo(v3.0.5)與DFU設(shè)備之間的約定。如下:
圖5 DfuSeDemo軟件中所顯示的內(nèi)部FLASH的可讀寫屬性
知道了這些信息后,我們?cè)倩剡^頭來看APP的起始地址0x0800C000,那么APP的起始地址該如何得來的?有什么要求?與這個(gè)接口字符串之間是否有關(guān)系?
到目前為止,我們可以確定地是,APP_DEFAULT_ADD的地址必須是位于接口字符串表示的可讀寫的地址范圍內(nèi),也就是第4個(gè)扇區(qū)起(前3個(gè)扇區(qū)都是只讀的),不然是燒錄不進(jìn)去的。其他問題我們先暫且放一放,后續(xù)我們回過頭來會(huì)回答這個(gè)問題。
Project Setting :堆設(shè)置為0x500,棧大小設(shè)置為0x2000。
圖6 堆棧設(shè)置
另外,在高級(jí)設(shè)置中,設(shè)置先不調(diào)用對(duì)USB DFU的初始化:
圖7 高級(jí)設(shè)置
最后生成代碼。
3 代碼完善
對(duì)生成后的代碼是可以直接編譯通過的,我們這里使用的是IAR,當(dāng)然你也可以使用MDK,由于不同編譯器編譯的最終文件大小有所差異,而APP的偏移地址在一定程度上也是有考慮到這個(gè)DFU本身代碼大小的,接下來我們都將以IAR為例。
打開usbd_duf_if.c文件,這個(gè)文件就是USB DFU CLASS與本地對(duì)接的接口實(shí)現(xiàn)文件,我們需要對(duì)這個(gè)源文件內(nèi)沒有接口填充其具體實(shí)現(xiàn)內(nèi)容,當(dāng)然,我們主要的目的是想借助DFU這個(gè)IAP來實(shí)現(xiàn)對(duì)APP的升級(jí)和跳轉(zhuǎn),而這些接口就是實(shí)現(xiàn)對(duì)FLASH讀寫的操作。
uint16_tMEM_If_Init_FS(void) { /*USERCODEBEGIN0*/ HAL_FLASH_Unlock(); return(USBD_OK); /*USERCODEEND0*/ }
如上,初始化實(shí)現(xiàn)對(duì)FALSH的解鎖。
uint16_tMEM_If_DeInit_FS(void) { /*USERCODEBEGIN1*/ HAL_FLASH_Lock(); return(USBD_OK); /*USERCODEEND1*/ }
對(duì)應(yīng)地,反初始化時(shí),實(shí)現(xiàn)對(duì)FALSH的上鎖。
uint16_tMEM_If_Erase_FS(uint32_tAdd) { /*USERCODEBEGIN2*/ uint32_tstartsector=0; uint32_tsectornb=0; /*VariablecontainsFlashoperationstatus*/ HAL_StatusTypeDefstatus; FLASH_EraseInitTypeDeferaseinitstruct; /*Getthenumberofsector*/ startsector=GetSector(Add); eraseinitstruct.TypeErase=FLASH_TYPEERASE_SECTORS; eraseinitstruct.VoltageRange=FLASH_VOLTAGE_RANGE_3; eraseinitstruct.Sector=startsector; eraseinitstruct.NbSectors=1; status=HAL_FLASHEx_Erase(&eraseinitstruct,§ornb); if(status!=HAL_OK) { return1; } return0; /*USERCODEEND2*/ }
如上,實(shí)現(xiàn)對(duì)FLASH擦除操作。對(duì)應(yīng)的GetSector函數(shù)實(shí)現(xiàn)如下:
staticuint32_tGetSector(uint32_tAddress) { uint32_tsector=0; if((Address=ADDR_FLASH_SECTOR_0)) { sector=FLASH_SECTOR_0; } elseif((Address=ADDR_FLASH_SECTOR_1)) { sector=FLASH_SECTOR_1; } elseif((Address=ADDR_FLASH_SECTOR_2)) { sector=FLASH_SECTOR_2; } elseif((Address=ADDR_FLASH_SECTOR_3)) { sector=FLASH_SECTOR_3; } elseif((Address=ADDR_FLASH_SECTOR_4)) { sector=FLASH_SECTOR_4; } elseif((Address=ADDR_FLASH_SECTOR_5)) { sector=FLASH_SECTOR_5; } elseif((Address=ADDR_FLASH_SECTOR_6)) { sector=FLASH_SECTOR_6; } elseif((Address=ADDR_FLASH_SECTOR_7)) { sector=FLASH_SECTOR_7; } elseif((Address=ADDR_FLASH_SECTOR_8)) { sector=FLASH_SECTOR_8; } elseif((Address=ADDR_FLASH_SECTOR_9)) { sector=FLASH_SECTOR_9; } elseif((Address=ADDR_FLASH_SECTOR_10)) { sector=FLASH_SECTOR_10; } else { sector=FLASH_SECTOR_11; } returnsector; }
寫操作:
uint16_tMEM_If_Write_FS(uint8_t*src,uint8_t*dest,uint32_tLen) { /*USERCODEBEGIN3*/ uint32_ti=0; for(i=0;i
如上,實(shí)現(xiàn)對(duì)FLASH的寫操作。
uint8_t*MEM_If_Read_FS(uint8_t*src,uint8_t*dest,uint32_tLen) { /*ReturnavalidaddresstoavoidHardFault*/ /*USERCODEBEGIN4*/ uint32_ti=0; uint8_t*psrc=src; for(i=0;i
讀FLASH接口實(shí)現(xiàn)。
uint16_tMEM_If_GetStatus_FS(uint32_tAdd,uint8_tCmd,uint8_t*buffer) { /*USERCODEBEGIN5*/ switch(Cmd) { caseDFU_MEDIA_PROGRAM: buffer[1]=(uint8_t)FLASH_PROGRAM_TIME; buffer[2]=(uint8_t)(FLASH_PROGRAM_TIME<8); ????buffer[3]?=?0; ????break; ??case?DFU_MEDIA_ERASE: ????buffer[1]?=?(uint8_t)FLASH_ERASE_TIME; ????buffer[2]?=?(uint8_t)(FLASH_ERASE_TIME?<8); ????buffer[3]?=?0; ??default: ????break; ??} ??return??(USBD_OK); ??/*?USER?CODE?END?5?*/?? }
獲取狀態(tài)接口實(shí)現(xiàn)。
接下來實(shí)現(xiàn)從DFU跳轉(zhuǎn)到APP的功能,在main函數(shù)中 :
/*USERCODEBEGIN2*/ if(HAL_GPIO_ReadPin(USER_BTN_GPIO_Port,USER_BTN_Pin)==GPIO_PIN_SET) { /*Testifusercodeisprogrammedstartingfromaddress0x0800C000*/ if(((*(__IOuint32_t*)USBD_DFU_APP_DEFAULT_ADD)&0x2FFE0000)==0x20000000) { /*Jumptouserapplication*/ JumpAddress=*(__IOuint32_t*)(USBD_DFU_APP_DEFAULT_ADD+4); JumpToApplication=(pFunction)JumpAddress; /*Initializeuserapplication'sStackPointer*/ __set_MSP(*(__IOuint32_t*)USBD_DFU_APP_DEFAULT_ADD); JumpToApplication(); } } MX_USB_DEVICE_Init(); /*USERCODEEND2*/
這樣代碼就大體修改完了,再次編譯下,生成最終可執(zhí)行文件。我們得到IAR如下編譯信息:
18170bytesofreadonlycodememory 290bytesofreadonlydatamemory 12517bytesofreadwritedatamemory
那么DFU這個(gè)IAP本身所占ROM大小為(18170+290 )/1024 =18.02K,從圖4中我們可以得知,它需要占用兩個(gè)扇區(qū)(扇區(qū)0和1都是16K大小),那么APP至少應(yīng)該是從扇區(qū)2開始。
此時(shí),我們回過頭去看之前提到的APP偏移地址的問題,此處結(jié)合之前說到的APP必須是第4個(gè)扇區(qū)起,那么最終APP的地址應(yīng)該設(shè)置在第4個(gè)扇區(qū)的起始位置,也就是扇區(qū)3的位置,從圖4可知,扇區(qū)3的起始位置為0x0800C000,這樣我們回到CubeMx中將其設(shè)置,這也就是為什么APP地址設(shè)置為0x0800C000的原因。
重新編譯并燒錄進(jìn)MCU,接下來連接USB到PC,接可是識(shí)別這個(gè)DFU設(shè)備,并通過DfuSeDemo這個(gè)軟件升級(jí)APP了。
4 制作APP工程需要注意事項(xiàng)
不同編譯器設(shè)置方式略有不同,在IAR中:首先將system_stm32f4xx.c文件中找到VECT_TAB_OFFSET宏定義 :
#defineVECT_TAB_OFFSET0xC000 1
即將中斷向量表的偏移位置相應(yīng)偏移0xC000.接下來修改連接選項(xiàng) :
圖8 IAR鏈接設(shè)置
MDK中:?首先也是修改system_stm32f4xx.c文件中的VECT_TAB_OFFSET宏定義.接著 :
圖9 Target設(shè)置
相應(yīng)設(shè)置好了接可以了。
5 測試
最后就是通過ST的軟件Dfu File Manager這個(gè)軟件將APP的HEX文件或BIN文件轉(zhuǎn)化成dfu文件,然后通過DfuSeDemo這個(gè)軟件導(dǎo)入dfu文件,最終燒錄APP到0x0800C000這個(gè)地址了,最終驗(yàn)證是可以運(yùn)行的。
6 總結(jié)
APP的起始地址應(yīng)該設(shè)置為扇區(qū)的起始地址,且即使沒有重疊,也不能放在IAP的所在扇區(qū)。
APP的起始地址必須在USB DFU CLASS接口字符串所描述的可讀寫扇區(qū)范圍內(nèi)。
審核編輯:湯梓紅
-
APP
+關(guān)注
關(guān)注
33文章
1577瀏覽量
72774 -
IAP
+關(guān)注
關(guān)注
2文章
164瀏覽量
24388 -
CubeMx
+關(guān)注
關(guān)注
0文章
31瀏覽量
1378
原文標(biāo)題:如何使用CubeMx生成一個(gè)DFU工程
文章出處:【微信號(hào):技術(shù)讓夢(mèng)想更偉大,微信公眾號(hào):技術(shù)讓夢(mèng)想更偉大】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論