STM32H7使用外部flash运行程序

在淘宝上买了一块核心板,使用的STM32H7B0VBT6。
客服很尽责,帮助了我很多。
H7系列的功能很强大,但是H7B0他有个问题,只有128k的内部flash,这么强大的芯片只有这么小的flash,想搞个RTTreadOS都不行。无奈,智能选择使用外部flash,好在核心板上有两个W25Q64,一个SPI,一个QSPI足够我们使用的。

思路

使用SPI总线的Flash模拟成U盘,然后把app的bin文件拷到模拟u盘中,通过SPI去读取SPI内保存的bin文件数据,然后写入缓存中,QSPI读取缓存数据到QSPI中,最后执行跳转程序,运行app。

细节

1,使用stm32cubemx创建一个usb模板
2,编写SPI,W25Q64驱动

void MX_SPI6_Init(void)
{hspi6.Instance                                 = SPI6;                                                //  使用SPI6hspi6.Init.Mode                               = SPI_MODE_MASTER;                             //  主机模式hspi6.Init.Direction                        = SPI_DIRECTION_2LINES;                        //  双线全双工hspi6.Init.DataSize                            = SPI_DATASIZE_8BIT;                           //  8位数据宽度hspi6.Init.CLKPolarity                        = SPI_POLARITY_LOW;                            //  CLK空闲时保持低电平hspi6.Init.CLKPhase                          = SPI_PHASE_1EDGE;                             //  数据在CLK第一个边沿有效hspi6.Init.NSS                                 = SPI_NSS_HARD_OUTPUT;                         //  使用硬件片选   // 这里将 pll2_q_ck 设置为 100M 作为 SPI6 的内核时钟,然后再经过2分频得到 50M 的SCK驱动时钟hspi6.Init.BaudRatePrescaler                 = SPI_BAUDRATEPRESCALER_2;hspi6.Init.FirstBit                          = SPI_FIRSTBIT_MSB;                                    //  高位在先hspi6.Init.TIMode                           = SPI_TIMODE_DISABLE;                          //  禁止TI模式hspi6.Init.CRCCalculation                 = SPI_CRCCALCULATION_DISABLE;                  //  禁止CRChspi6.Init.CRCPolynomial                   = 0x0;                                         // CRC校验项,这里用不到              hspi6.Init.NSSPMode                             = SPI_NSS_PULSE_DISABLE;                       //  不使用片选脉冲模式hspi6.Init.NSSPolarity                         = SPI_NSS_POLARITY_LOW;                            //  片选低电平有效hspi6.Init.FifoThreshold                     = SPI_FIFO_THRESHOLD_02DATA;                   //  FIFO阈值hspi6.Init.TxCRCInitializationPattern     = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;   // 发送端CRC初始化模式,这里用不到hspi6.Init.RxCRCInitializationPattern  = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;   // 接收端CRC初始化模式,这里用不到hspi6.Init.MasterSSIdleness                = SPI_MASTER_SS_IDLENESS_00CYCLE;            // 额外延迟周期为0hspi6.Init.MasterInterDataIdleness         = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;     // 主机模式下,两个数据帧之间的延迟周期hspi6.Init.MasterReceiverAutoSusp         = SPI_MASTER_RX_AUTOSUSP_DISABLE;            // 禁止自动接收管理hspi6.Init.MasterKeepIOState               = SPI_MASTER_KEEP_IO_STATE_ENABLE;                //   主机模式下,SPI当前引脚状态hspi6.Init.IOSwap                             = SPI_IO_SWAP_DISABLE;                         // 不交换MOSI和MISOHAL_SPI_Init(&hspi6);
}

3,编写QSPI,W25Q64驱动

