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

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

3天内不再提示

使用DFU方案实现STM32单片机的高级开发

技术让梦想更伟大 来源:csdn 作者:ArthurZheng150 2022-11-28 09:33 次阅读

什么是 DFU

DFU全称为Device Firmware update,是ST官方推出的一个通过USB接口进行IAP升级的方案,同串口ISP一样,他们都集成在了芯片内部的Bootloader区段,可以通过配置boot引脚来启动。(具体可参照ST文档:AN2606)。不过内置DFU的芯片大部分型号都比较新,如果你用的型号没有内置DFU程序,没关系我们也可以通过CubeMX来快速生成和移植一个DFU功能程序到你的Flash中来使用。

DFU方案完整的组件包括单片机DFU Demo代码、PC端升级程序、PC端Demo代码以及相关资料手册等。通过使用DFU方案,我们可以快速的集成升级功能到开发的产品中,同时还能够快速的开发与之配套的升级程序。

使用CubeMX生成初始工程

由于官方提供的DFU例程并不多,我们很难找到现成的可已使用DFU程序,但是通过CubeMX我们可以很快速的配置和生成DFU的Bootloader,下面我们正式开始。

新建CubeMX工程

首先选定好IC的型号,进入配置界面,由于只是Bootloader代码所以这里我们只需要配置USB功能和一个做Bootloader触发的引脚就可,其余的时钟等部分一切按照正常方式配置。

设置USB引脚功能

设定USB模式为Device(HS还是FS并不影响DFU的功能,按照应用选择就可)。

cae4ea6a-6eb1-11ed-8abf-dac502259ad0.png

开启DFU组件

在MiddleWares中加入USB DFU组件

caece1ca-6eb1-11ed-8abf-dac502259ad0.png

设置DFU参数

开启DFU组件后,CubeMX的程序设置窗口的MiddleWares中就会出现DFU程序设置按钮。

cb0498c4-6eb1-11ed-8abf-dac502259ad0.png

点开它将APP加载的地址改为0x0800_c000,这个加载地址根据你实际的应用设置,目前我们选择让flash的前三个sector为Bootloader的区域。

cb0e3366-6eb1-11ed-8abf-dac502259ad0.png

第二个全是字段的参数是用来在DFU连接升级软件式传输给软件用来获取Flash结构字符串数据,很好理解这个小协议的内容,点击设置后,下方的CubeMX的参数说明也写的很清晰,这里就不多说了。当然这些参数也在工程生成后在 usbd_conf.h 和 usbd_dfu_if.c 文件中修改。

cb21e7da-6eb1-11ed-8abf-dac502259ad0.png

最后的设置

最后我们添加一个外部的按键作为触发单片机启动时进入DFU的方式,按键按下后就启动DFU模式,否则直接加载后方APP程序,这里选用PA0引脚,给它设置个User Label 就叫 USER_BTN_GPIO_Port。

cb2daafc-6eb1-11ed-8abf-dac502259ad0.png

修改补全工程

实现 DFU 功能代码

打开 src 目录下的 usbd_dfu_if.c 文件补全其中的功能代码

