什么是 DFU
DFU全稱(chēng)為Device Firmware update,是ST官方推出的一個(gè)通過(guò)USB接口進(jìn)行IAP升級(jí)的方案,同串口ISP一樣,他們都集成在了芯片內(nèi)部的Bootloader區(qū)段,可以通過(guò)配置boot引腳來(lái)啟動(dòng)。(具體可參照ST文檔:AN2606)。不過(guò)內(nèi)置DFU的芯片大部分型號(hào)都比較新,如果你用的型號(hào)沒(méi)有內(nèi)置DFU程序,沒(méi)關(guān)系我們也可以通過(guò)CubeMX來(lái)快速生成和移植一個(gè)DFU功能程序到你的Flash中來(lái)使用。
DFU方案完整的組件包括單片機(jī)DFU Demo代碼、PC端升級(jí)程序、PC端Demo代碼以及相關(guān)資料手冊(cè)等。通過(guò)使用DFU方案,我們可以快速的集成升級(jí)功能到開(kāi)發(fā)的產(chǎn)品中,同時(shí)還能夠快速的開(kāi)發(fā)與之配套的升級(jí)程序。
使用CubeMX生成初始工程
由于官方提供的DFU例程并不多,我們很難找到現(xiàn)成的可已使用DFU程序,但是通過(guò)CubeMX我們可以很快速的配置和生成DFU的Bootloader,下面我們正式開(kāi)始。
新建CubeMX工程
首先選定好IC的型號(hào),進(jìn)入配置界面,由于只是Bootloader代碼所以這里我們只需要配置USB功能和一個(gè)做Bootloader觸發(fā)的引腳就可,其余的時(shí)鐘等部分一切按照正常方式配置。
設(shè)置USB引腳功能
設(shè)定USB模式為Device(HS還是FS并不影響DFU的功能,按照應(yīng)用選擇就可)。
開(kāi)啟DFU組件
在MiddleWares中加入U(xiǎn)SB DFU組件
設(shè)置DFU參數(shù)
開(kāi)啟DFU組件后,CubeMX的程序設(shè)置窗口的MiddleWares中就會(huì)出現(xiàn)DFU程序設(shè)置按鈕。
點(diǎn)開(kāi)它將APP加載的地址改為0x0800_c000,這個(gè)加載地址根據(jù)你實(shí)際的應(yīng)用設(shè)置,目前我們選擇讓flash的前三個(gè)sector為Bootloader的區(qū)域。
第二個(gè)全是字段的參數(shù)是用來(lái)在DFU連接升級(jí)軟件式傳輸給軟件用來(lái)獲取Flash結(jié)構(gòu)字符串?dāng)?shù)據(jù),很好理解這個(gè)小協(xié)議的內(nèi)容,點(diǎn)擊設(shè)置后,下方的CubeMX的參數(shù)說(shuō)明也寫(xiě)的很清晰,這里就不多說(shuō)了。當(dāng)然這些參數(shù)也在工程生成后在 usbd_conf.h 和 usbd_dfu_if.c 文件中修改。
最后的設(shè)置
最后我們添加一個(gè)外部的按鍵作為觸發(fā)單片機(jī)啟動(dòng)時(shí)進(jìn)入DFU的方式,按鍵按下后就啟動(dòng)DFU模式,否則直接加載后方APP程序,這里選用PA0引腳,給它設(shè)置個(gè)User Label 就叫 USER_BTN_GPIO_Port。
修改補(bǔ)全工程
實(shí)現(xiàn) DFU 功能代碼
打開(kāi) src 目錄下的 usbd_dfu_if.c 文件補(bǔ)全其中的功能代碼
Flash 解鎖
-
uint16_tMEM_If_Init_HS(void) { HAL_FLASH_Unlock(); return(USBD_OK); }
Flash 上鎖
uint16_tMEM_If_DeInit_HS(void) { HAL_FLASH_Lock(); return(USBD_OK); }
Flash 擦除
staticuint32_tGetSector(uint32_tAddress) { uint32_tsector=0; if((Address=ADDR_FLASH_SECTOR_0)) { sector=FLASH_SECTOR_0; } ...... } elseif((Address=ADDR_FLASH_SECTOR_22)) { sector=FLASH_SECTOR_22; } else { sector=FLASH_SECTOR_23; } returnsector; } uint16_tMEM_If_Erase_HS(uint32_tAdd) { 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) { return(USBD_FAIL); } return(USBD_OK); }
Flash 寫(xiě)入
uint16_tMEM_If_Write_HS(uint8_t*src,uint8_t*dest,uint32_tLen) { uint32_ti=0; for(i=0;i4) { /*Devicevoltagerangesupposedtobe[2.7Vto3.6V],theoperationwill bedonebybyte*/ if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,(uint32_t)(dest+i),*(uint32_t*)(src+i))==HAL_OK) { /*Checkthewrittenvalue*/ if(*(uint32_t*)(src+i)!=*(uint32_t*)(dest+i)) { /*Flashcontentdoesn'tmatchSRAMcontent*/ return(USBD_FAIL); } } else { /*ErroroccurredwhilewritingdatainFlashmemory*/ return(USBD_FAIL); } } return(USBD_OK); }
Flash 讀取
uint8_t*MEM_If_Read_HS(uint8_t*src,uint8_t*dest,uint32_tLen) { /*ReturnavalidaddresstoavoidHardFault*/ uint32_ti=0; uint8_t*psrc=src; for(i=0;i/*ReturnavalidaddresstoavoidHardFault*/ return(uint8_t*)(dest); }
獲取 Flash 擦寫(xiě)時(shí)間參數(shù)
uint16_tMEM_If_GetStatus_HS(uint32_tAdd,uint8_tCmd,uint8_t*buffer) { /*USERCODEBEGIN11*/ uint16_ttime; time=TimingTable[GetSector(Add)]; switch(Cmd) { caseDFU_MEDIA_PROGRAM: buffer[1]=(uint8_t)time; buffer[2]=(uint8_t)(time<8); buffer[3]=0; break; caseDFU_MEDIA_ERASE: default: buffer[1]=(uint8_t)time; buffer[2]=(uint8_t)(time<8); buffer[3]=0; break; } return(USBD_OK); /*USERCODEEND11*/ }
usbd_dfu_if.h 文件添加的宏定義
/*Defineflashaddress*///BLANK1#defineADDR_FLASH_SECTOR_00x08000000#defineADDR_FLASH_SECTOR_10x08004000#defineADDR_FLASH_SECTOR_20x08008000#defineADDR_FLASH_SECTOR_30x0800C000#defineADDR_FLASH_SECTOR_40x08010000#defineADDR_FLASH_SECTOR_50x08020000#defineADDR_FLASH_SECTOR_60x08040000#defineADDR_FLASH_SECTOR_70x08060000#defineADDR_FLASH_SECTOR_80x08080000#defineADDR_FLASH_SECTOR_90x080A0000#defineADDR_FLASH_SECTOR_100x080C0000#defineADDR_FLASH_SECTOR_110x080E0000//BLANK2#defineADDR_FLASH_SECTOR_120x08100000#defineADDR_FLASH_SECTOR_130x08104000#defineADDR_FLASH_SECTOR_140x08108000#defineADDR_FLASH_SECTOR_150x0810C000#defineADDR_FLASH_SECTOR_160x08110000#defineADDR_FLASH_SECTOR_170x08120000#defineADDR_FLASH_SECTOR_180x08140000#defineADDR_FLASH_SECTOR_190x08160000#defineADDR_FLASH_SECTOR_200x08180000#defineADDR_FLASH_SECTOR_210x081A0000#defineADDR_FLASH_SECTOR_220x081C0000#defineADDR_FLASH_SECTOR_230x081E0000/*Flashopratetimefromdatasheetpage128*/#defineFLASH_SECTOR_16KB_WRITE_ERASE_TIME500//500usbframe,means500ms#defineFLASH_SECTOR_64KB_WRITE_ERASE_TIME1100#defineFLASH_SECTOR_128KB_WRITE_ERASE_TIME2000
-
修改Main文件
首先在main文件前添加幾個(gè)用于加載APP程序的變量和函數(shù)定義
typedefvoid(*pFunction)(void); pFunctionJumpToApplication; uint32_tJumpAddress;1234
然后再 main 函數(shù)中加入 外部按鍵的判斷、APP程序加載以及USB DFU初始化功能
intmain(void) { /*Resetofallperipherals,InitializestheFlashinterfaceandtheSystick.*/ HAL_Init(); /*Configurethesystemclock*/ SystemClock_Config(); /*Initializeallconfiguredperipherals*/ MX_GPIO_Init(); if(HAL_GPIO_ReadPin(USER_BTN_GPIO_Port,USER_BTN_Pin)==GPIO_PIN_SET) { HAL_GPIO_WritePin(GPIOG,LD3_Pin,GPIO_PIN_SET);//Fordebug /*Testifusercodeisprogrammedstartingfromaddress0x0800C000*/ if(((*(__IOuint32_t*)USBD_DFU_APP_DEFAULT_ADD)&0x2FF80000)==0x20000000) { HAL_GPIO_WritePin(GPIOG,LD4_Pin,GPIO_PIN_SET);//Fordebug /*Jumptouserapplication*/ JumpAddress=*(__IOuint32_t*)(USBD_DFU_APP_DEFAULT_ADD+4); JumpToApplication=(pFunction)JumpAddress; /*Resetofallperipherals*/ HAL_DeInit(); /*Setinterruptvectortoappcode*/ SCB->VTOR=USBD_DFU_APP_DEFAULT_ADD; /*Initializeuserapplication'sStackPointer*/ __set_MSP(*(__IOuint32_t*)USBD_DFU_APP_DEFAULT_ADD); JumpToApplication(); } } MX_USB_DEVICE_Init(); while(1) { } }
編譯程序下載進(jìn)入單片機(jī)
使用DfuSe
從ST官網(wǎng)DfuSe的程序安裝包,并安裝。然后我們按下之前寫(xiě)的觸發(fā)按鍵并復(fù)位單片機(jī),讓單片機(jī)初始 USB DFU 功能,這時(shí)如果你插著單片機(jī)的USB線,系統(tǒng)應(yīng)該已經(jīng)識(shí)別了。如果沒(méi)有右鍵更新驅(qū)動(dòng)程序,手動(dòng)指定驅(qū)動(dòng)搜索路徑在DfuSe安裝目錄下的 BinDriver 內(nèi)。如果直接無(wú)法識(shí)別USB設(shè)備,建議在CubeMx配置完工程后就編譯下載測(cè)試一下,看看是不是你在移植過(guò)程中哪里寫(xiě)錯(cuò)了。
然后我們需要生成一個(gè)地址設(shè)定在0x0800_c000后的測(cè)試程序,就先編寫(xiě)一個(gè) Blink LED 的程序吧,生成bin、hex或S19文件。然后我們打開(kāi)DfuSe軟件的Dfu file manager來(lái)生成DFU軟件用的.dfu格式的文件。選擇第一項(xiàng),第二個(gè)是用來(lái)將.dfu反向變換回來(lái)的。大概的操作已經(jīng)標(biāo)在圖片上了,操作比較簡(jiǎn)單就不做詳細(xì)介紹了,記得把Address的地址改到偏移后的地址上否則下載會(huì)出錯(cuò),其他參數(shù)可以不用修改。
然后我們打開(kāi)DfuSe程序,在Upgrade中選擇生成好的blink.dfu文件,勾選校驗(yàn)功能,下載程序。成功后復(fù)位單片機(jī),LED開(kāi)始閃爍,移植成功。
更多
仔細(xì)區(qū)看看DfuSe的安裝目錄,里面有DFU的資料文檔,還有DFU的工程源代碼,可以用來(lái)改寫(xiě)自己需要的DFU升級(jí)程序。
審核編輯:郭婷
-
芯片
+關(guān)注
關(guān)注
454文章
50460瀏覽量
421971 -
usb
+關(guān)注
關(guān)注
60文章
7903瀏覽量
264047 -
STM32
+關(guān)注
關(guān)注
2266文章
10876瀏覽量
354926
原文標(biāo)題:STM32高級(jí)開(kāi)發(fā)——使用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)論