先開門見上DFU是啥
DFU全稱為Download Firmware Update,是ST官方推出的一個通過USB接口進行IAP升級的方案,同串口ISP一樣,他們都集成在了芯片內部的Bootloader區段,可以通過配置boot引腳來啟動。
當用戶使用STM32F4 H7或者更高級MCU時,自己編譯生成燒寫文件,或者通過其他途徑獲得燒寫文件,通常格式為hex,一般情況下用戶可以通過調試器例如JLink,ST-link等使用專用的軟件下載,或者配合BOOT設置使用串口進行ISP下載,這時候則使用USB轉串口ttl設備,現在STM32F4 H7以上版本的可以通過USB口進行下載,只需要一根USB線就OK. 這種技術叫作DFU模式燒寫
但是STM32內置DFU的型號都比較新,像上面說的STM32F4系列是有的,但是像F0和F1系列則沒有,不過沒有關系,如果你用的型號沒有內置DFU程序,也可以通過CubeMX來快速生成和移植一個DFU功能程序到你的Flash中來使用。
使用DFU的優缺點?使用DFU的好處是不用自己制作Bootloader,因為這部分代碼在STM32出廠之前就已經做好并且燒錄進去了,而且不占用用戶代碼的Flash,另外,在PC端我們也不需要專門定制一個上位機,因為官方就有專門的升級Tool以及USB驅動。缺點是要改變boot引腳的電平,才能啟動Bootloader,這樣的話在應用場景上就有比較大的限制了。
F4系列就可以通過下面的方式來進入DFU模式
下面是F103系列的話,上面操作就不行了,必須通過bootloader的方式的進入dfu模式
今天我們看先USB如何燒錄,F103也可以的,通過cubemx方式來生成工程
首先打開STM32CubeMX,建立一個工程,選擇MCU型號為STM32F103C8。
如下圖對USB外設進行設置。
MiddleWare設置:
Mode選為 Download Fireware Update Class(DFU)。USBD_DFU_APP_DEFAULT_ADD設置為0x08005800,這個地址即為升級時存入升級程序的起始地址,用FLASH中0x08000000~0x08005800 的0x5800/1024=22KB存儲空間存儲DFU程序。USB_DFU_MEDIA Interface設置為:@Internal Flash /0x08000000/22*001Ka,42*001Kg,其中22*001Ka表示flash的前22KB存儲空間為只讀(a)的,42*001Kg表示flash后42KB的存儲空間可寫(g),用于存儲升級下載的程序。這個需要按照MCU的flash的Flash module organization修改的,stm32f103c8為中等容量系列mcu,flash為64KB,下圖為中等容量MCU的flash存儲分布,根據這個圖可知stm32f103c8的64KB分為64頁,每頁1KB,所以可以按照這個按需設置@Internal Flash的值,注意與USBD_DFU_APP_DEFAULT_ADD對應。
為了能使STM32F103C8進入DFU模式,選擇一個按鍵作為進入DFU模式的開關,此處我選擇PA5(COL5)和PA15(ROW3)這連個IO進行設置,然后通過程序使得當按下某個鍵,再插上USB后進入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 */
}
//擦除一個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寫入數據
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讀取數據
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狀態
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進入DFU模式,
//否則讀到值為GPIO_PIN_RESET,則執行if代碼段,跳轉到0x08005800處執行應用程序代碼。
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(); //此處官方代碼未放置關閉中斷,在跳轉app的時候會出問題!!!!!!
JumpToApplication();
}
}
MX_USB_DEVICE_Init();
while (1)
{
}
}
燒錄程序,然后按住某個按鍵,USB查到電腦上,安裝驅動,后設備管理器會出現一個dfu設備,然后通過dfu-util或者stm32cubeprogramer就能通過usb升級程序了。
連接成功
注意APP的程序地址需要改動一下
這幾天的問題簡單總結一下
安裝使用了 STM32CubeProgrammer
之后,再使用DfuSeDemo
,發現DfuSeDemo
不能識別出設備( STM Device in DFU Mode
) 。
打開我的電腦-> 設備管理器,發現在通用串行總線設備下面出現: STM32 Download Firmware Update
這個設備。
右鍵點擊 STM32 Download Firmware Update
, 選擇卸載設備,并刪除設備驅動程序。
將usb線拔出設備,然后再插入設備,DfuSeDemo
重新能識別出設備。