Flash 解锁

  1. uint16_tMEM_If_Init_HS(void)
    {
    HAL_FLASH_Unlock();
    return(USBD_OK);
    }
    

    Flash 上锁

    uint16_tMEM_If_DeInit_HS(void)
    {
    HAL_FLASH_Lock();
    return(USBD_OK);
    }
    

    Flash 擦除

    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_23) && (Address >=ADDR_FLASH_SECTOR_22))
    {
    sector=FLASH_SECTOR_22;
    }
    else
    {
    sector=FLASH_SECTOR_23;
    }
    returnsector;
    }
    
    uint16_tMEM_If_Erase_HS(uint32_tAdd)
    {
    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)
    {
    return(USBD_FAIL);
    }
    return(USBD_OK);
    }
    

    Flash 写入

    uint16_tMEM_If_Write_HS(uint8_t*src,uint8_t*dest,uint32_tLen)
    {
    uint32_ti=0;
    
    for(i=0;i< Len; i += 4)
    {
    /*Devicevoltagerangesupposedtobe[2.7Vto3.6V],theoperationwill
    bedonebybyte*/
    if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,(uint32_t)(dest+i),*(uint32_t*)(src+i))==HAL_OK)
    {
    /*Checkthewrittenvalue*/
    if(*(uint32_t*)(src+i)!=*(uint32_t*)(dest+i))
    {
    /*Flashcontentdoesn'tmatchSRAMcontent*/
    return(USBD_FAIL);
    }
    }
    else
    {
    /*ErroroccurredwhilewritingdatainFlashmemory*/
    return(USBD_FAIL);
    }
    }
    return(USBD_OK);
    }
    

    Flash 读取

    uint8_t*MEM_If_Read_HS(uint8_t*src,uint8_t*dest,uint32_tLen)
    {
    /*ReturnavalidaddresstoavoidHardFault*/
    uint32_ti=0;
    uint8_t*psrc=src;
    
    for(i=0;i< Len; i++)
          {
            dest[i] = *psrc++;
          }
          /*ReturnavalidaddresstoavoidHardFault*/
    return(uint8_t*)(dest);
    }
    

    获取 Flash 擦写时间参数

    uint16_tMEM_If_GetStatus_HS(uint32_tAdd,uint8_tCmd,uint8_t*buffer)
    {
    /*USERCODEBEGIN11*/
    uint16_ttime;
    
    time=TimingTable[GetSector(Add)];
    
    switch(Cmd)
    {
    caseDFU_MEDIA_PROGRAM:
    buffer[1]=(uint8_t)time;
    buffer[2]=(uint8_t)(time<< 8);
    buffer[3]=0;
    break;
    
    caseDFU_MEDIA_ERASE:
    default:
    buffer[1]=(uint8_t)time;
    buffer[2]=(uint8_t)(time<< 8);
    buffer[3]=0;
    break;
    }
    return(USBD_OK);
    /*USERCODEEND11*/
    }
    

    usbd_dfu_if.h 文件添加的宏定义

    /*Defineflashaddress*///BLANK1#defineADDR_FLASH_SECTOR_00x08000000#defineADDR_FLASH_SECTOR_10x08004000#defineADDR_FLASH_SECTOR_20x08008000#defineADDR_FLASH_SECTOR_30x0800C000#defineADDR_FLASH_SECTOR_40x08010000#defineADDR_FLASH_SECTOR_50x08020000#defineADDR_FLASH_SECTOR_60x08040000#defineADDR_FLASH_SECTOR_70x08060000#defineADDR_FLASH_SECTOR_80x08080000#defineADDR_FLASH_SECTOR_90x080A0000#defineADDR_FLASH_SECTOR_100x080C0000#defineADDR_FLASH_SECTOR_110x080E0000//BLANK2#defineADDR_FLASH_SECTOR_120x08100000#defineADDR_FLASH_SECTOR_130x08104000#defineADDR_FLASH_SECTOR_140x08108000#defineADDR_FLASH_SECTOR_150x0810C000#defineADDR_FLASH_SECTOR_160x08110000#defineADDR_FLASH_SECTOR_170x08120000#defineADDR_FLASH_SECTOR_180x08140000#defineADDR_FLASH_SECTOR_190x08160000#defineADDR_FLASH_SECTOR_200x08180000#defineADDR_FLASH_SECTOR_210x081A0000#defineADDR_FLASH_SECTOR_220x081C0000#defineADDR_FLASH_SECTOR_230x081E0000/*Flashopratetimefromdatasheetpage128*/#defineFLASH_SECTOR_16KB_WRITE_ERASE_TIME500//500usbframe,means500ms#defineFLASH_SECTOR_64KB_WRITE_ERASE_TIME1100#defineFLASH_SECTOR_128KB_WRITE_ERASE_TIME2000
  2. 修改Main文件

    首先在main文件前添加几个用于加载APP程序的变量和函数定义

    typedefvoid(*pFunction)(void);
    
    pFunctionJumpToApplication;
    uint32_tJumpAddress;1234

    然后再 main 函数中加入 外部按键的判断、APP程序加载以及USB DFU初始化功能

    intmain(void)
    {
    /*Resetofallperipherals,InitializestheFlashinterfaceandtheSystick.*/
    HAL_Init();
    
    /*Configurethesystemclock*/
    SystemClock_Config();
    
    /*Initializeallconfiguredperipherals*/
    MX_GPIO_Init();
    
    if(HAL_GPIO_ReadPin(USER_BTN_GPIO_Port,USER_BTN_Pin)==GPIO_PIN_SET)
    {
    HAL_GPIO_WritePin(GPIOG,LD3_Pin,GPIO_PIN_SET);//Fordebug
    /*Testifusercodeisprogrammedstartingfromaddress0x0800C000*/
    if(((*(__IOuint32_t*)USBD_DFU_APP_DEFAULT_ADD)&0x2FF80000)==0x20000000)
    {
    HAL_GPIO_WritePin(GPIOG,LD4_Pin,GPIO_PIN_SET);//Fordebug
    /*Jumptouserapplication*/
    JumpAddress=*(__IOuint32_t*)(USBD_DFU_APP_DEFAULT_ADD+4);
    JumpToApplication=(pFunction)JumpAddress;
    
    /*Resetofallperipherals*/
    HAL_DeInit();
    
    /*Setinterruptvectortoappcode*/
    SCB->VTOR=USBD_DFU_APP_DEFAULT_ADD;
    
    /*Initializeuserapplication'sStackPointer*/
    __set_MSP(*(__IOuint32_t*)USBD_DFU_APP_DEFAULT_ADD);
    JumpToApplication();
    }
    }
    
    MX_USB_DEVICE_Init();
    
    while(1)
    {
    }
    }
    

