0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何使用CubeMx生成一个DFU工程

技术让梦想更伟大 来源:CSDN技术社区 作者:flydream0 2022-10-26 09:31 次阅读

1 前言

DFU用来做IAP是很方便的,可以直接通过USB来对APP进行升级,因此,掌握DFU的制作还是挺有好处,特别是使用CubeMx工具可以快速制作,本文将基于STM3240G-EVL评估板来一步一步实现一个DFU的IAP工程。

2 制作CubeMx工程

新建一个STM32F407IGHx工程:Pinout:

Peripherals:RCC->High Speed Clock(HSE):Crystal/Ceramic ResonatorSYS->Debug:Serial WireUSB_OTG_FS->Mode:Device_Only

MiddleWaresUSB_DEVICE->Class For FS IP:Download Firmware Update Class(DFU)

再配置PG15脚为GPIO_Input模式。

Clock Configuration:

17fd57a4-5474-11ed-a3b6-dac502259ad0.png

图1 时钟树设置

如上图,STM3240G-EVAL评估板使用的是25M HSE。Configuration:NVIC中将USB中断优先级调为5,PG15的标签设置为USER_BTN,此外还需要设置中间件USB DFU参数,如下图:

183e14d8-5474-11ed-a3b6-dac502259ad0.png

图2 USB DFU参数设置

如上图,红色框内为需要修改的代码,0x0800C000为需要为用户程序APP烧录的起始地址,字符串“@Internal Flash /0x08000000/03016Ka,01016Kg,01064Kg,07128Kg”实际为USB DFU类的interface字符串描述符,在USB DFU标准文件中有提到可选接口可以使用一个对应的接口字符串来表示此可选接口对应的目标设备的存储块信息,但如何具体规定的,DFU标准(DFU_1.1)并没有要求,是开放的,如下:

1860a408-5474-11ed-a3b6-dac502259ad0.png

图3 DFU标准对接口字符串定义的描述

由此可见,接口字符串定义是可以自由定义的,那么在这里,由于使用到ST工具软件DfuSe Demo(v3.0.5),那么这个工具与USB DFU设备就有一个自定义的接口字符串定义,用来表示当前MCU内部的FLASH组织结构。

接下来我们来看看MCU内部FLASH的组织,由于这里的MCU是STM32F407IGHx,找到其参考文档,并查看其内部FLASH组织结构:

188778da-5474-11ed-a3b6-dac502259ad0.png

图4 STM32F407内部FLASH的组织结构

如上图,STM32F407内部FLASH包含4个16K扇区+1个64K扇区+7个128K扇区,并且起始地址为0x0800 0000,所以它对应的接口字符串表示为: “@Internal Flash /0x08000000/03016Ka,01016Kg,01064Kg,07128Kg”, Internal Flash为在工具软件显示的名称,0x08000000为起始地址,03016Ka表示3个16K大小只读的扇区,01064Kg表示1个64K大小的可读写扇区,07*128Kg表示7个128K大小的可读写扇区,后缀a表示只读,后缀g表示可读写。这个就是工具软件DfuSe Demo(v3.0.5)与DFU设备之间的约定。如下:

18c09070-5474-11ed-a3b6-dac502259ad0.png

图5 DfuSeDemo软件中所显示的内部FLASH的可读写属性

知道了这些信息后,我们再回过头来看APP的起始地址0x0800C000,那么APP的起始地址该如何得来的?有什么要求?与这个接口字符串之间是否有关系?

到目前为止,我们可以确定地是,APP_DEFAULT_ADD的地址必须是位于接口字符串表示的可读写的地址范围内,也就是第4个扇区起(前3个扇区都是只读的),不然是烧录不进去的。其他问题我们先暂且放一放,后续我们回过头来会回答这个问题。

Project Setting :堆设置为0x500,栈大小设置为0x2000。

18deb550-5474-11ed-a3b6-dac502259ad0.png

图6 堆栈设置

另外,在高级设置中,设置先不调用对USB DFU的初始化:

19047dc6-5474-11ed-a3b6-dac502259ad0.png

图7 高级设置

最后生成代码。

3 代码完善

