读懂Flash接口,我终于明白单片机指令是如何运行的了!

张开发
2026/4/18 15:39:00 15 分钟阅读

分享文章

读懂Flash接口,我终于明白单片机指令是如何运行的了!
作为一个单片机的萌新我之前一直有个想不通的问题 我把写好的点灯程序烧进单片机的 Flash 里按下复位键LED 就开始一闪一闪好像一切都是理所当然的。但我始终搞不懂那些存在 Flash 里的指令到底是怎么跑到 CPU 里的为什么我写的程序能一边让灯闪一边用 DMA 收串口数据CPU 好像完全不用停下来等直到我翻到了这张 Cortex-M4F 的系统架构图盯着中间的 Flash 接口、那三条颜色不同的总线还有那个奇怪的梯形 “总线矩阵”突然就全通了 —— 原来单片机的指令运行根本不是我想的 “单车道堵车”而是一套设计精巧的 “智能快递站” 系统。先搞懂我们的程序到底存在哪刚学单片机的时候我一直误以为程序烧进去之后会全部搬到 SRAM 里运行就像电脑把 exe 加载到内存一样。 后来才发现完全不是Flash就是我们烧程序的地方它是个 “只读仓库”断电数据不丢我们写的所有代码、常量全都存在这。SRAM只是个临时周转的地方只存运行时的变量、堆栈断电就没了根本存不下整个程序。也就是说CPU 从开机到断电每一条指令都要实时从 Flash 里读出来 这就是为什么 Flash 接口这么重要 —— 它就是 CPU 和 Flash 仓库之间的 “大门”所有的指令、常量都要从这个门里运出来。原来 CPU 有三个专属窗口再也不用排队了我之前以为CPU 和 Flash 之间只有一条路所有的请求都要挤在这一条道上那不得堵死 直到看了架构图才发现Cortex-M 内核居然给 CPU 开了三个完全独立的专属通道直接连到中间的总线矩阵 紫色的 ICode、绿色的 DCode、蓝色的 System三条完全分开的总线各干各的互不干扰。我把整个系统类比成一个智能快递站一下子就懂了图片有点小建议放大看CPU 就是那个金牌快递员红色小哥小 C所有的程序任务都是他在跑ICode 总线紫色那条小 C 的「取任务单专属窗口」—— 只干一件事给小 C 送 “下一步要做什么” 的任务清单也就是我们说的程序指令。DCode 总线绿色那条小 C 的「取固定包裹专属窗口」—— 只干一件事给小 C 送 Flash 里提前打包好的固定包裹也就是我们代码里的#define常量。System 总线蓝色那条小 C 的「万能办事窗口」—— 除了上面两件事小 C 所有其他活全走这存临时变量、给 GPIO 外设发命令、操作内存。哦原来如此三个完全独立的窗口小 C 可以一边拿任务单一边拿固定包裹一边办杂事三个事同时干根本不用排队这就是 Cortex-M 用的哈佛架构的核心指令和数据分开走彻底解决了单总线的拥堵问题。如果不懂啥是哈佛架构的话可以看看我之前写的这篇文章。补充知识冯诺依曼 架构 vs 哈佛架构中间的梯形是什么整个系统的智能调度大厅那三个窗口的请求最后都要到 Flash 接口那里不会堵车吗 这就是架构图里那个奇怪的梯形 ——总线矩阵的作用了它就是整个快递站的「智能调度大厅」它有一堆的 “门”一边连着 CPU 的三个窗口一边连着 Flash 接口、SRAM 接口、各种外设接口所有的请求都要经过它来调度如果请求的目的地不一样比如一个要去 Flash 拿任务单一个要去 GPIO 驿站发命令那直接同时放行两个请求走不同的路互不干扰如果两个请求要去同一个地方比如都要去 Flash 仓库那它就会排队叫号核心任务优先 —— 比如 ICode 的取指令请求优先级最高先让他过绝对不能让 CPU 停下来等不然程序就卡了就这么简单能并行就并行不能并行就排队核心任务优先永远不会堵车拿最基础的点灯程序走一遍完整的指令运行流程说了这么多我们拿入门最熟悉的点灯程序一步一步走一遍你就彻底懂了指令到底是怎么运行的// 固定常量存在Flash的固定包裹仓永远不变#define LED_PIN 5int main(void){// 临时变量存在SRAM的临时仓库小C的工作笔记int count 0;// 初始化给LED驿站开门设置工作模式RCC-AHBENR | 1 17; // 给GPIO外设开时钟GPIOA-MODER | 1 (2*LED_PIN); // 设置PA5为输出模式// 主循环让灯一直闪while(1){GPIOA-ODR ^ 1 LED_PIN; // 翻转LED电平灯亮/灭切换delay_ms(500); // 延时500mscount; // 小C自己记一下闪了多少次了}}阶段 1上电开机 ——CPU 先拿第一个任务单片机一上电整个快递站启动小 CCPU第一件事就是直奔 ICode 窗口喊调度大厅「我要去 Flash 拿第一个任务单」 调度大厅立刻把请求转发给 Flash 接口Flash 把main函数的第一条指令int count 0;发给小 CCPU 这就正式开始跑起来了。阶段 2初始化 —— 三个窗口同时开工接下来初始化的时候三个窗口同时开工调度大厅同时处理三个请求完全不堵车ICode 窗口小 C 每干完一个活就立刻来这拿下一个任务刚干完变量初始化就拿 “开 GPIO 时钟” 的任务刚干完开时钟就拿 “设置 GPIO 模式” 的任务全程不停永远有活干。DCode 窗口当小 C 要用到LED_PIN5这个常量的时候他直奔 DCode 窗口从 Flash 里把5这个固定包裹拿出来这个过程和 ICode 窗口完全并行 —— 小 C 一边拿新的任务单一边拿固定包裹两个事同时干不用排队System 窗口同时小 C 的所有杂事全走这给 RCC 驿站发命令开时钟给 LED 驿站发命令设置输出模式去 SRAM 仓库把count的初始值存进去三个活同时推进互不耽误其实总结就是✅ ICode 总线持续取指令码保证 CPU 有指令可执行。✅ DCode 总线读代码区的固定常量✅ System 总线操作外设 读写内存阶段 3主循环 —— 灯开始闪了指令循环跑初始化完了灯开始闪了小 C 进入死循环三个窗口开始循环干活一直转到单片机断电ICode 窗口每次循环都从 Flash 里拿循环里的三个任务翻转 LED、延时、count 加 1保证 CPU 能一直循环跑永远不会缺指令。DCode 窗口每次要翻转 LED 的时候都来这拿LED_PIN5的固定包裹用来算要操作 GPIO 的哪个位每次都要但是因为有专属窗口根本不耽误事。System 窗口同时小 C 去 LED 驿站发命令翻转电平去定时器驿站做延时去 SRAM 仓库更新 count 变量全程不停灯就这么顺顺利利的一闪一闪。这时候你就明白了CPU 每一条指令都是实时从 Flash 里读出来的三个窗口同时运调度大厅分流所以灯才能这么顺的闪根本不会堵车更神奇的DMA 跑腿员居然能和 CPU 同时干活你以为这就完了这个调度大厅还能管另一个兼职跑腿员 ——DMA 就像我之前写的那个进阶程序一边闪灯一边用 DMA 收串口数据这到底是怎么做到的很简单DMA 就是那个不用 CPU 管的兼职跑腿员小 D他自己给调度大厅发请求自己搬数据干完活才通知 CPU串口驿站收到了电脑发的数据小 D 就给调度大厅发请求「我要把这些数据从串口驿站搬到 SRAM 的缓冲区」调度大厅一看小 C 现在正在通过 System 窗口去 LED 驿站发命令小 D 要去的是 SRAM目的地不一样直接同时放行两个人的请求同时走不同的通道CPU 继续闪他的灯DMA 自己默默搬数据CPU 完全不用停这就是为什么我们的单片机能同时干好多事原来不是 CPU 会分身是调度大厅总线矩阵把所有的交通都安排明白了就算多了个跑腿员也完全不会堵车。写在最后原来这就是指令运行的真相看完这些我终于搞懂了单片机的指令是怎么运行的 原来不是我之前想的所有东西挤在一条道上慢慢跑。 而是所有的指令都存在 Flash 里CPU 实时从里面一条一条读CPU 有三个专属的通道取指令、取常量、办杂事分开走不用排队中间的总线矩阵把所有的请求分流、排队保证不堵车核心任务永远优先甚至还能加个 DMA 跑腿员和 CPU 并行干活效率直接拉满原来那个小小的 Flash 接口背后藏着这么多精巧的设计从三个总线的分工到总线矩阵的调度都是为了让 CPU 能最快的拿到指令让程序跑的又快又顺。 现在再看我的点灯程序终于不是那个 “黑盒” 了每一个指令的运行都清清楚楚。如果你想学习更多的好玩小技巧欢迎关注我一起学习一起进步如果这篇教程对你有帮助记得点赞 收藏关注我嵌入式开发少走弯路新手懒鼠鼠一起交流一起进步

更多文章