资讯动态

STM32 QSPI内存映射与TouchGFX外部Flash资源部署实战

发布时间:2026/10/26 11:38:59 来源:尧图企业网站定制
简介此资源包面向已具备 STM32 与图形界面开发基础的嵌入式工程师及学习者聚焦 STM32CubeMX 与 TouchGFX 协同开发中一个典型痛点TouchGFX 生成的图片、字库等超大常量数组容易占满片内 Flash需要迁移到外部 QSPI FLASH如 W25Q256保存与按需读取。包内共约 2000 个文件以 652 个 C 源文件、530 个头文件及 191 个 hpp、125 个 cpp 为主构成完整工程代码主体另含 242 个 d、242 个 o 等编译中间文件42 个 icf 与 3 个 sct 链接脚本、23 个 lib 与 48 个 a 静态库、51 个 s 汇编启动文件以及少量 hex、map、ioc、uvprojx 等工程与产物文件便于直接对照链接配置和固件布局。压缩包约 119.4MB已有 1731 人学习下载。读者可据此研究外部存储器映射、资源分区存放与工程编译链接的完整组织方式理解大数组从片内搬移到 QSPI 的实现路径并复用其目录结构与配置思路排查类似容量受限问题。1. 一个 8 MB 外存跑 UI 的工程这个 zip 到底解决了什么问题拿到一块带 QSPI Flash 的 STM32 板子屏幕分辨率一上到 480×272 以上内部 Flash 就开始捉襟见肘几张全屏位图加两套中文字库几 MB 就没了。STM32CubeMX 把时钟树和外设点完TouchGFX Designer 把界面拖完中间那根线——资源怎么落到外部 QSPI Flash、CPU 怎么在内存映射模式下直接读图——很少有人给出完整可编译的样板。这个工程包干的就是这件事CubeMX 生成的 HAL 初始化、TouchGFX 的 C 生成层、QSPI 外部加载与烧写配置三者拼成一条能跑通的链路。适合正在选 GUI 方案、被内部 Flash 容量卡住、或者第一次接触 QSPI 内存映射模式的嵌入式开发者也适合想拿一个现成骨架改自己板级配置的熟手。2. 工程拆解CubeMX 生成的 HAL 与 TouchGFX 目录的对应关系2.1 顶层目录逐个看拿到压缩包先别急着打开 IDE先把目录树铺开看一眼能省下后面半小时找文件的时间。这类工程一般是三方工具拼出来的每一层目录归属不同搞清楚谁生成谁后面改配置才不至于改错地方被覆盖。典型的顶层结构长这样目录生成方作用能不能手改Core/Inc、Core/SrcSTM32CubeMXmain.c、外设 MSP、中断服务、时钟配置可以但重新生成会合并Drivers/STM32CubeMXHAL、CMSIS、LL 驱动不建议重新生成会覆盖Middlewares/ST/touchgfx/TouchGFX 安装包框架头文件和库根目录配置可改源码不要动TouchGFX/Application/用户 Designer用户界面、状态机、自定义控件主改区TouchGFX/Target/用户 Generator显示、触摸、DMA2D、QSPI 接入层主改区TouchGFX/Generated/TouchGFX Designer视图基类、资源索引、字体与位图符号别动会被覆盖TouchGFX/gui/TouchGFX DesignerScreen1View 等 UI 代码主改区MDK-ARM/、STM32CubeIDE/、EWARM/CubeMX 或手动建各 IDE 工程文件视情况几个文件夹名字对不上是很常见的现象不同版本的 CubeMX 生成时会带创作者的目录前缀比如Core前多一层文件夹名。这不影响编译只要 IDE 里的 include 路径对着就行不用强迫症式地改名改错了反而会把 TouchGFX Generator 的路径解析搞乱。TouchGFX/Target/这一层是这个工程里比较关键的地方。标准 TouchGFX 库自带一个STM32TouchController和TouchGFXHAL这个工程把它们和 QSPI 的初始化顺序绑在了一起先 CubeMX 初始化 QSPI 到内存映射模式再让 TouchGFX 框架启动因为框架第一次刷屏时就会去外部 Flash 取背景位图。如果初始化顺序反了第一帧大概率是白屏或者整片色块——后面避坑章节会展开讲。2.2 TouchGFX 生成代码与用户代码的边界搞不清哪块代码归谁管是这个工程新手最容易踩的坑。TouchGFX 的生成逻辑是基类 子类Designer 生成基类放在Generated/gui_generated/用户可改的子类放在gui/两边的类名一一对应。举个实际场景。屏上有个按钮点击后切换页面。Designer 里拖按钮、改名字、绑定交互点 Generate Code会生成这些文件Generated/gui_generated/src/screen1_screen/Screen1ViewBase.cpp按钮的回调注册、控件布局、setupScreen()的骨架gui/src/screen1_screen/Screen1View.cpp空的子类用户往里加逻辑Generated/gui_generated/include/gui_generated/screen1_screen/Screen1ViewBase.hpp基类声明包含virtual void buttonCallbackHandler(...)这样的虚函数用户要在按钮点击后干活不是在Screen1ViewBase.cpp里塞代码而是在Screen1View.cpp里重写回调// gui/src/screen1_screen/Screen1View.cpp // 这个文件由 Designer 首次生成之后用户维护重新生成代码不会覆盖 #include gui/screen1_screen/Screen1View.hpp Screen1View::Screen1View() { // 构造时不要碰硬件此时 HAL 已经初始化完但 TouchGFX 调度器还没起 } void Screen1View::setupScreen() { Screen1ViewBase::setupScreen(); // 界面控件装好之后再做一次状态同步比如根据全局变量点亮某个指示灯 } void Screen1View::tearDownScreen() { Screen1ViewBase::tearDownScreen(); } void Screen1View::startButtonClicked() { // Designer 里给按钮绑定了 Call new virtual function就会生成这个虚函数的空实现 // 业务逻辑写在这里切页、读传感器、改全局模型都行 application().gotoScreen2ScreenNoTransition(); }要理解的关键点Screen1ViewBase每次 Generate Code 都会重写一遍用户代码放进去必丢Screen1View只在第一次创建时生成之后 Designer 不会覆盖已有文件所以业务逻辑必须落在这里。同理TouchGFX/Target/下的TouchGFXHAL.cpp是用户可改的 HAL 接入层而Middlewares/ST/touchgfx/framework/下的框架源码就不要碰了改了升级框架时对不上版本。另一个边界是资源符号。Designer 里加的每张图片、每套字体都会在Generated/images/和Generated/fonts/里生成.cpp形式的符号表形如BITMAP_BACKGROUND_ID、FONT_SIMHEI_24。这些符号在用户代码里可以直接引用但前提是资源真的被编译进了最终镜像。如果资源被配置成放外部 Flash这些符号指向的就是0x90000000开头的地址运行时读取靠的是 QSPI 内存映射中间没有 memcpy 这一步——这个机制下一章展开。3. QSPI 配置从时钟树到内存映射模式的参数链路3.1 QSPI 控制器五个参数怎么定CubeMX 的 QUADSPI 界面里参数不多但每一项都直接影响能不能读对数据。这一小节按必填、易错、可默认的顺序拆一遍。核心参数是这几个ClockPrescalerQSPI 内核时钟的分频系数。假设 AHB 给 QSPI 的时钟是 200 MHz分频 4 后 QSPI 时钟 50 MHz。多数 NOR Flash 标称能跑 104 MHz但板子上走线一长、排线一转50 MHz 就够稳了。初期调试建议直接给到 4 或 8等工程跑通再往下压。FifoThresholdFIFO 触发阈值。设 4 表示 FIFO 里攒够 4 字节才触发一次 DMA 或中断请求。这个值影响吞吐和延迟做内存映射模式时它基本不参与做 DMA 搬运资源时设 4 或 8 都行设太小会频繁进中断。SampleShifting采样偏移。Flash 时钟超过 50 MHz 时建议开半周期QSPI_SAMPLE_SHIFTING_HALFCYCLE补偿时钟和数据之间的走线延迟。这个参数是玄学重灾区同一块板子换一批 Flash 芯片可能就得改。FlashSize外部 Flash 容量的以 2 为底的对数。8 MB 的 Flash 填 2316 MB 填 24。填错了访问到边界外会直接 HardFault而且报错位置和真正的原因八竿子打不着非常难查。ChipSelectHighTime片选拉高的保持时间单位是 QSPI 时钟周期。Flash 手册里叫 tSHSL4 个周期是通用值。ClockModeSPI 模式 0 或 3绝大多数 NOR Flash 支持模式 0先按 0 配。CubeMX 生成的初始化代码长这样/* Core/Src/quadspi.cCubeMX 生成参数按上面的思路填 */ QSPI_HandleTypeDef hqspi; void MX_QUADSPI_Init(void) { hqspi.Instance QUADSPI; /* 200 MHz 内核时钟 / 4 50 MHz先保证能读通跑稳了再提速 */ hqspi.Init.ClockPrescaler 4; /* FIFO 阈值 4DMA 搬运位图资源时不至于频繁进中断 */ hqspi.Init.FifoThreshold 4; /* 采样偏移半周期走线较长或 Flash 较慢时建议开启 */ hqspi.Init.SampleShifting QSPI_SAMPLE_SHIFTING_HALFCYCLE; /* 2^23 8 MB和板上 Flash 实际容量一致写错会越界 */ hqspi.Init.FlashSize 23; /* 片选保持 4 个时钟周期对应 Flash 手册的 tSHSL */ hqspi.Init.ChipSelectHighTime QSPI_CS_HIGH_TIME_4_CYCLE; /* 模式 0CPOL0 CPHA0 */ hqspi.Init.ClockMode QSPI_CLOCK_MODE_0; hqspi.Init.FlashID QSPI_FLASH_ID_1; hqspi.Init.DualFlash QSPI_DUALFLASH_DISABLE; if (HAL_QSPI_Init(hqspi) ! HAL_OK) { Error_Handler(); } }参数说明就按注释里的走ClockPrescaler决定的不是QSPI 时钟而是QSPI 相对于内核时钟的分频改这个之前先确认时钟树里 QUADSPI 的时钟源到底给到多少。FlashSize填的是指数不是字节数这一点 CubeMX 的 tooltip 里其实写清楚了但真正配置的时候十个里有八个会填成0x800000。SampleShifting在 50 MHz 以下基本可以不折腾一旦往 80 MHz 以上走它就是第一个要试的参数。3.2 内存映射模式与 TouchGFX 读取路径配置完控制器只是让 QSPI 能发命令真正让 TouchGFX 像读内部 Flash 一样读外部 Flash 的是内存映射模式Memory Mapped Mode。这个模式下QSPI 外设把外部 Flash 的地址空间映射到 MCU 的一个固定地址区间STM32 上是0x90000000起步CPU 一条LDR指令就能取到外部 Flash 里的字节不用软件手动发读命令。进入这个模式的代码/* Core/Src/quadspi.c 追加初始化完控制器后调用一次 */ QSPI_CommandTypeDef s_command {0}; QSPI_MemoryMappedTypeDef s_mem_mapped_cfg {0}; static void QSPI_EnterMemoryMapped(void) { /* 读命令的指令码因 Flash 型号而异0xEB 是 Quad I/O Fast Read通用性较好 */ s_command.InstructionMode QSPI_INSTRUCTION_1_LINE; s_command.Instruction 0xEB; /* 地址阶段走 4 线24 位地址够覆盖 16 MB 空间 */ s_command.AddressMode QSPI_ADDRESS_4_LINES; s_command.AddressSize QSPI_ADDRESS_24_BITS; /* 4 线交替字节阶段某些 Flash 需要它来确认模式 */ s_command.AlternateByteMode QSPI_ALTERNATE_BYTES_4_LINES; s_command.AlternateBytes 0x00; s_command.AlternateBytesSize QSPI_ALTERNATE_BYTES_8_BITS; s_command.DataMode QSPI_DATA_4_LINES; /* dummy 周期数必须按 Flash 手册填填少了读出来全是偏移的垃圾数据 */ s_command.DummyCycles 6; s_command.NbData 0; s_command.DdrMode QSPI_DDR_MODE_DISABLE; s_command.SIOOMode QSPI_SIOO_INST_EVERY_CMD; /* 超时配置映射模式下访问违规会触发超时回调 */ s_mem_mapped_cfg.TimeOutActivation QSPI_TIMEOUT_COUNTER_ENABLE; s_mem_mapped_cfg.TimeoutPeriod 0xFFFF; if (HAL_QSPI_MemoryMapped(hqspi, s_command, s_mem_mapped_cfg) ! HAL_OK) { Error_Handler(); } }几个参数值得单独说。DummyCycles是读命令里地址和数据之间必须插入的空周期数Flash 手册的 AC 特性表里能查到常见值是 6 或 8填错了读出来的数据整体偏移屏幕上是花屏而不是白屏这个现象后面避坑章节会再提。SIOOMode设成EVERY_CMD表示每条命令都重新发一次指令字节稳定性好代价是每个读事务多一两个时钟设成SINGLE会缓存指令理论吞吐略高但部分 Flash 不支持。进入映射模式之后0x90000000往后这一段地址在 CPU 眼里就是普通内存。TouchGFX 那边完全不需要知道背后是 QSPI只要资源符号的地址落在这个区间里框架的Bitmap绘制流程就是正常的指针解引用// TouchGFX 内部绘制位图时本质上是拿到资源指针后按行拷贝 // 这里是从外部 Flash 读图的关键路径简化示意 void paintBitmap(const uint16_t* src, uint16_t* dst, uint32_t pixelCount) { // src 指向 0x90000000 之后的地址每次读都会触发一次 QSPI 读事务 // 如果开了 D-Cache 且没做 cache 维护这里读到的可能是陈旧数据 for (uint32_t i 0; i pixelCount; i) { dst[i] src[i]; } }这段循环看着朴素但它就是外部 Flash 资源在屏幕上的真实读取路径。后面的章节能看出为什么D-Cache和采样偏移这两个东西会直接决定这段循环读出来的是不是正确像素。4. 资源落地图片字体如何编进外部 Flash 并让板子认出来4.1 TouchGFX Designer 的资源打包产物Designer 里的图片和字体最终不是散装.png进固件的而是被转成 C 源文件或二进制资源包。具体走哪条路取决于 TouchGFX Generator 里对Location的配置。这个工程用的是外部 Flash 方案也就是资源打到独立二进制文件烧写到0x90000000不占内部 Flash。打包流程分两步。第一步Designer 把每张图按目标色深一般是 RGB565转成原始像素数组把字体按码点范围转成位图 宽度表。第二步Configuration 里选 External指定基地址Designer 会生成一份assets.bin和一份符号表头文件符号表里每个资源的地址都是一个绝对地址。符号表文件大概长这样// Generated/images/include/gui_generated/images/BitmapDatabase.hpp片段示意 // 每个资源符号对应外部 Flash 里的一个绝对地址 // Designer 里配置的基地址是 0x90000000这里就是基地址加上资源内的偏移 extern const uint16_t bitmap_background[320 * 240]; extern const uint16_t bitmap_icon_wifi[32 * 32]; extern const uint16_t bitmap_icon_battery[32 * 32];实际生成的不是这么简单的数组而是Bitmap::BitmapData结构体里面带宽度、高度、色深、每行字节数这些元数据。但本质一样符号的地址就是数据在外部 Flash 里的位置。工程编译时assets.bin不进.elf它和固件是并列产物。烧录的时候要分两次固件烧到内部 Flash资源烧到外部 Flash。这就是为什么很多人编译一切正常、烧完白屏——固件里根本没有图只是指针指过去了。4.2 外部 Flash 烧写与地址校验烧外部 Flash 需要外挂加载算法external loaderST 官方的 CubeProgrammer 安装包里带了一批常用 Flash 的算法按芯片型号挑一个。命令行烧写# 烧内部 Flash 固件 烧外部 Flash 资源一步做完 STM32_Programmer_CLI -c portSWD freq4000 \ -el MT25TL01G_STM32H7B3I-EVAL.stldr \ -d build/firmware.hex \ -d build/assets.bin 0x90000000 \ -v-c后面是调试器连接参数SWD 接口4 MHz 频率是保守值。-el指定外部加载算法这个文件必须和板上 Flash 型号对得上对不上会报 External loader not found 或者写进去读出来不一致。-d可以给多条一条烧固件HEX 里带地址一条烧资源BIN 要显式给地址0x90000000。-v是写后校验资源动辄几 MB第一次烧一定加上省得回读时发现前 1 KB 对、后面全是 0xFF。烧写完的验证方式有两种。一是命令行回读# 从外部 Flash 读回 64 字节和本地 assets.bin 头对比 STM32_Programmer_CLI -c portSWD freq4000 \ -el MT25TL01G_STM32H7B3I-EVAL.stldr \ -u 0x90000000 64 dump.bin读回来的dump.bin和assets.bin前 64 字节用xxd比一下一致说明加载算法和地址都对了。二是运行期校验在main.c里框架启动之前读0x90000000的头几个字节和符号表里的第一个资源首字节比较。这个校验我一般会临时加调通后删掉比每次靠肉眼看屏幕快得多。提示assets.bin 的文件大小不一定等于资源实际占用空间Designer 可能按扇区对齐补过 0xFF。用ls -l看到的大小对照FlashSize参数算一下余量别填满。5. 避坑排查QSPI TouchGFX 联调时最常见的五个翻车点5.1 白屏、花屏与整片纯色现象固件能跑、串口有打印、TouchGFX 调度器也起来了但屏幕要么纯白、要么整片单色、要么随机噪点。原因白屏最常见的原因是资源没烧进外部 Flash或者烧错地址。CPU 从0x90000000读回来全是0xFFTouchGFX 按 RGB565 解释出来就是接近纯白。整片单色则是资源烧对了但 map 地址偏了——比如 Designer 基地址配成0x90000000烧写时给的却是0x90001000读出来的像素整体错位。随机噪点一般是 QSPI 时序问题指向采样偏移和 dummy 周期。解决先命令行回读0x90000000前 64 字节和assets.bin头对比确认烧写链路没问题再在QSPI_EnterMemoryMapped前后打断点读一下hqspi.Init.ClockPrescaler和DummyCycles的实际值最后把 QSPI 时钟降一半再试如果降速后画面正常就是时序余量不够调SampleShifting或加DummyCycles。5.2 数据读出来对画面就是不动现象回读外部 Flash 数据完全正确但屏上画面静止TouchGFX 的 tick 也在跑就是不刷新。原因这个坑九成出在中断优先级上。TouchGFX 的OSWrappers::signalVSync()由 LTDC 的垂直同步中断触发QSPI 的 DMA 完成中断如果配得比它高DMA 一忙就一直抢不到 VSync调度器以为没到刷屏时机。另一种可能是 DMA2D 的传输完成中断和 QSPI 共用 DMA 通道配错了通道号。解决CubeMX 的 NVIC 里把 LTDC 的 VSync 中断优先级调到 QSPI 之上QSPI 的 DMA 中断调到最低档检查 DMA2D 和 QSPI 用的 DMA 流是否冲突冲突就换一条。改完记得HAL_NVIC_SetPriority在MX_QUADSPI_Init之后、MX_TouchGFX_Init之前调用顺序反了不生效。5.3 HardFault 报到和 QSPI 完全无关的地址现象程序跑几分钟崩一次HardFault 的栈回溯指到某个 UI 绘制函数里看起来和 QSPI 无关。原因FlashSize参数填错导致映射区越界。比如实际 8 MB 填了 2416 MBCPU 访问到0x90800000之后的地址时 QSPI 控制器认为越界但错误信号传上去的时候已经过了几层调用栈回溯完全误导。这个坑特别费时间因为每次崩的位置都不一样。解决FlashSize严格按log2(容量字节数)填8 MB 是 234 MB 是 22。同时在链接脚本里给 QSPI 区留出精确长度让链接器帮忙卡住越界的资源引用/* 链接脚本片段QSPI 资源区长度严格等于 Flash 容量越界直接链接报错 */ MEMORY { FLASH (rx) : ORIGIN 0x08000000, LENGTH 1024K RAM (xrw) : ORIGIN 0x20000000, LENGTH 320K QSPI (r) : ORIGIN 0x90000000, LENGTH 8192K }5.4 画面偶尔闪一下、局部错位现象大部分时间正常偶尔刷屏时某一块区域闪一下或者切换页面瞬间出现半帧旧图。原因Cortex-M7 这类带 D-Cache 的核0x90000000走 QSPI 的访问默认可能被当成可缓存区Cache 里存了旧数据CPU 读的时候命中 Cache 拿到陈旧像素。另一种可能是 TC 中断传输完成没等 QSPI 读完就切了帧缓冲。解决在 MPU 里把0x90000000起始、长度等于 Flash 容量的区域配成 Normal Non-cacheable或者配置成 Write-Through 但读路径加SCB_InvalidateDCache_by_Addr。TouchGFX 的Bitmap绘制前如果检测到资源在外部 Flash官方做法是走Bitmap::setCacheable(false)那条分支。改完用SCB_CleanInvalidateDCache()清一次全 cache 验证。5.5 编译通过、下载也正常一上电就卡死现象调试器全速跑没问题断电重新上电卡在启动阶段连串口打印都出不来。原因启动流程里某处提前访问了0x90000000比如全局对象的构造函数里初始化了一个指向外部 Flash 的Bitmap或者.data段拷贝时链接脚本把 QSPI 区算进了初始化数据区。上电时 QSPI 还没初始化完访问映射区会触发总线错误。解决检查链接脚本QSPI 区不要出现在.data、.bss的任何 QSPI段里只保留资源段检查所有全局对象的构造顺序确保没有任何对象在main()里MX_QUADSPI_Init()之前碰0x90000000。用调试器复位后单步走一遍启动文件看卡在哪一行比读代码猜快得多。6. 进阶把 QSPI 读带宽和帧率一起抠出来的几个手法工程跑通只是及格线真正决定体验的是刷屏帧率和资源加载延迟。这一章说几个我实际调过的方向都是单点改动、可量化对比的。第一个是 QSPI 读命令的形态。默认的 0xEB 命令每条读事务都带一次 1 线指令阶段指令阶段本身不传数据纯开销。换成 QPI 模式4 线指令 4 线地址 4 线数据可以把指令阶段的时钟省掉四分之三理论吞吐提升 20% 到 30%。代价是进入 QPI 模式需要先发一条切换命令且部分 Flash 在 QPI 模式下需要重新上电才能退出。切换代码写在QSPI_EnterMemoryMapped之前/* 发 QPI 使能命令具体指令码按 Flash 手册查常见是 0x38 */ QSPI_CommandTypeDef qpi_enable {0}; qpi_enable.InstructionMode QSPI_INSTRUCTION_1_LINE; qpi_enable.Instruction 0x38; qpi_enable.DataMode QSPI_DATA_NONE; qpi_enable.AddressMode QSPI_ADDRESS_NONE; qpi_enable.AlternateByteMode QSPI_ALTERNATE_BYTES_NONE; qpi_enable.DummyCycles 0; qpi_enable.NbData 0; HAL_QSPI_Command(hqspi, qpi_enable, HAL_QSPI_TIMEOUT_DEFAULT_VALUE); /* 之后的映射命令全部改成 4 线指令阶段 */ s_command.InstructionMode QSPI_INSTRUCTION_4_LINES;改完一定要回读校验QPI 模式下时序余量更紧SampleShifting可能要跟着调。第二个是 DMA2D 的介入时机。TouchGFX 默认在 RGB565 屏幕上直接拷像素DMA2D 主要用来做图层混合和颜色转换。如果 UI 里有大量带 alpha 的图标叠加把混合操作从 CPU 搬到 DMA2D单帧耗时能降一半以上。配置入口在TouchGFX/Target/TouchGFXHAL.cpp的flushFrameBuffer附近把纯软件混合分支替换成HAL_DMA2D_BlendingStart别忘了开 DMA2D 的传输完成中断并在回调里通知 TouchGFX 继续。第三个是资源分层。把最常用的几张背景图和字体放进内部 Flash外部 Flash 只放大图和低频资源。TouchGFX Designer 里每个资源可以单独选 Location不用一刀切全放外部。这样高频绘制路径完全不走 QSPI帧率立刻稳定代价是内部 Flash 要预留 200 KB 到 500 KB。判断哪些资源高频直接看触摸滑动时的 trace或者用 GPIO 翻转 示波器测绘制函数耗时比拍脑袋准。最后一个是启动期校验。我在main()里框架初始化之前加了一段 CRC 校验读外部 Flash 的前 4 KB 算 CRC32和固件里烧进去的期望值比对不一致就点亮红色 LED 停在那里。这段代码占用几十毫秒启动时间但省掉了白屏了到底是没烧还是烧错了还是时序不对的反复排查。这个工程包里的资源是一套可编译的骨架拿下来之后第一件事建议先把 QSPI 时钟压到 25 MHz把assets.bin烧进去、确认能点亮再一步步往上提频率、开 QPI、开 DMA2D每一步都留一个可回退的提交。从那以后我每次碰 QSPI 映射类工程烧写和校验都强制走一遍命令行回读再也没出现过以为烧进去了其实只烧了固件这种翻车。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