STM32F103 DFU功能实现(MXcube)(一)

来源于: 自制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升级程序了。

代码与转载代码有所不同,直接搬运是不能用的,需要关闭中断。你坑我呀我坑你!

纯属搬运,如有侵权,必定删除

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值