stm32单片机的USB烧录程序

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

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 2
收藏 3
关注 194
成为作者 赚取收益
全部留言
0/200
  • dy-3rTDlD7J 02-05 11:13
    你好哥 太阳能智能充电管理系统那个具体的原理图有点模糊 想要一张清晰的可以吗哥
    回复