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

审核编辑:郭婷


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

    关注

    462

    文章

    53534

    浏览量

    459038
  • usb
    usb
    +关注

    关注

    60

    文章

    8372

    浏览量

    281670
  • STM32
    +关注

    关注

    2305

    文章

    11120

    浏览量

    371134

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

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

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    为什么单片机还在用C语言编程?

    的存储空间非常有限,我们使用者需要靠精打细算来设计程序,根本经不起高级语言臃肿的代码体积。高级语言也无法实现精确的时序控制。 三、C语言是一个折中选择 其实用C语言开发单片机
    发表于 11-28 07:37

    单片机的误区

    1.去背寄存器 寄存器不用死记硬背,,当我们去使用单片机外设的时候就要去配置,千万别去记寄存器,华维单片机编程的导师做开发这么多年了,一个寄存器都记不住。 何况,单片机的寄存器非常多,
    发表于 11-14 07:46

    单片机用什么封装

    。深圳市安凯星科技有限公司在为拓邦、朗科、安徽龙多等客户开发方案时,会根据项目场景精准选择封装类型,确保性能与实用性平衡。 常见单片机封装类型及特点 DIP 封装:直插式的经典之选 DIP(双列直插封装)是最基础的
    的头像 发表于 08-01 13:47 895次阅读

    什么单片机比较耐用

    凯星科技有限公司在多年的单片机应用开发中,对各类单片机的耐用性有着深入了解,通过为拓邦、朗科、安徽龙多等客户提供方案,积累了丰富的选型与优化经验。 影响
    的头像 发表于 07-31 13:48 549次阅读

    单片机的储存优点是什么

    场景。深圳市安凯星科技有限公司在单片机应用开发中,充分利用这些储存优点,为拓邦、朗科、安徽龙多等客户打造了高效稳定的解决方案。 1.集成度高,节省硬件空间 单片机将程序存储器(ROM)
    的头像 发表于 07-31 10:09 490次阅读

    怎么测单片机系统频率

    单片机系统频率是指单片机工作时的时钟频率,它直接影响单片机的运行速度和处理能力,准确测量系统频率对单片机应用开发、程序调试和性能优化具有重要
    的头像 发表于 07-25 11:39 474次阅读

    单片机怎么驱动电机?

    在各类自动化设备和智能装置中,电机是重要的执行部件,而单片机作为控制核心,需要通过特定的方式驱动电机运转。单片机驱动电机并非直接连接即可,而是要根据电机类型和功率,搭配合适的驱动电路,才能实现稳定
    的头像 发表于 07-25 09:31 438次阅读

    单片机开发流程包括什么?

    单片机开发是一个系统性的工程,从需求明确到最终产品落地,需要经历多个相互关联的流程环节,每个环节都对最终产品的性能和质量有着重要影响。 一、需求分析与文档梳理 开发流程的第一步是需求分析,这一
    的头像 发表于 07-22 11:21 622次阅读

    单片机定制开发的设计思路

    单片机定制开发是根据特定场景和功能需求,量身打造符合要求的单片机应用方案,其设计过程需要兼顾技术可行性与实际应用价值,涉及多个关键环节。 一、需求分析阶段
    的头像 发表于 07-17 11:14 584次阅读
    <b class='flag-5'>单片机</b>定制<b class='flag-5'>开发</b>的设计思路

    KF32A136系列单片机产品介绍

    F32A136 系列单片机是基于 KF32 内核架构开发单片机
    的头像 发表于 06-27 11:42 1664次阅读
    KF32A136系列<b class='flag-5'>单片机</b>产品介绍

    STM32F10xxx单片机编程手册

    电子发烧友网站提供《STM32F10xxx单片机编程手册.pdf》资料免费下载
    发表于 04-14 14:56 17次下载

    STM32F103x8 STM32F103xB单片机数据手册

    STM32F103x8STM32F103xB单片机数据手册
    发表于 04-14 14:55 6次下载

    STM32单片机最小系统电路设计

    单片机最小系统是指用最少的电路组成单片机可以工作的系统,通常最小系统包含:电源电路、时钟电路、复位电路、调试/下载电路,对于STM32还需要启动选择电路。总之,刚开始如果不太懂电路的话,就抄别人的电路,然后自己拼凑。
    的头像 发表于 03-12 14:09 1.7w次阅读
    <b class='flag-5'>STM32</b><b class='flag-5'>单片机</b>最小系统电路设计

    32位单片机相关资料和解决方案参考指南

    电子发烧友网站提供《32位单片机相关资料和解决方案参考指南.pdf》资料免费下载
    发表于 01-21 14:00 0次下载
    32位<b class='flag-5'>单片机</b>相关资料和解决<b class='flag-5'>方案</b>参考指南

    单片机Debug工具性能对比 单片机调试常用命令

    单片机(Microcontroller Unit, MCU)调试是嵌入式开发中的一个重要环节,它帮助开发者发现和修复代码中的错误,优化程序性能。不同的单片机
    的头像 发表于 12-19 09:56 2162次阅读