完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980php
本章节为你们讲解使用系统bootloader作程序升级的方法,即便不依赖外部boot引脚也能够方便升级。编程
DFU的全称是Device Firmware Upgrade,即设备固件升级框架
68.1 初学者重要提示函数
68.2 跳转到系统bootloader的程序设计学习
68.3 STM32CubeProg的安装说明测试
68.3 STM32CubeProg的程序下载说明ui
68.4 USB DFU方式系统bootloader驱动移植和使用spa
68.6 实验例程设计框架线程
68.7 实验例程说明(MDK)设计
68.8 实验例程说明(IAR)
68.9 总结
程序设计以下,基本是按照第67章3.2小节的方法进行设计
1. /* 2. ****************************************************************************************************** 3. * 函 数 名: JumpToBootloader 4. * 功能说明: 跳转到系统BootLoader 5. * 形 参: 无 6. * 返 回 值: 无 7. ****************************************************************************************************** 8. */ 9. static void JumpToBootloader(void) 10. { 11. uint32_t i=0; 12. void (*SysMemBootJump)(void); /* 声明一个函数指针 */ 13. __IO uint32_t BootAddr = 0x1FF09800; /* STM32H7的系统BootLoader地址 */ 14. 15. /* 关闭全局中断 */ 16. DISABLE_INT(); 17. 18. /* 关闭滴答定时器,复位到默认值 */ 19. SysTick->CTRL = 0; 20. SysTick->LOAD = 0; 21. SysTick->VAL = 0; 22. 23. /* 设置全部时钟到默认状态,使用HSI时钟 */ 24. HAL_RCC_DeInit(); 25. 26. /* 关闭全部中断,清除全部中断挂起标志 */ 27. for (i = 0; i < 8; i++) 28. { 29. NVIC->ICER[i]=0xFFFFFFFF; 30. NVIC->ICPR[i]=0xFFFFFFFF; 31. } 32. 33. /* 使能全局中断 */ 34. ENABLE_INT(); 35. 36. /* 跳转到系统BootLoader,首地址是MSP,地址+4是复位中断服务程序地址 */ 37. SysMemBootJump = (void (*)(void)) (*((uint32_t *) (BootAddr + 4))); 38. 39. /* 设置主堆栈指针 */ 40. __set_MSP(*(uint32_t *)BootAddr); 41. 42. /* 在RTOS工程,这条语句很重要,设置为特权级模式,使用MSP指针 */ 43. __set_CONTROL(0); 44. 45. /* 跳转到系统BootLoader */ 46. SysMemBootJump(); 47. 48. /* 跳转成功的话,不会执行到这里,用户能够在这里添加代码 */ 49. while (1) 50. { 51. 52. } 53. }
这里把程序设计中的几个关键地方作个说明:
STM32CubeProg的安装比较简单,若是你们的电脑中缺乏JAVA环境,会提示安装,按照提示操做便可。
这里特别注意USB DFU驱动的安装,若是你们的电脑上安装了DfuSe软件,那边板子工做在系统bootLoader模式时,电脑端的设备管理器识别出来的标识是这样的:
若是用STM32CubeProg的话,务必要将此驱动删掉,鼠标右击此标识,选择卸载,弹出以下对话框:
卸载完毕后,重启电脑,而后运行STM32CubeProg安装目录里面的STM32Bootloader.bat便可,最后插上设备就能够正常识别了。识别后的标识:
这里把两种下载方式都作个说明,一种是设置外部boot引脚进行下载,另外一种是设置程序跳转到系统bootloader进行下载。
在电脑端设备管理器就能够看到已经识别出来:
应用程序跳转到系统bootLoader比较方便,无需用户操做外置的boot引脚了,只需调用本章第2小节的程序就能够跳转。本章配套的例子是用户按下按键K1后执行跳转程序,你们能够根据须要实现各类触发跳转的方式。跳转成功后,在电脑端设备管理器里面也会看到bootloader标识:
识别成功后就能够下载程序了。
第1步,选择USB方式,点击Connect按钮。
识别成功后的效果以下:
这里要特别注意一点,若是用户没有关闭这个软件,屡次插拔USB线时,记得点击这里的刷新按钮,由于有时候这个软件不会自动显示出来,点击刷新按钮才行。
第2步,添加要下载的hex文件,勾选须要设置的选项,点击启动编程。
弹出这个窗口并非表示下载失败了,而是下载完成后退出了系统bootloader。
第3步,完成下载后的效果以下:
下载完成后板子从新上电就能够看到程序已经成功下载了。
系统bootloader的移植比较简单,仅需添加本章第2小节的程序到本身工程里面便可。里面有个开关中断API,是在bsp.h文件里面定义的:
/* 开关全局中断的宏 */ #define ENABLE_INT() __set_PRIMASK(0) /* 使能全局中断 */ #define DISABLE_INT() __set_PRIMASK(1) /* 禁止全局中断 */
经过程序设计框架,让你们先对配套例程有一个全面的认识,而后再理解细节,本次实验例程的设计框架以下:
第1阶段,上电启动阶段:
第2阶段,进入main函数:
配套例子:
V7-047_基于系统bootloader的USB接口方式IAP升级(USB DFU)
实验目的:
实验内容:
实验操做:
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,中止位 1。
程序设计:
系统栈大小分配:
RAM空间用的DTCM:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化全部的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只须要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 库初始化,此时系统用的仍是H7自带的64MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIV优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到400MHz - 切换使用HSE。 - 此函数会更新全局变量SystemCoreClock,并从新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。 - 默认不开启,若是要使能此选项,务必看V7开发板用户手册第xx章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并开启 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitDWT(); /* 初始化DWT时钟周期计数器 */ bsp_InitKey(); /* 按键初始化,要放在滴答定时器以前,由于按钮检测是经过滴答定时器扫描 */ bsp_InitTimer(); /* 初始化滴答定时器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */ bsp_InitLed(); /* 初始化LED */ bsp_InitExtSDRAM(); /* 初始化SDRAM */ }
MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。
/* ********************************************************************************************************* * 函 数 名: MPU_Config * 功能说明: 配置MPU * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /* ********************************************************************************************************* * 函 数 名: CPU_CACHE_Enable * 功能说明: 使能L1 Cache * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void CPU_CACHE_Enable(void) { /* 使能 I-Cache */ SCB_EnableICache(); /* 使能 D-Cache */ SCB_EnableDCache(); }
每10ms调用一次蜂鸣器处理:
蜂鸣器处理是在滴答定时器中断里面实现,每10ms执行一次检测。
/* ********************************************************************************************************* * 函 数 名: bsp_RunPer10ms * 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求 * 不严格的任务能够放在此函数。好比:按键扫描、蜂鸣器鸣叫控制等。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_RunPer10ms(void) { bsp_KeyScan10ms(); }
主功能:
主程序实现以下操做:
/* ********************************************************************************************************* * 函 数 名: main * 功能说明: c程序入口 * 形 参: 无 * 返 回 值: 错误代码(无需处理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按键代码 */ bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名称和版本等信息 */ PrintfHelp(); /* 打印操做提示 */ bsp_StartAutoTimer(0, 100); /* 启动1个100ms的自动重装的定时器 */ while (1) { bsp_Idle(); /* 这个函数在bsp.c文件。用户能够修改这个函数实现CPU休眠和喂狗 */ /* 判判定时器超时时间 */ if (bsp_CheckTimer(0)) { /* 每隔100ms 进来一次 */ bsp_LedToggle(2); } /* 按键滤波和检测由后台systick中断服务程序实现,咱们只须要调用bsp_GetKey读取键值便可。 */ ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1键按下,K1键按下,跳转到系统BootLoader */ JumpToBootloader(); break; default: /* 其它的键值不处理 */ break; } } } }
配套例子:
V7-047_基于系统bootloader的USB接口方式IAP升级(USB DFU)
实验目的:
实验内容:
实验操做:
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,中止位 1。
程序设计:
系统栈大小分配:
RAM空间用的DTCM:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化全部的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只须要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 库初始化,此时系统用的仍是H7自带的64MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIV优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到400MHz - 切换使用HSE。 - 此函数会更新全局变量SystemCoreClock,并从新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。 - 默认不开启,若是要使能此选项,务必看V7开发板用户手册第xx章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并开启 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitDWT(); /* 初始化DWT时钟周期计数器 */ bsp_InitKey(); /* 按键初始化,要放在滴答定时器以前,由于按钮检测是经过滴答定时器扫描 */ bsp_InitTimer(); /* 初始化滴答定时器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */ bsp_InitLed(); /* 初始化LED */ bsp_InitExtSDRAM(); /* 初始化SDRAM */ }
MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。
/* ********************************************************************************************************* * 函 数 名: MPU_Config * 功能说明: 配置MPU * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /* ********************************************************************************************************* * 函 数 名: CPU_CACHE_Enable * 功能说明: 使能L1 Cache * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void CPU_CACHE_Enable(void) { /* 使能 I-Cache */ SCB_EnableICache(); /* 使能 D-Cache */ SCB_EnableDCache(); }
每10ms调用一次蜂鸣器处理:
蜂鸣器处理是在滴答定时器中断里面实现,每10ms执行一次检测。
/* ********************************************************************************************************* * 函 数 名: bsp_RunPer10ms * 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求 * 不严格的任务能够放在此函数。好比:按键扫描、蜂鸣器鸣叫控制等。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_RunPer10ms(void) { bsp_KeyScan10ms(); }
主功能:
主程序实现以下操做:
/* ********************************************************************************************************* * 函 数 名: main * 功能说明: c程序入口 * 形 参: 无 * 返 回 值: 错误代码(无需处理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按键代码 */ bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名称和版本等信息 */ PrintfHelp(); /* 打印操做提示 */ bsp_StartAutoTimer(0, 100); /* 启动1个100ms的自动重装的定时器 */ while (1) { bsp_Idle(); /* 这个函数在bsp.c文件。用户能够修改这个函数实现CPU休眠和喂狗 */ /* 判判定时器超时时间 */ if (bsp_CheckTimer(0)) { /* 每隔100ms 进来一次 */ bsp_LedToggle(2); } /* 按键滤波和检测由后台systick中断服务程序实现,咱们只须要调用bsp_GetKey读取键值便可。 */ ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1键按下,K1键按下,跳转到系统BootLoader */ JumpToBootloader(); break; default: /* 其它的键值不处理 */ break; } } } }
本章节为你们介绍的USB DFU方式仍是很是实用的,特别是产品硬件不带boot引脚时。