void MX_OCTOSPI1_Init(void)
{OSPIM_CfgTypeDef sOspiManagerCfg = {0};HAL_OSPI_DeInit(&hospi1);           // 复位OSPIhospi1.Instance                           = OCTOSPI1;                                // OSPI外设hospi1.Init.ClockPrescaler             = 2;                                      // 时钟分频值,将OSPI内核时钟进行 2 分频得到OSPI通信驱动时钟hospi1.Init.FifoThreshold                = 8;                                       // FIFO阈值hospi1.Init.DualQuad                   = HAL_OSPI_DUALQUAD_DISABLE;              // 禁止双OSPI模式hospi1.Init.MemoryType                   = HAL_OSPI_MEMTYPE_MICRON;                // 存储器模式,只有8线模式才会用到hospi1.Init.DeviceSize                     = 23;                                          // flash大小,核心板采用是8M字节的W25Q64,这里设置为23,即2^23                          hospi1.Init.ChipSelectHighTime         = 1;                                      // 片选保持高电平的时间hospi1.Init.FreeRunningClock            = HAL_OSPI_FREERUNCLK_DISABLE;            // 禁止自由时钟模式hospi1.Init.ClockMode                     = HAL_OSPI_CLOCK_MODE_3;                  // 模式3hospi1.Init.WrapSize                       = HAL_OSPI_WRAP_NOT_SUPPORTED;            // 不使用 wrap-sizehospi1.Init.SampleShifting           = HAL_OSPI_SAMPLE_SHIFTING_HALFCYCLE;     // 半个CLK周期之后进行采样hospi1.Init.DelayHoldQuarterCycle    = HAL_OSPI_DHQC_DISABLE;                  // 不使用数据保持功能hospi1.Init.ChipSelectBoundary       = 0;                                                 // 禁止片选边界功能hospi1.Init.ClkChipSelectHighTime  = 0;                                      // 通信结束后 0 个CLK周期CS设置为高hospi1.Init.DelayBlockBypass          = HAL_OSPI_DELAY_BLOCK_BYPASSED;          // 延时模块旁路hospi1.Init.MaxTran                 = 0;                                      // 禁止通信管理功能hospi1.Init.Refresh                 = 0;                                      // 禁止刷新功能HAL_OSPI_Init(&hospi1);    // 初始化 OSPI1 设置sOspiManagerCfg.ClkPort = 1;     // OSPI引脚分配管理器设置,使用 Port1 的 CLKsOspiManagerCfg.NCSPort = 1;     // OSPI引脚分配管理器设置,使用 Port1 的 NCSsOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW;   // OSPI引脚分配管理器设置,使用 Port1 的低4位引脚,IO[3:0]HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);  // 初始化OSPI引脚分配管理器设置
}

4,主函数中初始化

