来源于: 自制17键数字机械键盘(3)——STM32F103 DFU功能实现 - 知乎
STM32F103系列默认没有内置USB DFU功能,不能通过进入DFU模式,通过USB 对程序进行更新,虽然在调试阶段用比如jlink、STlink等仿真器进行程序的烧录更新,但是总是不太方便,毕竟不是人人都有仿真器,不是每个人都是开发者,抱着做产品的态度,还是要方便用户升级,所以有必要实现STM32F103的DFU功能,方便键盘的后续更新升级。这章我用STM32CubeMX实现DFU功能。
1. STM32CubeMX工程建立
首先打开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进行设置,COL5、ROW3对应键盘的Enter键,对两个IO进行如下设置,然后通过程序使得当按下Enter键,再插上USB后进入DFU模式。
时钟树如下设置:
工程设置如下,堆和栈的空间选大一些,否则可能进不去DFU模式。然后生成项目即可。
2. 项目程序修改
修改usbd_dfu_if.c文件:
//擦除及写入时间应该按文档改写
#define FLASH_ERASE_TIME (uint16_t)50
#define FLASH_PROGRAM_TIME (uint16_t)50
//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
//添加函数指针定义
typedef void (*pFunction)(void);
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)
{
}
}
烧录程序,然后按住Enter按键,USB查到电脑上,安装驱动,后设备管理器会出现一个dfu设备,然后通过dfu-util或者stm32cubeprogramer就能通过usb升级程序了。
代码与转载代码有所不同,直接搬运是不能用的,需要关闭中断。你坑我呀我坑你!
纯属搬运,如有侵权,必定删除