STM32H7使用外部flash运行程序
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运行程序相关推荐
- CSR8675学习笔记:从外部Flash读取bin文件
为了让CSR867x的开发更容易,现与思度科技联合推出CSR867x学习板[淘宝链接:思度科技CSR开发板]. 技术交流QQ群号:743434463 开发板会员QQ群号:725398389(凭订单号入 ...
- Android Studio Terminal 不是内部或外部命令,也不是可运行程序或批处理文件
版权声明:本文为博主原创文章,未经我的允许不得转载! 转载请标明出处: http://blog.csdn.net/guiying712/article/details/53129961 ,本文出自:[ ...
- '无法将“vue”项识别为 cmdlet、函数、脚本文件或可运行程序的名称' 或 'vue不是内部或外部命令' 的解决方法...
如果在使用 vue 初始化项目的时候提示: vue : 无法将"vue"项识别为 cmdlet.函数.脚本文件或可运行程序的名称.请检查名称的拼写,如果包括路径,请确保路径正确,然 ...
- 解决umi 不是内部或外部命令,也不是可运行程序问题
一 UmiJS安装 安装Node.js,使用node -v命令检测安装结果 安装yarn,其中tyarn使用的是npm.tao.bao.org的源,速度比较快,其实可以将yarn看作为优化后的npm ...
- 说说在CMD命令行模式下ADB命令显示为不是内部或外部命令,亦不是可运行程序和批处理文件的解决办法
有时候在android开发中我们会用到adb命令行,但很多人在cmd中输入adb命令后,会出现'adb'不是内部或外部命令,亦不是可运行程序和批处理文件的问题 解决办法是:找到SDK目录,在其子目录p ...
- 在CMD中输入adb命令,提示“‘adb‘ 不是内部或外部命令,也不是可运行程序或批处理文件”的解决方法
在android开发中有时我们会用到adb命令行,但很多人在cmd中输入adb命令后,会出现'adb'不是内部或外部命令,也不是可运行程序或批处理文件的问题,如图 解决步骤如下: 打开AndroidS ...
- 已配置好环境,在CMD中输入adb命令,还是提示“‘adb‘ 不是内部或外部命令,也不是可运行程序或批处理文件”的解决方法
前提:win10环境,已在系统变量中配置好ANDROID_HOME,且确认无误,cmd输入adb命令,还是提示"'adb' 不是内部或外部命令,也不是可运行程序或批处理文件" 百度 ...
- 【stm32H7读内部flash程序】
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.芯片内部flash 二.驱动程序 1.扇区自动识别代码 2.读数据 2.写数据 总结 前言 STM32 corte ...
- c语言不是内部或外部命令也不是可运行程序,CMD命令:不是内部或者外部命令也不是可运行的程序或批处理文件...
[本文转自:https://blog.csdn.net/l_mloveforever/article/details/79513681] 前言: 相信有很多小伙伴都比较喜欢使用Command命令来快速 ...
最新文章
- 综述 | 现在是蛋白质组学数据共享和再分析的黄金时间?
- pytorch bert预训练模型的加载地址
- delphi中exit,abort,break,continue,halt,runerro 的区别
- Some Essential JavaScript Questions And Answers(6)
- 基于qtc++设计文本编辑器的代码_文本编辑器Vim/Neovim被曝任意代码执行漏洞,Notepad:兄弟等你好久了...
- Android wpa_supplicant 四次握手 流程分析
- oracle——服务器同时安装服务端和客户端冲突
- 第八届蓝桥杯第七题日期问题
- 面试被问高并发流量控制,我脸都绿了...
- HTTP请求方式和幂等性
- JS 在线格式化工具
- HDU-6287 口算训练(想法题)
- Chaos Mesh® 在腾讯——腾讯互娱混沌工程实践
- bmp,jpg,png,tif,wmf,emf与eps图片格式转换
- 聚合微服务中的 Swagger API 文档
- 杨柳目-杨柳科:杨柳科
- 20pinamp;amp;24pin ATX电源针脚定义
- Revit模型审查器 使用方法介绍
- 测试用例的设计方法——正交排列
- 保护眼睛的电脑桌面颜色(推荐)
热门文章
- 【读书笔记】精通正则表达式
- 巨头押注医疗 | 论XR与医疗的不解之缘
- Image Matching from Handcrafted to Deep Features: A Survey 图像匹配译文(三)
- 2022-2028全球与中国平板显示器制造设备市场现状及未来发展趋势
- 数据结构基础知识点,看完保证期末不挂科!
- 论Java selenium 上传文件,图片的正确姿势
- 2018年最受欢迎 Linux 发行版都有哪些?
- 俄罗斯游戏java源代码_Java俄罗斯方块游戏源代码JAVA游戏源码下载
- Unity 网络视频教程第一讲
- 广发财智金到底安不安全