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升级程序。

审核编辑:郭婷


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

    关注

    455

    文章

    50697

    浏览量

    423037
  • usb
    usb
    +关注

    关注

    60

    文章

    7934

    浏览量

    264423
  • STM32
    +关注

    关注

    2270

    文章

    10893

    浏览量

    355666

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

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

收藏 人收藏

    评论

    相关推荐

    STM32单片机的特点介绍

    STM32系列单片机是由ST厂商推出的单片机,基于专为高性能、低成本、低功耗嵌入式应用设计的ARM Cortex-M内核,具备一流的外设配置,使其在功耗和集成度方面表现出色,STM32
    的头像 发表于 09-25 17:19 1293次阅读

    单片机STM32可以用Python写吗?可以的开发板有哪些?

    近年来,随着嵌入式技术的发展,Python语言逐渐被引入到单片机开发中,尤其是一些高性能的单片机上。这一趋势给开发者带来了极大的便利,尤其是在快速原型设计和实验中。本文将详细探讨
    的头像 发表于 09-05 08:00 3126次阅读
    <b class='flag-5'>单片机</b><b class='flag-5'>STM32</b>可以用Python写吗?可以的<b class='flag-5'>开发</b>板有哪些?

    stm32单片机用什么软件编程

    STM32单片机是一种广泛应用于嵌入式系统领域的微控制器,具有高性能、低功耗、丰富的外设接口等特点。要对STM32单片机进行编程,需要选择合适的软件工具。 概述
    的头像 发表于 09-02 10:16 1235次阅读

    stm32单片机引脚介绍及功能

    STM32系列单片机是STMicroelectronics(意法半导体)推出的一款基于ARM Cortex-M内核的32位微控制器。STM32单片机具有高性能、低功耗、丰富的外设和灵活
    的头像 发表于 09-02 09:38 4986次阅读

    stm32和51单片机的区别是什么

    架构差异: STM32是基于ARM Cortex-M系列内核的32位微控制器,具有更高效的处理能力和更丰富的外设。 51单片机是基于8051内核的8位微控制器,处理能力相对较低,外设也相对有限
    的头像 发表于 09-02 09:33 711次阅读

    stm32单片机的优势有哪些?

    STM32作为一款强大而多功能的单片机,凭借其性能、功能、开发者生态系统以及广泛的应用领域,成为了众多单片机中的佼佼者。无论是专业开发
    发表于 07-29 09:29

    蓝牙模块如何实现单片机和手机端数据互传

    蓝牙模块实现单片机和手机端数据互传的过程可以分为以下几个步骤: 硬件准备 : 确保你有一个支持蓝牙通信的单片机开发板,如Arduino、Raspberry Pi等。 准备一个兼容的蓝牙
    的头像 发表于 07-24 17:59 2362次阅读
    蓝牙模块如何<b class='flag-5'>实现</b><b class='flag-5'>单片机</b>和手机端数据互传

    基于STM32单片机的智能冰箱控制系统设计

    基于单片机设计的一款智能冰箱控制系统,可以控制冰箱的制冷装置,可以采集冰箱的异味浓度,超过闽值之后控制紫外灯进行消毒工作单片机采用单片机采用单片机采用
    的头像 发表于 06-25 09:21 1178次阅读
    基于<b class='flag-5'>STM32</b><b class='flag-5'>单片机</b>的智能冰箱控制系统设计

    STM32单片机有哪几种常见的开发环境?

    STM32单片机是一款广泛应用于嵌入式系统开发单片机,针对其开发,有以下几种常见的方式:STM32
    的头像 发表于 05-18 08:04 3020次阅读
    <b class='flag-5'>STM32</b><b class='flag-5'>单片机</b>有哪几种常见的<b class='flag-5'>开发</b>环境?

    stm32单片机学习路线

    、函数、指针等。 2.了解电子电路基础 对于单片机开发来说,了解电子电路的基础知识是非常重要的,包括基本的电路原理、电阻、电容、电感等元件的作用和特性。 第二步STM32入门 1.了解
    发表于 05-10 15:34

    STM32单片机最小化系统设计原理

    STM32最小系统,就是能让STM32单片机能够正常工作所必须拥有的组成部分的集合,也是STM32单片机正常运行的必要环境。
    发表于 04-23 14:54 2795次阅读
    <b class='flag-5'>STM32</b><b class='flag-5'>单片机</b>最小化系统设计原理

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

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

    51单片机开发板的主要功能 51单片机开发板能做什么

    51单片机开发板是一种基于8051系列单片机芯片的开发板,具有丰富的功能和广泛的应用。下面将详细介绍51单片机
    的头像 发表于 01-23 15:52 3649次阅读

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

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

    stm32单片机5v电压怎么提供

    STM32单片机一般需要供电电压为3.3V,但也有一些型号的单片机可以工作在5V电压下。在供电电压方面,有几种方法可以为STM32单片机提供
    的头像 发表于 12-22 14:30 1.8w次阅读