在这里插入图片描述

在这里插入图片描述

🎬 云泽Q个人主页

🔥 专栏传送入口: 《C语言》《数据结构》《C++》《Linux》《蓝桥杯系列》《笔试算法》《AI赋能》《STM32

⛺️遇见安然遇见你,不负代码不负卿~

前言

大家好啊,我是云泽Q,欢迎阅读我的文章,一名热爱计算机技术的在校大学生,喜欢在课余时间做一些计算机技术的总结性文章,希望我的文章能为你解答困惑~

一、MCU内部总线架构与统一编址详解

在正式深入理解点灯程序之前,我们需要先打通一个至关重要的底层概念——总线与统一编址。这不仅是理解STM32等MCU如何工作的基石,更是我们后续编写代码、操作寄存器的理论依据。为了让大家更直观地理解,我们将结合各种图片,从物理连接、逻辑架构到软件映射进行全方位的拆解。

1.1 总线主从设备与连接基础

之前的文章在STM32或接触ARM Cortex-M3内核时,我都贴过一张MCU内部结构图,但往往没有深究其背后的逻辑。首先我们要明确,MCU内部并非孤立存在的组件堆砌,它包含了ARM Cortex-M3这样的CPU核心,以及大量的片上外设(如GPIO、定时器、通信接口等)。这些外设提供了数据存储能力和设备控制能力。
在这里插入图片描述

既然我们要编写代码来控制这些外设,就必须在硬件层面建立连接。如果没有“线”作为数据通路,CPU发出的数据信号就无法传输到具体的外设中,控制也就无从谈起。这种在MCU内部连接各个硬件模块的“线”,我们称之为总线

基于总线的交互关系,我们将MCU内部的设备分为两类:

  • 总线主设备:指能够主动发起对硬件单元访问请求的设备。最典型的就是ARM CPU,此外还有我们后续文章中会写到的DMA(直接存储器访问)控制器。它们就像发号施令的“皇帝”或“指挥官”。
  • 总线从设备:指被动响应主设备请求的硬件模块。例如内存、Flash以及各类片上外设(GPIO、I2C等)。它们相当于执行具体任务的“职能部门”。
    在这里插入图片描述

1.2 总线矩阵:解决互联复杂度的关键

如果MCU内部只有几个设备,直接连线尚可维持。但随着外设数量增加,如果让每个主设备都直接连接到每个从设备,连线数量将呈爆炸式增长,导致芯片面积巨大且布线极其复杂。为了解决这个问题,引入了总线矩阵的概念。
在这里插入图片描述
在这里插入图片描述

1.2.1 为什么需要总线矩阵?

想象一下,如果所有线都直接从CPU引出连接到各个外设,CPU引脚数将变得不可接受。总线矩阵的作用就是简化这种互联结构。它像一个智能的交通枢纽,主设备只需要连接到矩阵的输入端,从设备连接到输出端,矩阵内部负责根据地址和需求动态建立通路。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2.2 并行访问与仲裁机制

总线矩阵不仅仅是简化连线,它还支持并行访问。例如,当CPU正在通过一条路径访问Flash读取指令时,DMA控制器可以同时通过另一条路径访问SRAM进行数据搬运,两者互不干扰,极大地提高了系统效率。

当多个主设备同时请求访问同一个从设备(例如都想去读内存)时,总线矩阵会启动仲裁机制。它会根据预设的优先级或者轮询策略,决定谁先谁后,避免数据冲突。

1.2.3 专用路径直连

虽然大部分访问通过矩阵,但为了提高性能,某些高频访问的路径会被“特殊照顾”。比如ICode总线,它是专门为了让CPU快速读取Flash中的指令而设计的专线,直接绕过复杂的矩阵仲裁,确保指令执行不卡顿。

1.3 哈佛架构与冯·诺依曼架构的演变

要理解为什么STM32会有ICode、DCode这么多条总线,我们必须先聊聊计算机体系结构中的两大流派:冯·诺依曼架构和哈佛架构。