编译程序下载进入单片机

使用DfuSe

从ST官网DfuSe的程序安装包,并安装。然后我们按下之前写的触发按键并复位单片机,让单片机初始 USB DFU 功能,这时如果你插着单片机的USB线,系统应该已经识别了。如果没有右键更新驱动程序,手动指定驱动搜索路径在DfuSe安装目录下的 BinDriver 内。如果直接无法识别USB设备,建议在CubeMx配置完工程后就编译下载测试一下,看看是不是你在移植过程中哪里写错了。

然后我们需要生成一个地址设定在0x0800_c000后的测试程序,就先编写一个 Blink LED 的程序吧,生成bin、hex或S19文件。然后我们打开DfuSe软件的Dfu file manager来生成DFU软件用的.dfu格式的文件。选择第一项,第二个是用来将.dfu反向变换回来的。大概的操作已经标在图片上了,操作比较简单就不做详细介绍了,记得把Address的地址改到偏移后的地址上否则下载会出错,其他参数可以不用修改。

然后我们打开DfuSe程序,在Upgrade中选择生成好的blink.dfu文件,勾选校验功能,下载程序。成功后复位单片机,LED开始闪烁,移植成功。

更多

仔细区看看DfuSe的安装目录,里面有DFU的资料文档,还有DFU的工程源代码,可以用来改写自己需要的DFU升级程序。

审核编辑:郭婷


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

    关注

    446

    文章

    47705

    浏览量

    408858
  • usb
    usb
    +关注

    关注

    59

    文章

    7412

    浏览量

    257878
  • STM32
    +关注

    关注

    2239

    文章

    10665

    浏览量

    348518

原文标题:STM32高级开发——使用DFU方案

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