对生成后的代码是可以直接编译通过的,我们这里使用的是IAR,当然你也可以使用MDK,由于不同编译器编译的最终文件大小有所差异,而APP的偏移地址在一定程度上也是有考虑到这个DFU本身代码大小的,接下来我们都将以IAR为例。

打开usbd_duf_if.c文件,这个文件就是USB DFU CLASS与本地对接的接口实现文件,我们需要对这个源文件内没有接口填充其具体实现内容,当然,我们主要的目的是想借助DFU这个IAP来实现对APP的升级和跳转,而这些接口就是实现对FLASH读写的操作。

uint16_tMEM_If_Init_FS(void)
{
/*USERCODEBEGIN0*/
HAL_FLASH_Unlock();
return(USBD_OK);
/*USERCODEEND0*/
}

如上,初始化实现对FALSH的解锁。

uint16_tMEM_If_DeInit_FS(void)
{
/*USERCODEBEGIN1*/
HAL_FLASH_Lock();
return(USBD_OK);
/*USERCODEEND1*/
}

对应地,反初始化时,实现对FALSH的上锁。

uint16_tMEM_If_Erase_FS(uint32_tAdd)
{
/*USERCODEBEGIN2*/
uint32_tstartsector=0;
uint32_tsectornb=0;
/*VariablecontainsFlashoperationstatus*/
HAL_StatusTypeDefstatus;
FLASH_EraseInitTypeDeferaseinitstruct;

/*Getthenumberofsector*/
startsector=GetSector(Add);

eraseinitstruct.TypeErase=FLASH_TYPEERASE_SECTORS;
eraseinitstruct.VoltageRange=FLASH_VOLTAGE_RANGE_3;
eraseinitstruct.Sector=startsector;
eraseinitstruct.NbSectors=1;
status=HAL_FLASHEx_Erase(&eraseinitstruct,§ornb);

if(status!=HAL_OK)
{
return1;
}
return0;
/*USERCODEEND2*/
}

如上,实现对FLASH擦除操作。对应的GetSector函数实现如下:

staticuint32_tGetSector(uint32_tAddress)
{
uint32_tsector=0;

if((Address< ADDR_FLASH_SECTOR_1) && (Address >=ADDR_FLASH_SECTOR_0))
{
sector=FLASH_SECTOR_0;
}
elseif((Address< ADDR_FLASH_SECTOR_2) && (Address >=ADDR_FLASH_SECTOR_1))
{
sector=FLASH_SECTOR_1;
}
elseif((Address< ADDR_FLASH_SECTOR_3) && (Address >=ADDR_FLASH_SECTOR_2))
{
sector=FLASH_SECTOR_2;
}
elseif((Address< ADDR_FLASH_SECTOR_4) && (Address >=ADDR_FLASH_SECTOR_3))
{
sector=FLASH_SECTOR_3;
}
elseif((Address< ADDR_FLASH_SECTOR_5) && (Address >=ADDR_FLASH_SECTOR_4))
{
sector=FLASH_SECTOR_4;
}
elseif((Address< ADDR_FLASH_SECTOR_6) && (Address >=ADDR_FLASH_SECTOR_5))
{
sector=FLASH_SECTOR_5;
}
elseif((Address< ADDR_FLASH_SECTOR_7) && (Address >=ADDR_FLASH_SECTOR_6))
{
sector=FLASH_SECTOR_6;
}
elseif((Address< ADDR_FLASH_SECTOR_8) && (Address >=ADDR_FLASH_SECTOR_7))
{
sector=FLASH_SECTOR_7;
}
elseif((Address< ADDR_FLASH_SECTOR_9) && (Address >=ADDR_FLASH_SECTOR_8))
{
sector=FLASH_SECTOR_8;
}
elseif((Address< ADDR_FLASH_SECTOR_10) && (Address >=ADDR_FLASH_SECTOR_9))
{
sector=FLASH_SECTOR_9;
}
elseif((Address< ADDR_FLASH_SECTOR_11) && (Address >=ADDR_FLASH_SECTOR_10))
{
sector=FLASH_SECTOR_10;
}
else
{
sector=FLASH_SECTOR_11;
}
returnsector;
}

写操作:

uint16_tMEM_If_Write_FS(uint8_t*src,uint8_t*dest,uint32_tLen)
{
/*USERCODEBEGIN3*/
uint32_ti=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 2;
      }
    }
    else
    {
      /* Error occurred while writing data in Flash memory */
      return 1;
    }
  }
  return 0;
  /* USER CODE END 3 */ 
}

如上,实现对FLASH的写操作。

uint8_t*MEM_If_Read_FS(uint8_t*src,uint8_t*dest,uint32_tLen)
{
/*ReturnavalidaddresstoavoidHardFault*/
/*USERCODEBEGIN4*/
uint32_ti=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_tMEM_If_GetStatus_FS(uint32_tAdd,uint8_tCmd,uint8_t*buffer)
{
/*USERCODEBEGIN5*/
switch(Cmd)
{
caseDFU_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:
    buffer[1] = (uint8_t)FLASH_ERASE_TIME;
    buffer[2] = (uint8_t)(FLASH_ERASE_TIME << 8);
    buffer[3] = 0;
  default:

    break;
  }
  return  (USBD_OK);
  /* USER CODE END 5 */  
}

获取状态接口实现。

接下来实现从DFU跳转到APP的功能,在main函数中 :

/*USERCODEBEGIN2*/
if(HAL_GPIO_ReadPin(USER_BTN_GPIO_Port,USER_BTN_Pin)==GPIO_PIN_SET)
{
/*Testifusercodeisprogrammedstartingfromaddress0x0800C000*/
if(((*(__IOuint32_t*)USBD_DFU_APP_DEFAULT_ADD)&0x2FFE0000)==0x20000000)
{
/*Jumptouserapplication*/
JumpAddress=*(__IOuint32_t*)(USBD_DFU_APP_DEFAULT_ADD+4);
JumpToApplication=(pFunction)JumpAddress;

/*Initializeuserapplication'sStackPointer*/
__set_MSP(*(__IOuint32_t*)USBD_DFU_APP_DEFAULT_ADD);
JumpToApplication();
}
}
MX_USB_DEVICE_Init();
/*USERCODEEND2*/

这样代码就大体修改完了,再次编译下,生成最终可执行文件。我们得到IAR如下编译信息:

18170bytesofreadonlycodememory
290bytesofreadonlydatamemory
12517bytesofreadwritedatamemory

那么DFU这个IAP本身所占ROM大小为(18170+290 )/1024 =18.02K,从图4中我们可以得知,它需要占用两个扇区(扇区0和1都是16K大小),那么APP至少应该是从扇区2开始。

此时,我们回过头去看之前提到的APP偏移地址的问题,此处结合之前说到的APP必须是第4个扇区起,那么最终APP的地址应该设置在第4个扇区的起始位置,也就是扇区3的位置,从图4可知,扇区3的起始位置为0x0800C000,这样我们回到CubeMx中将其设置,这也就是为什么APP地址设置为0x0800C000的原因。

重新编译并烧录进MCU,接下来连接USB到PC,接可是识别这个DFU设备,并通过DfuSeDemo这个软件升级APP了。

4 制作APP工程需要注意事项

不同编译器设置方式略有不同,在IAR中:首先将system_stm32f4xx.c文件中找到VECT_TAB_OFFSET宏定义 :

#defineVECT_TAB_OFFSET0xC000
1

即将中断向量表的偏移位置相应偏移0xC000.接下来修改连接选项 :

191c68c8-5474-11ed-a3b6-dac502259ad0.png1930c296-5474-11ed-a3b6-dac502259ad0.png

图8 IAR链接设置

MDK中:?首先也是修改system_stm32f4xx.c文件中的VECT_TAB_OFFSET宏定义.接着 :

194738d2-5474-11ed-a3b6-dac502259ad0.png

图9 Target设置

相应设置好了接可以了。

5 测试

最后就是通过ST的软件Dfu File Manager这个软件将APP的HEX文件或BIN文件转化成dfu文件,然后通过DfuSeDemo这个软件导入dfu文件,最终烧录APP到0x0800C000这个地址了,最终验证是可以运行的。

6 总结

APP的起始地址应该设置为扇区的起始地址,且即使没有重叠,也不能放在IAP的所在扇区。

APP的起始地址必须在USB DFU CLASS接口字符串所描述的可读写扇区范围内。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • APP
    APP
    +关注

    关注

    33

    文章

    1529

    浏览量

    71629
  • IAP
    IAP
    +关注

    关注

    2

    文章

    161

    浏览量

    23964
  • CubeMx
    +关注

    关注

    0

    文章

    28

    浏览量

    1211

原文标题:如何使用CubeMx生成一个DFU工程

文章出处:【微信号:技术让梦想更伟大,微信公众号:技术让梦想更伟大】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    【CANNON试用体验】STM32 DFU下载与 DFU生成工具

    方法,都是使用dfufile manager进行转换,偶然接触开源项目发现有python的工具可以将 bin转成dfudfu.py,
    发表于 04-23 22:19

    cubemx生成工程有问题

    cubemx自己生成工程就有问题。。为什么啊。。
    发表于 10-11 09:04

    使用CubeMX自动生成工程编译出错

    使用CubeMX 自动生成工程编译出错 ??工程文件包括 sd卡,网络,sdram,等,在编译时提示 HAL_SD_TransferStateTypedef 等找不到,但是我看看
    发表于 11-27 09:01

    修改cubemx,重新生成工程,原有工程自己的代码有些被删

    cubemx我最近才开始使用。 现在碰到这样问题:之前已经有人使用cubemx生成
    发表于 12-27 09:08

    使用CUBEMX生成工程

    参考野火《FreeRTOS内核实现与应用开发实战》####、使用CUBEMX生成工程主要外设初始化有usart3、led 及按键首先选择芯片STM32F767ZITx配置时钟源配置l
    发表于 08-10 08:10

    使用CubeMX生成工程文件

    ,使用CubeMX生成工程文件1,配置串口引脚2,时钟配置3,打开中断(此图还不太懂,先记下来,待研究)4,生成代码备注:此处选为自己所用
    发表于 08-13 06:00

    基于STM32CUBEMX生成工程些步骤

    ,害,上图做实验的话是够用的了2.生成工程因为之前用原子的例程写了,但是对于我这种懒人来说,还是STM32CUBEMX比较方便,后期开发
    发表于 08-19 07:08

    如何使用stm32 cubeMX配置生成工程

    如何使用stm32 cubeMX配置生成工程
    发表于 12-06 06:56

    如何用CubeMX生成工程文件?

    CubeMX移植野火ILI9341触摸画板笔记前言前期准备CubeMX配置移植代码调试感言前言本次移植使用CubeMX和Keil5,大致流程为用CubeMX
    发表于 01-21 07:37

    如何使用STM32CUBEMX生成工程

    使用STM32CUBEMX生成工程IAR+J-LINK调试准备工作CUBEMX生成的代码问题所在如何改变文本的样式插入链接与图片如何插入
    发表于 01-27 07:29

    如何使用CubeMx创建简单的LED工程

    简介这篇文章介绍如何使用CubeMx创建简单的LED工程,该工程
    发表于 02-21 06:07

    STM32Cubemx生成USB DFU固件升级程序

    STM32Cubemx 生成USB DFU 固件升级程序生成代码如图,新建项目工程2. 修改关键代码keil软件打开
    发表于 02-22 07:03

    使用CubeMX生成TCPEchoServer程序

    使用CubeMX生成TCPEchoServer程序
    发表于 12-08 11:45 0次下载

    使用CubeMX 生成 USB从机 Custom HID 工程(STM32F407ZE )

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档使用CubeMX 生成 USB从机 HID 自定义设备工程(STM32F40
    发表于 12-04 10:36 11次下载
    使用<b class='flag-5'>CubeMX</b> <b class='flag-5'>生成</b> USB从机 Custom HID <b class='flag-5'>工程</b>(STM32F407ZE )

    STM32Cube 生成 USB DFU

    STM32Cubemx 生成USB DFU 固件升级程序生成代码如图,新建项目工程2. 修改关键代码keil软件打开
    发表于 12-28 19:45 5次下载
    STM32Cube <b class='flag-5'>生成</b> USB <b class='flag-5'>DFU</b>