int main(void)
{SCB_EnableICache();        // 使能ICacheSCB_EnableDCache();      // 使能DCacheHAL_Init();                  // 初始化HAL库  SystemClock_Config();   // 配置系统时钟,主频280MHzLED_Init();                    // 初始化LED引脚KEY_Init();                  // 初始化按键引脚USART1_Init();                // USART1初始化    MX_USB_DEVICE_Init();   // 初始化USB设备,识别SPIU盘SPI_W25Qxx_Init();        //初始化SPIOSPI_W25Qxx_Init();     // 初始化OSPI和W25Q64       Main_Menu();         //程序更新引导代码while (1){LED1_Toggle;HAL_Delay(100);}}

5,进入到Main_Menu(); 这个程序更新函数中后会进行选择

void Main_Menu(void)
{uint8_t key = 0;uint8_t quit=    0 ;
//  uint8_t download_flag=0;printf("\r\n======================================================================");printf("\r\n=              (C) COPYRIGHT 2023 STMicroelectronics                 =");printf("\r\n=                                                                    =");printf("\r\n=  STM32H7xx In-Application Programming Application  (Version 1.0.0) =");printf("\r\n=                                                                    =");printf("\r\n=                   By GXY Application Team                          =");printf("\r\n======================================================================");printf("\r\n\r\n");while (1){printf("\r\n===================================");printf("\r\n=   输入序号选择命令          =");printf("\r\n= (1)SPI模拟U盘方式更新     =");printf("\r\n=    (2)SD卡方式更新    =");printf("\r\n= (3)执行跳转           =");printf("\r\n= (4)退出更新           =");printf("\r\n===================================");printf("\r\n\r\n");        /* Clean the input path */__HAL_UART_FLUSH_DRREGISTER(&huart1);__HAL_UART_CLEAR_PEFLAG(&huart1);__HAL_UART_CLEAR_FEFLAG(&huart1);__HAL_UART_CLEAR_NEFLAG(&huart1);__HAL_UART_CLEAR_OREFLAG(&huart1);key=0;/* Receive key */HAL_UART_Receive(&huart1, &key, 1, RX_TIMEOUT);switch (key){case '1' :/* Download user application in the Flash */printf ("正在更新...\r\n");if(SpiDownload()){printf ("更新完成,请执行跳转\r\n");      // 初始化成功}else printf ("重新上电更新\r\n");break;case '2' :printf ("此功能未开发,等待后续开发......\r\n");        // 初始化成功break;case '3' :OSPI_W25Qxx_MemoryMappedMode();    // 配置QSPI为内存映射模式       SCB_DisableICache();        // 关闭ICacheSCB_DisableDCache();     // 关闭DcacheSysTick->CTRL = 0;       // 关闭SysTickSysTick->LOAD = 0;      // 清零重载值SysTick->VAL = 0;           // 清零计数值printf ("跳转地址设置\r\n");    JumpToApplication = (pFunction) (*(__IO uint32_t*) (W25Qxx_Mem_Addr + 4));    // 设置起始地址__set_MSP(*(__IO uint32_t*) W25Qxx_Mem_Addr);  // 设置主堆栈指针printf("跳转到W25Q64运行用户程序>>>\r\n\r\n");JumpToApplication();          // 执行跳转 break;case '4' :quit =1;break;default:printf("选择输入 1,2,3,4控制更新方式\r");break;}if(quit)break;}
}

6,使用串口助手输入1,进入spi模拟u盘方式更新

uint8_t SpiDownload(void)
{int i;if(OSPI_W25Qxx_BlockErase_1024K(W25Qxx_TestAddr)==OSPI_W25Qxx_OK){//               printf ("擦除FLASH完成\r\n");     // 初始化成功for(i=0;i<32;i++){SPI_W25Qxx_ReadBuffer(W25Qxx_ReadBuffer,USB_FAT_Addr+W25Qxx_NumByteToTest*i,W25Qxx_NumByteToTest); // 读取数据
//                  printf ("读取u盘数据进度:%d%%\r\n",i*100/31);     // 初始化成功if(OSPI_W25Qxx_OK==OSPI_W25Qxx_WriteBuffer(W25Qxx_ReadBuffer,W25Qxx_TestAddr+W25Qxx_NumByteToTest*i,W25Qxx_NumByteToTest))// 写入数据{printf ("更新进度:%d%%\r\n",i*100/31);        // 初始化成功}else{printf ("写入失败");        // 初始化成功return 0;}}}return 1;
}

7,串口打印数据

[17:41:36.483]发→◇1
□
[17:41:36.487]收←◆正在更新...[17:41:40.756]收←◆更新进度:0%[17:41:40.815]收←◆更新进度:3%
更新进度:6%
更新进度:9%
更新进度:12%[17:41:40.908]收←◆更新进度:16%
更新进度:19%[17:41:40.950]收←◆更新进度:22%
更新进度:25%
更新进度:29%
更新进度:32%
更新进度:35%[17:41:41.064]收←◆更新进度:38%
更新进度:41%
更新进度:45%
更新进度:48%[17:41:41.153]收←◆更新进度:51%
更新进度:54%
更新进度:58%
更新进度:61%
更新进度:64%
更新进度:67%
更新进度:70%[17:41:41.311]收←◆更新进度:74%
更新进度:77%
更新进度:80%
更新进度:83%
更新进度:87%[17:41:41.424]收←◆更新进度:90%
更新进度:93%[17:41:41.469]收←◆更新进度:96%
更新进度:100%
更新完成,请执行跳转

8,使用串口助手发送3,进行程序跳转

[17:42:06.050]发→◇3
□
[17:42:06.053]收←◆跳转地址设置
跳转到W25Q64运行用户程序>>>程序运行中...
[17:42:07.059]收←◆程序运行中...
[17:42:08.060]收←◆程序运行中...
[17:42:09.062]收←◆程序运行中...
[17:42:10.063]收←◆程序运行中...
[17:42:11.066]收←◆程序运行中...
[17:42:12.067]收←◆程序运行中...
[17:42:13.070]收←◆程序运行中..

至此大功告成,整个工程都是例子程序改的,所以只是列出代表性的代码,大家可以下载工程代码参考,方便更新自己的程序。如有错误,多多指教。

核心板链接:https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-17800249301.10.75712bd3PuFnlt&id=658976139303
创建u盘模板工程参考链接:https://zhuanlan.zhihu.com/p/542651742
模拟U盘更新程序参考链接:https://blog.csdn.net/qq_44810226/article/details/127508789
整个工程代码:https://download.csdn.net/download/qq_43296570/87459589

STM32H7使用外部flash运行程序相关推荐

  1. CSR8675学习笔记:从外部Flash读取bin文件

    为了让CSR867x的开发更容易,现与思度科技联合推出CSR867x学习板[淘宝链接:思度科技CSR开发板]. 技术交流QQ群号:743434463 开发板会员QQ群号:725398389(凭订单号入 ...

  2. Android Studio Terminal 不是内部或外部命令,也不是可运行程序或批处理文件

    版权声明:本文为博主原创文章,未经我的允许不得转载! 转载请标明出处: http://blog.csdn.net/guiying712/article/details/53129961 ,本文出自:[ ...

  3. '无法将“vue”项识别为 cmdlet、函数、脚本文件或可运行程序的名称' 或 'vue不是内部或外部命令' 的解决方法...

    如果在使用 vue 初始化项目的时候提示: vue : 无法将"vue"项识别为 cmdlet.函数.脚本文件或可运行程序的名称.请检查名称的拼写,如果包括路径,请确保路径正确,然 ...

  4. 解决umi 不是内部或外部命令,也不是可运行程序问题

    一 UmiJS安装 安装Node.js,使用node -v命令检测安装结果 安装yarn,其中tyarn使用的是npm.tao.bao.org的源,速度比较快,其实可以将yarn看作为优化后的npm ...

  5. 说说在CMD命令行模式下ADB命令显示为不是内部或外部命令,亦不是可运行程序和批处理文件的解决办法

    有时候在android开发中我们会用到adb命令行,但很多人在cmd中输入adb命令后,会出现'adb'不是内部或外部命令,亦不是可运行程序和批处理文件的问题 解决办法是:找到SDK目录,在其子目录p ...

  6. 在CMD中输入adb命令,提示“‘adb‘ 不是内部或外部命令,也不是可运行程序或批处理文件”的解决方法

    在android开发中有时我们会用到adb命令行,但很多人在cmd中输入adb命令后,会出现'adb'不是内部或外部命令,也不是可运行程序或批处理文件的问题,如图 解决步骤如下: 打开AndroidS ...

  7. 已配置好环境,在CMD中输入adb命令,还是提示“‘adb‘ 不是内部或外部命令,也不是可运行程序或批处理文件”的解决方法

    前提:win10环境,已在系统变量中配置好ANDROID_HOME,且确认无误,cmd输入adb命令,还是提示"'adb' 不是内部或外部命令,也不是可运行程序或批处理文件" 百度 ...

  8. 【stm32H7读内部flash程序】

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.芯片内部flash 二.驱动程序 1.扇区自动识别代码 2.读数据 2.写数据 总结 前言 STM32 corte ...

  9. c语言不是内部或外部命令也不是可运行程序,CMD命令:不是内部或者外部命令也不是可运行的程序或批处理文件...

    [本文转自:https://blog.csdn.net/l_mloveforever/article/details/79513681] 前言: 相信有很多小伙伴都比较喜欢使用Command命令来快速 ...

最新文章

  1. 综述 | 现在是蛋白质组学数据共享和再分析的黄金时间?
  2. pytorch bert预训练模型的加载地址
  3. delphi中exit,abort,break,continue,halt,runerro 的区别
  4. Some Essential JavaScript Questions And Answers(6)
  5. 基于qtc++设计文本编辑器的代码_文本编辑器Vim/Neovim被曝任意代码执行漏洞,Notepad:兄弟等你好久了...
  6. Android wpa_supplicant 四次握手 流程分析
  7. oracle——服务器同时安装服务端和客户端冲突
  8. 第八届蓝桥杯第七题日期问题
  9. 面试被问高并发流量控制,我脸都绿了...
  10. HTTP请求方式和幂等性
  11. JS 在线格式化工具
  12. HDU-6287 口算训练(想法题)
  13. Chaos Mesh® 在腾讯——腾讯互娱混沌工程实践
  14. bmp,jpg,png,tif,wmf,emf与eps图片格式转换
  15. 聚合微服务中的 Swagger API 文档
  16. 杨柳目-杨柳科:杨柳科
  17. 20pinamp;amp;24pin ATX电源针脚定义
  18. Revit模型审查器 使用方法介绍
  19. 测试用例的设计方法——正交排列
  20. 保护眼睛的电脑桌面颜色(推荐)

热门文章

  1. 【读书笔记】精通正则表达式
  2. 巨头押注医疗 | 论XR与医疗的不解之缘
  3. Image Matching from Handcrafted to Deep Features: A Survey 图像匹配译文(三)
  4. 2022-2028全球与中国平板显示器制造设备市场现状及未来发展趋势
  5. 数据结构基础知识点,看完保证期末不挂科!
  6. 论Java selenium 上传文件,图片的正确姿势
  7. 2018年最受欢迎 Linux 发行版都有哪些?
  8. 俄罗斯游戏java源代码_Java俄罗斯方块游戏源代码JAVA游戏源码下载
  9. Unity 网络视频教程第一讲
  10. 广发财智金到底安不安全