开启生长之旅!这是我参与「日新计划 2 月更文挑战」的第 23 天,点击检查活动详情

一、USB简介

USB(Universal Serial BUS)通用串行总线,是一个外部总线规范,用于规范电脑与外部设备的衔接和通讯。是应用在 PC 领域的接口技能。USB 接口支撑设备的即插即用和热插拔功用。USB 是在 1994 年末由英特尔、康柏、IBM、Microsoft 等多家公司联合提出的。

USB 开展到现在现已有 USB1.0/1.1/2.0/3.0 等多个版别。现在用的最多的就是 USB1.1 和 USB2.0,USB3.0 现在现已开端普及。STM32F103 自带的 USB 契合 USB2.0 规范,不过 STM32F103 的 USB 都只能用来做设备,而不能用作主机。

规范 USB 共四根线组成,除 VCC/GND 外,别的为 D+,D-; 这两根数据线选用的是差分电压的方法进行数据传输的。在 USB 主机上,D-和 D+都是接了 15K 的电阻到低的,所以在没有设备接入的时分,D+、D-均是低电平。而在 USB 设备中,假如是高速设备,则会在 D+上接一个 1.5K 的电阻到 VCC,而假如是低速设备,则会在 D-上接一个 1.5K 的电阻到 VCC。这样当设备接入主机的时分,主机就能够判别是否有设备接入,并能判别设备是高速设备仍是低速设备。

STM32F103 的 MCU 自带 USB 从操控器,契合 USB 规范的通讯衔接;PC 主机和微操控器之间的数据传输是经过共享一专用的数据缓冲区来完结的,该数据缓冲区能被 USB 外设直接拜访。这块专用数据缓冲区的巨细由所运用的端点数目和每个端点最大的数据分组巨细所决议,每个端点最大可运用 512 字节缓冲区(专用的 512 字节,和 CAN 共用),最多可用于 16 个单向或 8 个双向端点。USB 模块同 PC 主机通讯,依据 USB 规范完结令牌分组的检测,数据发送/接纳的处理,和握手分组的处理。整个传输的格式由硬件完结,其间包含 CRC 的生成和校验。

1.1 USB DFU简介

DFU全称为Download Firmware Update,是ST官方推出的一个经过USB接口进行IAP晋级的方案,同串口ISP相同,他们都集成在了芯片内部的Bootloader区段,能够经过装备boot引脚来发动。(具体可参照ST文档:AN2606)。不过内置DFU的芯片大部分类型都比较新,假如你用的类型没有内置DFU程序,不要紧咱们也能够经过CubeMX来快速生成和移植一个DFU功用程序到你的Flash中来运用。

由于DFU是USB差分传输,相对而言速率和通讯稳定性上相当于UART更加速与稳定,但是由于设备端不知道待接纳文件的巨细,何时传输完毕,只为传输而传输,相比较带有完好文件传输协议而言(Ymodem、Xmodem等)这个差太多,不过CDC能够替代处理这个问题。

二、新建工程

1. 翻开 STM32CubeMX 软件,点击“新建工程”

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

2. 挑选 MCU 和封装

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

3. 装备时钟 RCC 设置,挑选 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
挑选 Clock Configuration,装备体系时钟 SYSCLK 为 72MHz 修正 HCLK 的值为 72 后,输入回车,软件会主动修正一切装备
STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

4. 装备调试形式 非常重要的一步,不然会造成第一次烧录程序后续无法辨认调试器 SYS 设置,挑选 Debug 为 Serial Wire

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

三、USB

3.1 参数装备

Connectivity 中挑选 USB 设置,并勾选 Device(FS) 激活 USB 设备。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

Parameter Settings 进行具体参数装备。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • Speed: Full Speed 12MBit/s(固定为全速)
  • Low Power: 默认 Disabled(在任何不需求运用usb模块的时分,经过写操控寄存器总能够使usb模块置于低功耗形式(low power mode ,suspend形式)。在这种形式下,不发生任何静态电流耗费,一起usb时钟也会减慢或停止。经过对usb线上数据传输的检测,能够在低功耗形式下唤醒usb模块。也能够将一特定的中止输入源直接衔接到唤醒引脚上,以使体系能当即康复正常的时钟体系,并支撑直接发动或停止时钟体系。)

3.2 引脚装备

USB 的 DP 引脚必须上拉 1.5K 欧的电阻,电脑才能检测到 USB,不然检测不到。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
检查野火攻略者开发板原理图可知,需求将 PD6 装备为低电平使能 USB。
STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

