先开门见上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
重新能识别出设备。