STM32Cubemx 生成USB DFU 固件升级程序
- 生成代码
如图,新建项目工程

首先,芯片型号为STM32F401CB,配置好USB时钟树,在USB_Device中选择Download Firmware Update Class(DFU)类,其主要设置如上图红色标记处,USBD_DFU_APP_DEFAULT_ADD为APP程序flash起始地址,这里我的程序才48k多一点,故使用最后的扇区64k,扇区具体参数请查阅相关datasheet。USBD_DFU_MEDIA Interface 属性是对芯片flash使用的描述,@Internal Flash /0x08000000/01016Ka,03016Kg,01*064Kg,0x08000000表示起始地址,我这里DFU的程序大小不到16K,故只使用了第一个扇区,a代表只读,g代表读写可擦除,具体参考软件介绍,这里的参数必须参照芯片型号进行填写。
程序栈可以设置大一点,这里为0x2000,Gnerate 生成程序代码。
- 修改关键代码
keil软件打开工程,找到 usbd_dfu_if.c文件依次修改如下:
uint16_t MEM_If_Init_FS(void)函数 增加如下代码
1 2 3 | HAL_FLASH_Unlock(); __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);//解锁flash,清楚标志位 |
uint16_t MEM_If_DeInit_FS(void) 函数 增加如下代码
1 | HAL_FLASH_Lock(); |
uint16_t MEM_If_Erase_FS(uint32_t Add)函数 增加如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | uint32_t UserStartSector; uint32_t SectorError; FLASH_EraseInitTypeDef pEraseInit; /* Unlock the Flash to enable the flash control register access *************/ MEM_If_Init_FS(); /* Get the sector where start the user flash area */ UserStartSector = GetSector(Add); pEraseInit.TypeErase = TYPEERASE_SECTORS; pEraseInit.Sector = UserStartSector; pEraseInit.NbSectors = 1;//我这里只使用了最后一个Sector,根据APP程序大小确定 pEraseInit.VoltageRange = VOLTAGE_RANGE_3; if (HAL_FLASHEx_Erase(&pEraseInit, &SectorError) != HAL_OK) {<!-- --> /* Error occurred while page erase */ return (1); } |
GetSector()可自行编写,就是根据flash地址确定扇区数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */ #define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */ #define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */ #define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */ #define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */ uint32_t GetSector(uint32_t Address) {<!-- --> uint32_t sector = 0; if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0)) {<!-- --> sector = FLASH_SECTOR_0; } else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1)) {<!-- --> sector = FLASH_SECTOR_1; } else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2)) {<!-- --> sector = FLASH_SECTOR_2; } else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3)) {<!-- --> sector = FLASH_SECTOR_3; } else {<!-- --> sector = FLASH_SECTOR_4; } return sector; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 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 2; } } else {<!-- --> /* Error occurred while writing data in Flash memory */ return 1; } } return (USBD_OK); /* USER CODE END 3 */ } 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 (uint8_t*)(dest); /* USER CODE END 4 */ } uint16_t MEM_If_GetStatus_FS(uint32_t Add, uint8_t Cmd, uint8_t *buffer) {<!-- --> /* USER CODE BEGIN 5 */ uint16_t FLASH_PROGRAM_TIME = 50; uint16_t FLASH_ERASE_TIME = 50; 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中程序如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | int main(void) {<!-- --> /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); //MX_USB_DEVICE_Init(); /* Initialize interrupts */ MX_NVIC_Init(); /* USER CODE BEGIN 2 */ if(HAL_GPIO_ReadPin( power_swich_sta_GPIO_Port , power_swich_sta_Pin ) == GPIO_PIN_SET)//根据按键进行APP跳转 {<!-- --> /* Test if user code is programmed starting from USBD_DFU_APP_DEFAULT_ADD address */ if(((*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD) & 0x2FFE0000 ) == 0x20000000) {<!-- --> /* Jump to user application */ JumpAddress = *(__IO uint32_t*) (USBD_DFU_APP_DEFAULT_ADD + 4);//USBD_DFU_APP_DEFAULT_ADD 为初始设置的APP地址 JumpToApplication = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t*) USBD_DFU_APP_DEFAULT_ADD); JumpToApplication(); } } MX_USB_DEVICE_Init(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) {<!-- --> /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_Delay(100); HAL_GPIO_TogglePin(BAT_Sta_GPIO_Port,BAT_Sta_Pin); } /* USER CODE END 3 */ } |
- APP程序下载地址修改
start地址为APP flash起始地址0x8010000 ,大小64k,编译程序,生成hex文件
如上图,将APP hex 文件转换成 Dfu文件
先将dfu程序下载,连接USB,打开上图软件,Choose选择生成好的DFU文件,Upgrade下载,Verify校验,leave DEU mode。
若软件提示 红色字体 bad firmwa ,请修改usbd_def.h 如上图
DFU软件下载百度网盘提取码:提取码:phs2,也可自行搜索 UM0412 进行下载
start地址为APP flash起始地址0x8010000 ,大小64k,编译程序,生成hex文件