在右边图中找到 PD6 引脚,挑选 GPIO_Output

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

GPIO output level 中挑选 Low 输出低电平。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

3.3 装备时钟

挑选 Clock Configuration,USB 时钟装备为 48MHz,且来源最好是外部晶振分频得到。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

3.4 USB Device

USB有主机(Host)和设备(Device)之分。一般电脑的USB接口为主机接口,而键盘、鼠标、U盘等则为设备。

部分类型的STM32芯片有1~2个USB接口。像STM32F103系列的有一个USB Device接口,STM32F407系列的有2个USB接口,既能够作为HOST,又能够作为Device,还能够作为OTG接口。

Middleware 中挑选 USB_DEVICE 设置,在 Class For FS IP 设备类别挑选 Download Firmware Update Class(DFU) 固件晋级类。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
修正参数装备。
STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • USBD_DFU_XFER_SIZE(每次传输的最大字节数): 1024 Bytes
  • USBD_DFU_APP_DEFAULT_ADD (Base Address 0x)(晋级时存入Application程序的开端地址):0x08005800

依据下面 六、编写Bootloader程序 编译后 .map 文件巨细,能够了解程序存储到了哪些区域。来设置Application程序开端地址,避开Bootloader程序地址范围。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

翻开 map 文件后,检查文件最后部分的区域,能够看到一段以 “Memory Map of the image” 开头的记载(若找不到可用查找功用定位)

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
Bootloader程序的基地址是 0x08000000,巨细为 0x00004a84,可知它占用的最高的地址空间为 0x08004a84。0x08000000~0x08005800 的 0x5800/1024=22KB 存储空间存储DFU程序。USBD_DFU_APP_DEFAULT_ADD 设置的开端地址就要避开这个范围。

  • USBD_DFU_MEDIA Interface(对芯片Flash运用的描绘): @Internal Flash /0x08000000/11*002Ka,245*002Kg【这个参数是DfuSeDemo这个DFU晋级软件需求辨认的参数】
    • 0x08000000,表明开端地址
    • “a”代表的是Read-only,表明所指明的区域应该为Bootloader程序的空间不行擦除或许修正
    • “g”代表Read/Write/Erase,表明所指明的区域应该为Application程序的空间,巨细由前面的数字决议
    • “*”前面的为Sector的个数,后边的为Sector的巨细,这儿的意思就是从0x08000000开端,前面11个Sector(每个Sector为2k字节)为Read-only,后边245个Sector(每个Sector为2k字节)为Read/Write/Erase等等。

留意:由于芯片不同,芯片的内部 Flash 散布情况也不一定相同,故请参考运用的芯片参考手册。

STM32F103VET6 类型芯片的参数,即 STM32F1 系列大容量产品。Flash 分为 256 页,每页巨细为 2KB,共 512KB。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

设备描绘符坚持默认。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

四、增加按键

4.1 GPIO装备

System Core 中挑选 GPIO 设置。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

在右边图中找到按键对应引脚,挑选 GPIO_Input

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

五、生成代码

输入项目名和项目路径

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
挑选应用的 IDE 开发环境 MDK-ARM V5
STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
每个外设生成独立的 ’.c/.h’ 文件 不勾:一切初始化代码都生成在 main.c 勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。
STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
点击 GENERATE CODE 生成代码
STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

六、编写Bootloader程序

6.1 修正usbd_dfu_if.c

翻开工程文件夹Application/User/USB_DEVICE/Appusbd_dfu_if.c文件

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

6.1.1 修正内部Flash操作相关函数

  • MEM_If_Init_FS Flash初始化,将Flash解锁,并将一切的标志位清零,以便后续的写入动作
/**
  * @brief  Memory initialization routine.
  * @retval USBD_OK if operation is successful, MAL_FAIL else.
  */
uint16_t MEM_If_Init_FS(void)
{
  /* USER CODE BEGIN 0 */
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR);
  return (USBD_OK);
  /* USER CODE END 0 */
}
  • MEM_If_DeInit_FS Flash撤销初始化,将Flash从头上锁,禁止对Flash的操作
/**
  * @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 */
}
  • MEM_If_Erase_FS Flash擦除
/**
  * @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;
    /* 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 */
}
  • MEM_If_Write_FS Flash写入,将USB接纳到的Flash数据写入到Flash中
/**
  * @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 */
    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 */
}
  • MEM_If_Read_FS Flash读取,读取指定地址的数据到目标数组中,并返回数组地址
/**
  * @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 */
    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 */
}
  • MEM_If_GetStatus_FS 获取Flash状况