1.3.1 冯·诺依曼架构:大杂烩

在经典的冯·诺依曼架构中,指令和数据是存放在同一个存储器里的,而且共用同一套总线(地址总线+数据总线)。

  • 缺点:这就好比一条单行道,CPU在取指令的时候就不能传数据,传数据的时候就不能取指令。这会导致“总线瓶颈”,性能受限,实时性也一般。
  • 优点:硬件简单,成本低。
    在这里插入图片描述

1.3.2 哈佛架构:分道扬镳

为了解决速度问题,哈佛架构应运而生。它的核心特点是物理分离

  • 存储空间分离:指令存储器(通常是Flash)和数据存储器(通常是SRAM)是独立的。
  • 总线独立:有独立的指令总线和数据总线。
    在这里插入图片描述
    在这里插入图片描述

这意味着CPU可以一边通过指令总线取指令,一边通过数据总线读写数据,两者并行执行,互不干扰。这就好比修了高架桥和地面道路,快车和慢车互不影响,性能大幅提升,实时性也变得可预测。
在这里插入图片描述

STM32采用的正是改进型的哈佛架构。STM32内部既有存指令的Flash,又有存数据的SRAM,并且通过不同的总线去访问它们。

1.4 STM32的三条核心总线:ICode、DCode与System

基于哈佛架构的思想,ARM Cortex-M3内核设计了三条主要的总线接口,分别对应不同的任务,这也是我们理解MCU内部数据流向的关键。
在这里插入图片描述

1.4.1 ICode总线:指令专用通道

  • 全称:Instruction Code Bus(指令代码总线)。
  • 作用:这是一条“VIP通道”,专门负责从程序存储器(Flash)中读取指令,然后交给CPU去执行。
  • 特点:因为它只干这一件事,所以速度极快,且独占带宽。当你运行代码时,CPU就是通过这条线源源不断地把指令“吸”进来的。

1.4.2 DCode总线:数据专用通道(针对Flash)

  • 全称:Data Code Bus(数据代码总线)。
  • 作用:主要负责从Flash中读取常量数据
  • 场景解析:大家在写代码时定义的const变量(比如const int val = 100;),这些只读数据是存在Flash里的。当CPU需要用到这个常数时,就会通过DCode总线去Flash里把它读出来。
  • 注意:虽然它主要连Flash,但在某些特定调试场景下也能访问其他区域,但核心职责就是搬运Flash里的“只读数据”。

1.4.3 System总线:系统通用通道

  • 全称:System Bus(系统总线)。
  • 作用:这是一条“万能总线”,负责处理除了取指令和读Flash常量之外的所有事情。
  • 具体任务
    1. 访问SRAM:读写全局变量、局部变量(栈)、堆内存等。
    2. 访问外设:控制GPIO、配置定时器等。
    3. 访问外部存储器:通过FSMC等接口扩展的外部设备。
  • 连接方式:System总线连接到总线矩阵,再通过AHB(高速)和APB(低速)总线桥接器,最终连接到各个具体的外设上。

在这里插入图片描述

1.5 高速与低速的分层设计:AHB与APB

有了System总线后,面对速度差异巨大的外设(比如高速的SRAM和低速的串口),如果都用同一种速度通信,要么浪费高性能,要么拖慢整体节奏。因此,ST引入了分层设计。
在这里插入图片描述

1.5.1 AHB:高速公路

  • 全称:Advanced High-performance Bus(高级高性能总线)。
  • 定位:这是系统的主干道,类似于城市的主环路或高速公路。
  • 连接对象:连接那些对速度要求高的设备,如SRAM、Flash接口、DMA以及总线矩阵本身。
  • 特点:频率高,带宽大,支持突发传输。

