先開門見上DFU是啥
DFU全稱為Download Firmware Update,是ST官方推出的一個(gè)通過USB接口進(jìn)行IAP升級(jí)的方案,同串口ISP一樣,他們都集成在了芯片內(nèi)部的Bootloader區(qū)段,可以通過配置boot引腳來(lái)啟動(dòng)。
當(dāng)用戶使用STM32F4 H7或者更高級(jí)MCU時(shí),自己編譯生成燒寫文件,或者通過其他途徑獲得燒寫文件,通常格式為hex,一般情況下用戶可以通過調(diào)試器例如JLink,ST-link等使用專用的軟件下載,或者配合BOOT設(shè)置使用串口進(jìn)行ISP下載,這時(shí)候則使用USB轉(zhuǎn)串口ttl設(shè)備,現(xiàn)在STM32F4 H7以上版本的可以通過USB口進(jìn)行下載,只需要一根USB線就OK. 這種技術(shù)叫作DFU模式燒寫
但是STM32內(nèi)置DFU的型號(hào)都比較新,像上面說的STM32F4系列是有的,但是像F0和F1系列則沒有,不過沒有關(guān)系,如果你用的型號(hào)沒有內(nèi)置DFU程序,也可以通過CubeMX來(lái)快速生成和移植一個(gè)DFU功能程序到你的Flash中來(lái)使用。
使用DFU的優(yōu)缺點(diǎn)?使用DFU的好處是不用自己制作Bootloader,因?yàn)檫@部分代碼在STM32出廠之前就已經(jīng)做好并且燒錄進(jìn)去了,而且不占用用戶代碼的Flash,另外,在PC端我們也不需要專門定制一個(gè)上位機(jī),因?yàn)楣俜骄陀袑iT的升級(jí)Tool以及USB驅(qū)動(dòng)。缺點(diǎn)是要改變boot引腳的電平,才能啟動(dòng)Bootloader,這樣的話在應(yīng)用場(chǎng)景上就有比較大的限制了。
F4系列就可以通過下面的方式來(lái)進(jìn)入DFU模式
下面是F103系列的話,上面操作就不行了,必須通過bootloader的方式的進(jìn)入dfu模式
今天我們看先USB如何燒錄,F(xiàn)103也可以的,通過cubemx方式來(lái)生成工程
首先打開STM32CubeMX,建立一個(gè)工程,選擇MCU型號(hào)為STM32F103C8。
如下圖對(duì)USB外設(shè)進(jìn)行設(shè)置。
MiddleWare設(shè)置:
Mode選為 Download Fireware Update Class(DFU)。USBD_DFU_APP_DEFAULT_ADD設(shè)置為0x08005800,這個(gè)地址即為升級(jí)時(shí)存入升級(jí)程序的起始地址,用FLASH中0x08000000~0x08005800 的0x5800/1024=22KB存儲(chǔ)空間存儲(chǔ)DFU程序。USB_DFU_MEDIA Interface設(shè)置為:@Internal Flash /0x08000000/22*001Ka,42*001Kg,其中22*001Ka表示flash的前22KB存儲(chǔ)空間為只讀(a)的,42*001Kg表示flash后42KB的存儲(chǔ)空間可寫(g),用于存儲(chǔ)升級(jí)下載的程序。這個(gè)需要按照MCU的flash的Flash module organization修改的,stm32f103c8為中等容量系列mcu,flash為64KB,下圖為中等容量MCU的flash存儲(chǔ)分布,根據(jù)這個(gè)圖可知stm32f103c8的64KB分為64頁(yè),每頁(yè)1KB,所以可以按照這個(gè)按需設(shè)置@Internal Flash的值,注意與USBD_DFU_APP_DEFAULT_ADD對(duì)應(yīng)。
為了能使STM32F103C8進(jìn)入DFU模式,選擇一個(gè)按鍵作為進(jìn)入DFU模式的開關(guān),此處我選擇PA5(COL5)和PA15(ROW3)這連個(gè)IO進(jìn)行設(shè)置,然后通過程序使得當(dāng)按下某個(gè)鍵,再插上USB后進(jìn)入DFU模式。
然后我們生成代碼,修改usbd_dfu_if.c文件:
//Flash初始化,即解鎖FLash
uint16_t MEM_If_Init_FS(void)
{
/* USER CODE BEGIN 0 */
HAL_FLASH_Unlock();
return (USBD_OK);
/* USER CODE END 0 */
}
//Flash去初始化,即鎖FLash
uint16_t MEM_If_DeInit_FS(void)
{
/* USER CODE BEGIN 1 */
HAL_FLASH_Lock();
return (USBD_OK);
/* USER CODE END 1 */
}
//擦除一個(gè)page
uint16_t MEM_If_Erase_FS(uint32_t Add)
{
/* USER CODE BEGIN 2 */
uint32_t PageError;
/* Variable contains Flash operation status */
HAL_StatusTypeDef status;
FLASH_EraseInitTypeDef eraseinitstruct;
eraseinitstruct.TypeErase = FLASH_TYPEERASE_PAGES;
eraseinitstruct.PageAddress = Add;
eraseinitstruct.NbPages = 1U;
status = HAL_FLASHEx_Erase(&eraseinitstruct, &PageError);
if (status != HAL_OK)
{
return (USBD_FAIL);
}
return (USBD_OK);
/* USER CODE END 2 */
}
//flash寫入數(shù)據(jù)
uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
{
/* USER CODE BEGIN 3 */
uint32_t i = 0;
for (i = 0; i < Len; i += 4)
{
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
* be done by byte */
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (uint32_t) (dest + i),
*(uint32_t *) (src + i)) == HAL_OK)
{
/* Check the written value */
if (*(uint32_t *) (src + i) != *(uint32_t *) (dest + i))
{
/* Flash content doesn't match SRAM content */
return (USBD_FAIL);
}
}
else
{
/* Error occurred while writing data in Flash memory */
return (USBD_FAIL);
}
}
return (USBD_OK);
/* USER CODE END 3 */
}
//從Flash讀取數(shù)據(jù)
uint8_t *MEM_If_Read_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
{
/* Return a valid address to avoid HardFault */
/* USER CODE BEGIN 4 */
uint32_t i = 0;
uint8_t *psrc = src;
for (i = 0; i < Len; i++)
{
dest[i] = *psrc++;
}
/* Return a valid address to avoid HardFault */
return (uint8_t *) (dest);
/* USER CODE END 4 */
}
//獲取FLash狀態(tài)
uint16_t MEM_If_GetStatus_FS(uint32_t Add, uint8_t Cmd, uint8_t *buffer)
{
/* USER CODE BEGIN 5 */
switch (Cmd)
{
case DFU_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:
default:
buffer[1] = (uint8_t) FLASH_ERASE_TIME;
buffer[2] = (uint8_t) (FLASH_ERASE_TIME << 8);
buffer[3] = 0;
break;
}
return (USBD_OK);
/* USER CODE END 5 */
}
修改main.c
int main(void)
{
pFunction JumpToApplication;
uint32_t JumpAddress;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
//BOOTKEY_OUTPUT_PIN_Pin輸出高
HAL_GPIO_WritePin(BOOTKEY_OUTPUT_PIN_GPIO_Port, BOOTKEY_OUTPUT_PIN_Pin, GPIO_PIN_SET);
HAL_Delay(200);
//如果Enter按鍵被按下,BOOTKEY_INPUT_PIN_Pin讀到值為GPIO_PIN_SET,則跳過下面if代碼段,MCU進(jìn)入DFU模式,
//否則讀到值為GPIO_PIN_RESET,則執(zhí)行if代碼段,跳轉(zhuǎn)到0x08005800處執(zhí)行應(yīng)用程序代碼。
if (HAL_GPIO_ReadPin(BOOTKEY_INPUT_PIN_GPIO_Port, BOOTKEY_INPUT_PIN_Pin) == GPIO_PIN_RESET)
{
/* Test if user code is programmed starting from address 0x08007000 */
if (((*(__IO uint32_t *) USBD_DFU_APP_DEFAULT_ADD) & 0x2FFFB000) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t *) (USBD_DFU_APP_DEFAULT_ADD + 4);
JumpToApplication = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t *) USBD_DFU_APP_DEFAULT_ADD);
__disable_irq(); //此處官方代碼未放置關(guān)閉中斷,在跳轉(zhuǎn)app的時(shí)候會(huì)出問題?。。。。。? JumpToApplication();
}
}
MX_USB_DEVICE_Init();
while (1)
{
}
}
燒錄程序,然后按住某個(gè)按鍵,USB查到電腦上,安裝驅(qū)動(dòng),后設(shè)備管理器會(huì)出現(xiàn)一個(gè)dfu設(shè)備,然后通過dfu-util或者stm32cubeprogramer就能通過usb升級(jí)程序了。
連接成功
注意APP的程序地址需要改動(dòng)一下
這幾天的問題簡(jiǎn)單總結(jié)一下
安裝使用了 STM32CubeProgrammer
之后,再使用DfuSeDemo
,發(fā)現(xiàn)DfuSeDemo
不能識(shí)別出設(shè)備( STM Device in DFU Mode
) 。
打開我的電腦-> 設(shè)備管理器,發(fā)現(xiàn)在通用串行總線設(shè)備下面出現(xiàn): STM32 Download Firmware Update
這個(gè)設(shè)備。
右鍵點(diǎn)擊 STM32 Download Firmware Update
, 選擇卸載設(shè)備,并刪除設(shè)備驅(qū)動(dòng)程序。
將usb線拔出設(shè)備,然后再插入設(shè)備,DfuSeDemo
重新能識(shí)別出設(shè)備。