/**
  * @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 */
    //擦除及写入时刻应该按文档改写
    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 */
}

6.2 修正main.c

增加APP程序进口函数指针。

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef void (*pFunction)(void);
/* USER CODE END PTD */

增加用于加载APP程序的变量、外部按键的判别、APP程序加载以及USB DFU初始化功用。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  pFunction JumpToApplication;
  uint32_t JumpAddress;
  /* 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();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("\r\n****** USB-DFU Example ******\r\n\r\n");
  //读取PA0引脚电平决议是否进入APP以及判别APP程序进口地址是否存在或正确
  //按下按键进入DFU,不按按键进入Application程序
  if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)
  {
    printf("Enter Application\r\n");
    /* 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);
      JumpToApplication = (pFunction)JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t *) USBD_DFU_APP_DEFAULT_ADD);
      __disable_irq(); //此处官方代码未放置封闭中止,在跳转app的时分会出问题!!!!!!
      JumpToApplication();
    }
  }
  printf("Key Down Enter DFU\r\n");
  MX_USB_DEVICE_Init();
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

将程序编译烧录到开发板中

七、编写Application程序

7.1 修正main.c

屏蔽掉多余代码,替换打印信息

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

7.2 修正程序下载地址

修正成跟STM32CubeMX中USBD_DFU_APP_DEFAULT_ADD设置的相同的地址

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

将程序编译烧录到开发板中

八、检查打印

  • 没有按下按键,经过Bootloader程序跳转到Application程序

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 按下KEY1按键,并按RESET按键重启,在Bootloader程序中进入DFU形式

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

九、下载和运用DFU晋级东西

9.1 DfuSe USB设备固件晋级软件

官网下载:www.st.com/content/st_…

百度网盘:pan.baidu.com/s/1xViNHESc… 提取码:0ypj

  • 装置驱动 找到装置DfuSe目录下的Bin\Driver对应的Windows版别驱动。

    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 编译待晋级的Application程序,生成.hex文件 这儿修正了打印内容,表明晋级后的程序。

    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
    找到生成的.hex文件。
    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 翻开Dfu file manager,将.hex文件转成.dfu文件 找到装置DfuSe目录下的Dfu file manager。

    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

这儿的Target ID有不同的值,其间0代表片内Flash;1代表外部Flash;2代表外部Nor Flash。因此咱们这儿挑选0,点击S19 or Hex挑选hex文件即可生成DFU文件。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 按下KEY1按键,进入DFU形式

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

留意: 假如设备带有感叹号,则参考下面十一、留意事项

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 翻开DfuSeDemo,更新固件 找到装置DfuSe目录下的DfuSeDemo。
    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

点击Choose加载之前转换的.dfu文件;勾选校验功用。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

点击Update完结擦除与下载;别的,能够经过点击Verify验证是否下载成功。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 重启设备,检查打印

Application程序晋级成为Application2程序

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

9.2 STM32CubeProgrammer

官网下载:www.st.com/zh/developm…

百度网盘:pan.baidu.com/s/133ahMS2G… 提取码:hece

  • 编译待晋级的Application程序,生成.hex文件 这儿修正了打印内容,表明晋级后的程序。

    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
    找到生成的.hex文件。
    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 翻开STM32CubeProgrammer,切换到Erasing&Programming擦除和烧录界面,勾选烧录验证和烧录后重启选项

    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 按下KEY1按键,进入DFU形式

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

留意: 假如设备带有感叹号,则参考下面十一、留意事项

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 挑选USB,改写端口,点击Connect进行衔接

    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
    点击Browse加载之前新编译的.hex文件;开启烧录。
    STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

  • 重启设备,检查打印

Application程序晋级成为Application2程序

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

十、工程代码

链接:pan.baidu.com/s/1FKdwTyTm… 提取码:t2an

十一、留意事项

用户代码要加在 USER CODE BEGIN NUSER CODE END N 之间,不然下次运用 STM32CubeMX 从头生成代码后,会被删除。

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)

假如USB端口呈现感叹号设备无法发动的问题,可适当将堆改大,如0x400

STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)
STM32CubeMX学习笔记(50)——USB接口使用(DFU固件升级)


• 由 Leung 写于 2022 年 12 月 30 日

• 参考:利用STM32CubeMX软件生成USB_DEVICE_DFU晋级程序

    STM32 USB DFU功用

    STM32F103 DFU功用完结(MXcube)(一)

    STM32 HAL学习(七)USB DFU晋级BootLoader