1.5.2 APB:乡村公路

  • 全称:Advanced Peripheral Bus(高级外设总线)。
  • 定位:这是连接低速外设的支线,类似于通往各个小区的乡村公路。
  • 分类
    • APB2(相对高速):挂载了一些比较重要的、速度稍快的外设,比如GPIO(我们要用的点灯端口就在这里)、USART1、SPI1、ADC1等。它的最高时钟频率通常能达到72MHz(在标准配置下)。
    • APB1(低速):挂载了大量低速外设,如I2C、USART2/3、TIM2/3/4、CAN、USB等。它的最高时钟频率通常限制在36MHz。
  • 桥接器(Bridge):AHB和APB之间不是直接连的,而是通过“桥”连接。这个桥就像一个变速器协议转换器,把AHB的高速信号“降速”并转换为APB能听懂的协议信号,实现了不同速度域设备的无缝对接。

1.6 统一编址:软件视角的“大一统”

讲了这么多硬件连线,最后我们要回到软件开发者的视角。既然硬件上分了ICode、DCode、System,分了AHB、APB,那我们在写C语言代码时,岂不是要分别调用不同的驱动去访问?

答案是:不需要。这就是统一编址(Unified Addressing)的魅力。

1.6.1 什么是统一编址?

在ARM Cortex-M3看来,无论是内部的SRAM、Flash,还是外挂的GPIO、USART寄存器,本质上都是“存储空间”。

  • MCU内部有一个巨大的地址空间(对于32位CPU是4GB,即 2 32 2^{32} 232)。
  • ST工程师把这个4GB的空间像切蛋糕一样划分成了不同的区域(Block)。
  • 每个存储单元、每个外设寄存器,都被分配了一个唯一的、不重叠的物理地址
    在这里插入图片描述
    在这里插入图片描述

1.6.2 存储器映射图详解

大家可以看这张复杂的“存储器映射图”(Memory Map):

  • Block 0 (Code)0x0000 0000 - 0x1FFF FFFF。这里主要放Flash和SRAM。我们的代码和变量主要就在这儿。
  • Block 1 (SRAM)0x2000 0000 开始。这是内置SRAM的起始地址。
  • Block 2 (Peripherals)0x4000 0000 - 0x5FFF FFFF这里是重点! 所有的片上外设寄存器都映射在这个区域。
    • 比如 APB2总线 上的外设,起始地址通常在 0x4001 0000
    • GPIOF 的基地址就是 0x4001 1C00(举例,具体查手册)。
    • APB1总线 上的外设,起始地址通常在 0x4000 0000
      在这里插入图片描述
      在这里插入图片描述

1.6.3 对开发者的意义

因为做了统一编址,我们在C语言里操作外设就变得非常简单直接:

  1. 指针即地址:我们可以直接定义一个指针指向某个特定的物理地址(例如 0x40011C00)。
  2. 读写即操作:对这个地址进行写操作(*ptr = 1),硬件电路就会把这个电信号通过System总线 -> AHB -> APB2 -> GPIOF寄存器,最终点亮LED。
  3. 无感切换:你在代码里访问变量(读写SRAM)和访问寄存器(读写GPIO),语法上完全一样(都是解引用指针),底层硬件会自动判断该走哪条总线(SRAM走System->AHB,GPIO走System->AHB->APB),开发者完全不需要关心底层的总线切换细节。

1.7 总结与源码预告

综上所述,STM32的内部是一个精密的交通网络:

  • CPU是指挥中心。
  • ICode/DCode是读取程序和常量的专用快车道。
  • System总线是通往内存和外设的主干道。
  • AHB/APB是根据速度分级的高速路和辅路。
  • 统一编址则是这张交通网的地图,让CPU能通过唯一的坐标(地址)找到任何一个设备。

理解了这些,我们就明白了为什么之前写的点灯代码中,那个GPIOF其实代表的就是一串具体的物理地址。HAL库帮我们封装了这些地址,但如果我们要看透本质,就必须去看源码里的地址定义。

在下篇文章中,我们将正式切入源码分析环节,看看这些理论上的地址是如何在代码中被定义和使用的,彻底解决“地址从哪里来”的问题。

结语

在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