如何使用CubeMx生成一个DFU工程
关注、星标公众号,直达精彩内容
素材来源:https://blog.csdn.net/flydream0/article/details/54142208
整理:技术让梦想更伟大 | 李肖遥
1 前言
DFU用来做IAP是很方便的,可以直接通过USB来对APP进行升级,因此,掌握DFU的制作还是挺有好处,特别是使用CubeMx工具可以快速制作,本文将基于STM3240G-EVL评估板来一步一步实现一个DFU的IAP工程。
2 制作CubeMx工程
新建一个STM32F407IGHx工程:Pinout:
Peripherals: RCC->High Speed Clock(HSE):Crystal/Ceramic Resonator SYS->Debug:Serial Wire USB_OTG_FS->Mode:Device_Only
MiddleWares USB_DEVICE->Class For FS IP:Download Firmware Update Class(DFU)
再配置PG15脚为GPIO_Input模式。
Clock Configuration:
图1 时钟树设置
如上图,STM3240G-EVAL评估板使用的是25M HSE。Configuration:NVIC中将USB中断优先级调为5,PG15的标签设置为USER_BTN,此外还需要设置中间件USB DFU参数,如下图:
图2 USB DFU参数设置
如上图,红色框内为需要修改的代码,0x0800C000为需要为用户程序APP烧录的起始地址,字符串“@Internal Flash /0x08000000/03016Ka,01016Kg,01064Kg,07128Kg”实际为USB DFU类的interface字符串描述符,在USB DFU标准文件中有提到可选接口可以使用一个对应的接口字符串来表示此可选接口对应的目标设备的存储块信息,但如何具体规定的,DFU标准(DFU_1.1)并没有要求,是开放的,如下:
图3 DFU标准对接口字符串定义的描述
由此可见,接口字符串定义是可以自由定义的,那么在这里,由于使用到ST工具软件DfuSe Demo(v3.0.5),那么这个工具与USB DFU设备就有一个自定义的接口字符串定义,用来表示当前MCU内部的FLASH组织结构。
接下来我们来看看MCU内部FLASH的组织,由于这里的MCU是STM32F407IGHx,找到其参考文档,并查看其内部FLASH组织结构:
图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设备之间的约定。如下:
图5 DfuSeDemo软件中所显示的内部FLASH的可读写属性
知道了这些信息后,我们再回过头来看APP的起始地址0x0800C000,那么APP的起始地址该如何得来的?有什么要求?与这个接口字符串之间是否有关系?
到目前为止,我们可以确定地是,APP_DEFAULT_ADD的地址必须是位于接口字符串表示的可读写的地址范围内,也就是第4个扇区起(前3个扇区都是只读的),不然是烧录不进去的。其他问题我们先暂且放一放,后续我们回过头来会回答这个问题。
Project Setting :堆设置为0x500,栈大小设置为0x2000。
图6 堆栈设置
另外,在高级设置中,设置先不调用对USB DFU的初始化:
图7 高级设置
最后生成代码。
3 代码完善
对生成后的代码是可以直接编译通过的,我们这里使用的是IAR,当然你也可以使用MDK,由于不同编译器编译的最终文件大小有所差异,而APP的偏移地址在一定程度上也是有考虑到这个DFU本身代码大小的,接下来我们都将以IAR为例。
打开usbd_duf_if.c文件,这个文件就是USB DFU CLASS与本地对接的接口实现文件,我们需要对这个源文件内没有接口填充其具体实现内容,当然,我们主要的目的是想借助DFU这个IAP来实现对APP的升级和跳转,而这些接口就是实现对FLASH读写的操作。
uint16_t MEM_If_Init_FS(void)
{ /* USER CODE BEGIN 0 */ HAL_FLASH_Unlock();return (USBD_OK);/* USER CODE END 0 */
}
如上,初始化实现对FALSH的解锁。
uint16_t MEM_If_DeInit_FS(void)
{ /* USER CODE BEGIN 1 */ HAL_FLASH_Lock();return (USBD_OK);/* USER CODE END 1 */
}
对应地,反初始化时,实现对FALSH的上锁。
uint16_t MEM_If_Erase_FS(uint32_t Add)
{/* USER CODE BEGIN 2 */ uint32_t startsector = 0;uint32_t sectornb = 0;/* Variable contains Flash operation status */HAL_StatusTypeDef status;FLASH_EraseInitTypeDef eraseinitstruct;/* Get the number of sector */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){return 1;}return 0;/* USER CODE END 2 */
}
如上,实现对FLASH擦除操作。对应的GetSector函数实现如下:
static uint32_t GetSector(uint32_t Address)
{uint32_t sector = 0;if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0)){sector = FLASH_SECTOR_0;}else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1)){sector = FLASH_SECTOR_1;}else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2)){sector = FLASH_SECTOR_2;}else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3)){sector = FLASH_SECTOR_3;}else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4)){sector = FLASH_SECTOR_4;}else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5)){sector = FLASH_SECTOR_5;}else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6)){sector = FLASH_SECTOR_6;}else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7)){sector = FLASH_SECTOR_7;}else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8)){sector = FLASH_SECTOR_8;}else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9)){sector = FLASH_SECTOR_9;}else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10)){sector = FLASH_SECTOR_10;}else{sector = FLASH_SECTOR_11;}return sector;
}
写操作:
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 willbe 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_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: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函数中 :
/* USER CODE BEGIN 2 */if(HAL_GPIO_ReadPin(USER_BTN_GPIO_Port,USER_BTN_Pin) ==GPIO_PIN_SET){/* Test if user code is programmed starting from address 0x0800C000 */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);JumpToApplication();}}MX_USB_DEVICE_Init();/* USER CODE END 2 */
这样代码就大体修改完了,再次编译下,生成最终可执行文件。我们得到IAR如下编译信息:
18 170 bytes of readonly code memory290 bytes of readonly data memory12 517 bytes of readwrite data memory
那么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宏定义 :
#define VECT_TAB_OFFSET 0xC000
1
即将中断向量表的偏移位置相应偏移0xC000. 接下来修改连接选项 :
图8 IAR链接设置
MDK中:? 首先也是修改system_stm32f4xx.c文件中的VECT_TAB_OFFSET宏定义. 接着 :
图9 Target设置
相应设置好了接可以了。
5 测试
最后就是通过ST的软件Dfu File Manager这个软件将APP的HEX文件或BIN文件转化成dfu文件,然后通过DfuSeDemo这个软件导入dfu文件,最终烧录APP到0x0800C000这个地址了,最终验证是可以运行的。
6 总结
APP的起始地址应该设置为扇区的起始地址,且即使没有重叠,也不能放在IAP的所在扇区。
APP的起始地址必须在USB DFU CLASS接口字符串所描述的可读写扇区范围内。
版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。
如何使用CubeMx生成一个DFU工程相关推荐
- stm32f4 CubeMX生成IAR工程 移植ST官方 Bootloader 教程
目录 一.前言 二.CubeMX生成 IAR 工程 1.CubeMX生成工程 2.官方的IAP例程 三.移植bootloader 1.移植相关代码 2.flash_if.h文件修改 3.准备App程序 ...
- STM32L051测试 (一、使用CubeMX生成工程文件 — ST系列芯片通用)
本文也适合STM32CubeMX 支持的所有芯片的设置 ..调整文章结构,添加图文说明 2022.2 ..增加其他应用章节,增加 ADC 设置说明 2023.3 CubeMX生成工程步骤 前言 一.时 ...
- 如何利用CubeMX生成代码驱动STM32H750点亮一个LED灯
如何利用CubeMX生成代码驱动STM32H750点亮一个LED灯 CubeMX软件的配置 GPOI或者说引脚配置 第一步 第二步 工程文件的配置 最后一步:完整生成好的代码 CubeMX软件的配置 ...
- 搞到一个IDEA插件,H哥30秒生成一个Spring Cloud Alibaba工程
如果你想使用 Spring Cloud Alibaba,那么你遇到的第一个问题一定是如何快速的创建一个脚手架工程. 近日,阿里巴巴发布了 Spring 的国内脚手架定制版 Aliyun Java In ...
- 使用cubemx建立一个内部flash虚拟一个U盘的工程
序言 使用软件版本 建立一个基础工程 添加flash操作代码 工程链接 可以注意到的是,大部分的stm32芯片是支持usb从机的,当然这类的标准库例程也有,比如野火的例程就有,只要将例程稍做修改就可以 ...
- CubeMX生成的STM32F4xx MDK工程FPU和DSP库的使用
CubeMX生成的STM32F4xx MDK工程FPU和DSP库的使用 STM32F4xx属于Cortex M4F架构,带有32位的单精度硬件FPU(Float Point Unit),支持浮点指令集 ...
- cubeMX生成AC6工程,无法调试!!!
一.前言 由于之前采用KEIL开发,但是由于KEIL的编辑功能实在是太差了,偶然的机会,发现ST官方,有提供自己的开发工具(AC6).实际上是ST公司基于ECLIPSE针对STM32开发的一个插件,E ...
- FreeRTOS记录(九、一个裸机工程转FreeRTOS的实例)
记录一下一个实际项目由裸机程序改成FreeRTOS,以前产品的平台还是C8051单片机上面的程序, 硬件平台改成了STM32L051,同时使用STM32CubeMX生成的工程,使用FreeRTOS系统 ...
- GD32系列总结 - 时钟树总结及CubeMX生成代码
GD32系列总结 - 时钟树总结及CubeMX生成代码 写在前面 时钟树 HSE振荡器时钟(高速外部时钟信号) HSE用户外部时钟(旁路模式) HSE外部晶体/陶瓷谐振器 HSI振荡器时钟 PLL时钟 ...
- 使用Quartus建立第一个FPGA工程
到 www.altera.com.cn 免费申请到Quartus网络版并且安装,这个过程非常简单. 安装好Quartus后,我们开始建立第一个FPGA工程,我们开始建立第一个FPGA工程. 打开后界面 ...
最新文章
- Java 必须掌握的 20+ 种 Spring 常用注解
- 硬核!如何在 Github 精准搜索开源项目?
- 经典C语言程序100例之八八
- 深入react技术栈(8):事件系统
- SpringMVC之RequestHeader注解与CookieValue注解详解
- 运维工程师最容易出的状况,咋就找不到问题根因呢?
- 182.查找重复的电子邮箱
- 如何配置RadASM
- 【Electronics】数字电路实验——交通灯设计
- SiamRPN++算法详解
- H3C无线ap基本配置套路
- Android苹方圆三合一字体,橘色主题-圆形图标 内置苹方+googlesans字体 透明文件夹...
- SurfacePro6解决亮度自动调节问题
- 使用结构化思维,让工作有条不紊
- 定积分之几种常见曲线
- 计算机word中的行间距在哪里设置,word怎么把所有行间距设置成22磅
- 微信小程序流量主广告怎么加圆角
- 网上免费平台学习美术
- 离线语音芯片-开发指南
- Python Crypto.Cipher加密包