STM32-DFU

STM32 DFU Demo

前言

基于STM32F072 的DFU历程,适用于带USB接口的MCU,方便易用。本文主要介绍BOOT&APP的制作过程

感谢

实验环境

硬件准备

  • STM32F072B - DISCO
  • Mini USB数据线两根

软件准备

  • Keil 5.25
  • STM32Cube F0 V1.11.0
  • STM32 CubeMX 5.1
  • DfuSeDemo V3.0.6

BOOT_Project

Cubemx配置

  • 配置USB为Device模式

  • 配置USB Device为DFU模式,并且修改DFU模式下APP的模式起始地址

  • 配置指示LED、进入APP按键、SWD下载口

  • 配置时钟树(USB要求时钟最小48Mhz,因此配置为48Mhz)

  • 配置工程相关设置、生成代码

移植代码

  • 生成代码的目录如下

    • USB 库文件
    • USB 配置文件
    • USB上层文件配置
  • 修改USB上层文件usb_dfu_if.c

1
2
3
4
5
6
static uint16_t MEM_If_Init_FS(void);//flash 初始化
static uint16_t MEM_If_Erase_FS(uint32_t Add);//Flash擦除
static uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len);//flash写
static uint8_t *MEM_If_Read_FS(uint8_t *src, uint8_t *dest, uint32_t Len);//flash读
static uint16_t MEM_If_DeInit_FS(void);//flash反初始化
static uint16_t MEM_If_GetStatus_FS(uint32_t Add, uint8_t Cmd, uint8_t *buffer);//flash状态
1
2
3
4
5
6
7
uint16_t MEM_If_Init_FS(void)
{
/* USER CODE BEGIN 0 */
HAL_FLASH_Unlock();
return (USBD_OK);
/* USER CODE END 0 */
}
1
2
3
4
5
6
7
8
9
10
11
/**
* @brief De-Initializes Memory
* @retval USBD_OK if operation is successful, MAL_FAIL else
*/
uint16_t MEM_If_DeInit_FS(void)
{
/* USER CODE BEGIN 1 */
HAL_FLASH_Lock();
return (USBD_OK);
/* USER CODE END 1 */
}
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
/**
* @brief Erase sector.
* @param Add: Address of sector to be erased.
* @retval 0 if operation is successful, MAL_FAIL else.
*/
uint16_t MEM_If_Erase_FS(uint32_t Add)
{
/* USER CODE BEGIN 2 */
uint32_t PageError = 0;
FLASH_EraseInitTypeDef EraseInitStruct;

EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = USBD_DFU_APP_DEFAULT_ADD;
EraseInitStruct.NbPages = (USBD_DFU_APP_END_ADD - USBD_DFU_APP_DEFAULT_ADD)/USBD_DFU_APP_PAGE_SIZE;
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK)
{
/*
Error occurred while page erase.
User can add here some code to deal with this error.
PageError will contain the faulty page and then to know the code error on this page,
user can call function 'HAL_FLASH_GetError()'
*/
return (USBD_FAIL);
}
return (USBD_OK);
/* USER CODE END 2 */
}
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

/**
* @brief Memory write routine.
* @param src: Pointer to the source buffer. Address to be written to.
* @param dest: Pointer to the destination buffer.
* @param Len: Number of data to be written (in bytes).
* @retval USBD_OK if operation is successful, MAL_FAIL else.
*/
uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
{
/* USER CODE BEGIN 3 */
for(uint32_t i= 0;i < Len;i++)
{
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (uint32_t)(dest+i), *(uint32_t*)(src+i)) == HAL_OK)
{
if(*(uint32_t *)(src + i)!= *(uint32_t *)(dest + i))
{
return USBD_FAIL;
}
}
{
return USBD_FAIL;
}
}
return (USBD_OK);
/* USER CODE END 3 */
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @brief Memory read routine.
* @param src: Pointer to the source buffer. Address to be written to.
* @param dest: Pointer to the destination buffer.
* @param Len: Number of data to be read (in bytes).
* @retval Pointer to the physical address where data should be read.
*/
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 */
uint8_t *psrc = src;

for(uint32_t i =0;i <Len;i++)
{
dest[i] = *psrc++;
}
return (uint8_t*)(dest);
/* USER CODE END 4 */
}
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
/**
* @brief Get status routine
* @param Add: Address to be read from
* @param Cmd: Number of data to be read (in bytes)
* @param buffer: used for returning the time necessary for a program or an erase operation
* @retval USBD_OK if operation is successful
*/
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:
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 */
}
  • 增加对应宏定义 usbd_conf.h
1
2
3
4
5
6
7
8
9
#define USBD_DFU_APP_DEFAULT_ADD     0x08007000	//app起始地址
/*---------- -----------*/
#define USBD_DFU_APP_END_ADD 0x0801F800//F072 Flash终止地址,根据单片机修改
/*---------- -----------*/
#define USBD_DFU_APP_PAGE_SIZE 0x0800U//F072 Flash一个页的大小
/*---------- -----------*/
#define FLASH_PROGRAM_TIME (uint16_t)50// Flash编程时间
/*---------- -----------*/
#define FLASH_ERASE_TIME (uint16_t)50//Flash擦除时间
  • 在主函数中增加相关逻辑控制
1
2
3
4
/* USER CODE BEGIN PFP */
typedef void (*pFunction)(void); /*!< 跳转指针函数*/
void Bootloader_JumpToApplication(void); /*!< 跳转到APP函数*/
/* USER CODE END PFP */
1
2
3
4
5
6
7
 //MX_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)!= GPIO_PIN_SET)/*!< 判断是否要跳转到APP*/
{
Bootloader_JumpToApplication();
}
MX_USB_DEVICE_Init();/*!< USB初始化函数*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Bootloader_JumpToApplication(void)
{
uint32_t JumpAddress = *(__IO uint32_t*)(USBD_DFU_APP_DEFAULT_ADD + 4);
pFunction Jump = (pFunction)JumpAddress;

HAL_RCC_DeInit();/*!< 跳转前反初始化时钟*/
HAL_DeInit();/*!< 跳转前反初始化所有配置*/
/*!< 跳转前关闭滴答定时器并且清零计数器*/
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;

#if (SET_VECTOR_TABLE)
SCB->VTOR = APP_ADDRESS;/*!<设置跳转地址*/
#endif

__set_MSP(*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD);
Jump();/*!< 判断指令*/
}

APP_Project

  • 待续

DFU 上位机测试

  • 待续