收藏 人收藏

    评论

    相关推荐

    使用STM32L4R9单片机开发板,DfuSeDemo无法检测到设备的原因?

    使用STM32L4R9单片机开发板,官方例程STM32Cube_FW_L4_V1.16.0Projects32L4R9IDISCOVERYApplicationsUSB_DeviceD
    发表于 04-09 07:59

    求助,英飞凌单片机程序开发STM32程序开发之间有多少差异?

    英飞凌单片机程序开发STM32程序开发之间有多少差异?英飞凌单片机有没有类似库函数的东西?
    发表于 02-05 07:14

    STM32单片机的特点和功能是什么

    STM32单片机是一款基于ARM Cortex-M内核的32位闪存微控制器,由STMicroelectronics公司(意法半导体)生产。STM32单片机具有高性能、低功耗、丰富的外设
    的头像 发表于 01-03 15:33 3275次阅读

    STM32单片机实现固件在线升级(IAP)

    1,固件升级方案综述单片机的固件升级方式有很多种。1、ICP:简单说就是在单片机开发时使用烧录器升级程序,比如使用J-Link烧录单片机程序
    的头像 发表于 12-16 08:00 789次阅读
    <b class='flag-5'>STM32</b><b class='flag-5'>单片机</b><b class='flag-5'>实现</b>固件在线升级(IAP)

    你用过哪些编程语言开发单片机

    C语言是最常用的一种用于单片机开发语言,也是一种高级编程语言,具有较好的可移植性和可读性。对于单片机,通常使用嵌入式C来进行开发。 举
    发表于 12-04 10:18 323次阅读

    stm32和51单片机的区别

    OTPROM。闪存具有更大的存储容量和更高的读写速度。 接口 STM32单片机具有更多的外设接口,包括USB、CAN和以太网等。而51单片机的接口较少,只能支持一些基本的外设。 开发
    发表于 11-20 13:18

    STM32GOx1基于ARM内核的32位高级MCU

    stm32g0x1-中文参考手册 是stm32G0系列 中文参考手册 帮助单片机开发的     朋友开发该系列
    发表于 09-26 16:20 1次下载

    STM32单片机实现智能家居控制系统的方案

    详细介绍的是使用STM32单片机实现智能家居控制系统的方案介绍和源代码等资料合集
    发表于 09-25 06:23

    一款漂亮的板子 #单片机 #FreeRTOS #stm32 #stm32单片机

    单片机STM32
    百问网官方
    发布于 :2023年09月20日 15:26:00

    stm32单片机如何实现一个按键切换两个程序?

    stm32单片机如何实现一个按键切换两个程序? 作为一款功能强大的微控制器,STM32单片机可以支持多种应用场景,其中一个实用的功能是按键切
    的头像 发表于 09-14 14:22 4352次阅读

    STM32单片机开发环境安装与工程搭建

    STM32: 意法半导体基于ARM公司的Cortex-M内核开发的32位的高性能、低功耗单片机
    发表于 08-22 15:49 720次阅读
    <b class='flag-5'>STM32</b><b class='flag-5'>单片机</b><b class='flag-5'>开发</b>环境安装与工程搭建

    STM32高级定时器(2)#单片机

    单片机STM32电子技术
    未来加油dz
    发布于 :2023年08月11日 16:44:20

    STM32高级定时器(1)#单片机

    单片机STM32电子技术
    未来加油dz
    发布于 :2023年08月11日 16:43:32

    单片机方案开发的基本步骤

    单片机方案开发是指利用单片机进行电子产品设计与开发的过程。在这个过程中,设计人员需要从需求出发,通过选择合适的
    的头像 发表于 07-05 14:39 3544次阅读

    STM32单片机到底是如何实现软硬件结合?

    本文分析 STM32 单片机到底是如何实现软硬件结合的,接着分析单片机程序如何编译、运行。
    发表于 05-16 09:54 783次阅读
    <b class='flag-5'>STM32</b><b class='flag-5'>单片机</b>到底是如何<b class='flag-5'>实现</b>软硬件结合?