欢迎光临
我们一直在努力

【STM32】STM3基础知识2--深入理解STM32F1系列存储器映射

  “你有没有过这样的困惑?为什么操作STM32的GPIO只需要写GPIOA->ODR |= 1<<5?为什么往某个地址写数据,就能控制PA5引脚的LED亮灭?这一切的背后,都是存储器映射在起作用。”   本文将从基础概念出发,一步步拆解STM32的存储器映射,帮你理清地址空间的逻辑,为后续编程打下坚实基础。

目录

  • 一、什么叫存储器映射?
  • 二、STM32 存储器映射框架
  • 三、STM32的具体存储器映射详解
    • 3.1 Block0:代码与Flash存储区(0x00000000 ~ 0x1FFFFFFF)
    • 3.2 Block1:SRAM数据存储区(0x20000000 ~ 0x3FFFFFFF)
    • 3.3 Block2:片上外设寄存器区(0x40000000 ~ 0x5FFFFFFF)
  • 四、Cortex-M特色:位带操作
  • 五、从存储器映射到寄存器映射:编程实战

一、什么叫存储器映射?


  STM32 的存储器映射,简单来说就是把芯片内部所有的硬件资源(Flash、SRAM、外设寄存器、总线接口等),统一分配到一个固定的 32 位地址空间(0x00000000 ~ 0xFFFFFFFF)中,给每个资源分配唯一的地址段,让 CPU(Cortex-M3 内核)能通过访问地址的方式,像操作内存一样操作 Flash、SRAM,以及控制外设、读取寄存器数据。   可以把这个过程理解为:给 STM32 内部的所有 “硬件部件” 分配唯一的地址门牌号,CPU 不需要区分 “这是内存”“这是外设”,只需要通过门牌号(地址)访问,就能实现数据读写或硬件控制,这也是 STM32寄存器级编程的核心底层原理。

  • 所有硬件资源(存储、外设、内核外设)都被视为 “可寻址的存储器单元”;
  • CPU 通过AHB/APB 总线访问这些地址,地址与硬件资源一一对应,无重复;
  • 对地址的写操作 = 向硬件资源发送控制指令(如配置外设寄存器);
  • 对地址的读操作 = 从硬件资源获取数据(如读取 ADC 采样值、GPIO 电平状态)。

  • 二、STM32 存储器映射框架


      STM32F1 系列的存储器映射是分层划分的,先按资源类型划分大的地址段,再在大段内按具体硬件划分子地址段,和之前给你的地址映射表完全对应,核心分为全局地址空间划分和外设寄存器的地址细分两层:

    在这里插入图片描述   STM32 的 32 位地址空间范围是 0x00000000 ~ 0xFFFFFFFF(总计 4GB),按512MB / 块划分,正好是 8 块,划分依据是「资源类型 + 访问优先级 + 扩展用途」,每一块都有明确的功能定位,其中只有前 4 块涉及常规开发,后 4 块多为保留、内核专用或扩展用途。

    8 个 512MB 地址块详细功能解析

    按地址从低到高排序,每一块的地址范围、功能定位、核心细节如下:

    块序号地址范围大小核心功能定位详细解析 & 开发相关说明
    第 1 块 0x00000000 ~ 0x1FFFFFFF 512MB 代码区(Code) 专门用于存储程序代码、常量数据、Bootloader,是系统启动和程序运行的核心区域,支持多种存储介质映射:
    第 2 块 0x20000000 ~ 0x3FFFFFFF 512MB 片内 SRAM 区(SRAM) 专门用于映射易失性存储介质,核心是片内 SRAM,也支持外部扩展 SRAM:
    第 3 块 0x40000000 ~ 0x5FFFFFFF 512MB 片上外设寄存器区(Peripherals) 专门用于映射STM32 内部所有片上外设的寄存器,是软件控制硬件的核心区域:
    第 4 块 0x60000000 ~ 0x7FFFFFFF 512MB 外部扩展存储区(External Memory) 专门用于外部大容量存储设备的扩展,主要针对 STM32 的互联型 / 高性能型号:
    第 5 块 0x80000000 ~ 0x9FFFFFFF 512MB 保留区(Reserved) 核心用途:ST 官方预留的地址空间,用于后续芯片型号的功能扩展、新外设映射,保证 STM32 系列产品的地址兼容性;
    第 6 块 0xA0000000 ~ 0xBFFFFFFF 512MB 保留区(Reserved) 与第 5 块功能一致,属于全局预留扩展空间:
    第 7 块 0xC0000000 ~ 0xDFFFFFFF 512MB 内核专用外设区(Core Peripherals) 专门用于映射ARM Cortex-M3 内核内置的专用外设(非 ST 扩展外设),是内核运行的支撑区域:
    第 8 块 0xE0000000 ~ 0xFFFFFFFF 512MB 系统调试与跟踪区(Debug & Trace) 专门用于系统调试、数据跟踪、调试器交互,仅在开发调试阶段生效:

    关键补充说明(新手必看)

  • 开发重点聚焦前 3 块:对于 STM32F103 的常规开发(入门、工业控制、创客项目),只需关注第 1 块(片内 Flash)、第 2 块(片内 SRAM)、第 3 块(片上外设寄存器),这三块覆盖了程序存储、数据运行、外设控制的全部核心需求,后 5 块仅在高端扩展或调试时涉及。
  • 每块 512MB 的划分逻辑:ST 采用 512MB / 块的划分,是兼顾「地址空间充裕性」和「资源分类清晰性」,32位地址空间(4GB)足够支撑未来数十年的芯片扩展,同时按功能分块让开发者更容易记忆和定位资源。
  • 兼容性保障:8 块的顶层划分在整个 STM32 系列(F1/F4/F7/H7/L4)中保持一致,仅内部具体资源的地址子段有差异,这也是STM32 跨系列移植的核心基础之一。

  • 三、STM32的具体存储器映射详解


      ST公司在Cortex-M框架下,对STM32的地址空间做了更细致的划分,以STM32F1系列为例,重点关注前3个核心区块:

    3.1 Block0:代码与Flash存储区(0x00000000 ~ 0x1FFFFFFF)

      这部分对应片上Flash,是程序的“永久仓库”,断电数据不丢失,细分如下:

    • 用户Flash区:起始地址0x08000000,存放用户编写的程序代码和const常量。不同型号容量不同(比如F103C8T6是64KB,结束地址0x0800FFFF),结束地址公式为:0x08000000+Flash容量。
    • 系统存储器区:起始地址0x1FFFF000(F1系列),出厂固化Bootloader程序,用于串口下载代码等功能,用户一般无需修改。
    • 选项字节区:0x1FFFF800 ~ 0x1FFFF80F,共16字节,用于配置读保护、BOR级别等芯片底层参数。
    • 启动别名区:0x00000000 ~ 0x0007FFFF,根据BOOT引脚配置,会映射到用户Flash、系统存储器或SRAM,实现不同启动模式。

    3.2 Block1:SRAM数据存储区(0x20000000 ~ 0x3FFFFFFF)

      SRAM是芯片的“临时工作台”,用于存储运行时的变量、堆栈、中间计算数据,断电后数据立即丢失。

    • 所有STM32的SRAM起始地址固定为0x20000000,结束地址公式为0x20000000 + SRAM容量(比如F103C8T6是20KB,结束地址0x20004FFF)。
    • 这部分空间包含位带区,支持直接位操作(下文详细讲解)。

    3.3 Block2:片上外设寄存器区(0x40000000 ~ 0x5FFFFFFF)

      STM32的所有外设(GPIO、定时器、UART、ADC等)都通过寄存器控制,这些寄存器被映射到这个地址区块,CPU通过访问地址来操作外设。 根据总线速度,这部分划分为3类总线区域:

    总线类型基地址挂载外设类型
    APB1 0x40000000 低速外设(UART2-5、I2C、TIM2-7等)
    APB2 0x40010000 高速外设(GPIOA-G、UART1、TIM1等)
    AHB 0x40018000 高性能外设(DMA、RCC、FSMC等)

      外设的寄存器都集中在 APB1/APB2 的地址段内,每个外设分配独立的地址子段(通常为 0x400),方便按地址直接访问,以下是 F103 最常用的外设子地址:

    a、APB2 外设子地址(0x40000000 ~ 0x40007FFF,72MHz)

    外设名称地址范围核心用途
    GPIOA 0x40010800 ~ 0x40010BFF A 组通用 IO 口寄存器(控制 PA0~PA15)
    GPIOB 0x40010C00 ~ 0x40010FFF B 组通用 IO 口寄存器
    GPIOC 0x40011000 ~ 0x400113FF C 组通用 IO 口寄存器
    GPIOD 0x40011400 ~ 0x400117FF D 组通用 IO 口寄存器
    GPIOE 0x40011800 ~ 0x40011BFF E 组通用 IO 口寄存器(部分型号有)
    ADC1 0x40012400 ~ 0x400127FF 12 位模数转换器 1 寄存器
    TIM1 0x40012C00 ~ 0x40012FFF 高级定时器 1 寄存器(PWM / 定时)
    USART1 0x40013800 ~ 0x40013BFF 串口 1 寄存器(高速通信)
    EXTI 0x40010400 ~ 0x400107FF 外部中断控制器寄存器
    RCC(APB2 部分) 0x40010000 ~ 0x400103FF 时钟控制寄存器(APB2 外设时钟使能)

    b、APB1 外设子地址(0x40010000 ~ 0x40023FFF,36MHz)

    外设名称地址范围核心用途
    TIM2~TIM7 0x40000000 ~ 0x400017FF 通用定时器 2~7 寄存器
    UART2/UART3 0x40004400 ~ 0x40004BFF 串口 2/3 寄存器(低速通信)
    I2C1/I2C2 0x40005400 ~ 0x40005BFF I2C 总线 1/2 寄存器
    SPI2/SPI3 0x40003800 ~ 0x40003FFF SPI 总线 2/3 寄存器
    CAN1/CAN2 0x40006400 ~ 0x400073FF CAN 总线控制器寄存器(部分型号有)
    USB 0x40005C00 ~ 0x40005FFF USB 全速设备寄存器
    RCC(APB1 部分) 0x40021000 ~ 0x400213FF 时钟控制寄存器(APB1 外设时钟使能)
    PWR 0x40007000 ~ 0x400073FF 电源管理模块寄存器
    RTC 0x40002800 ~ 0x40002BFF 实时时钟寄存器

    四、Cortex-M特色:位带操作


      位带操作是Cortex-M内核的一大亮点,能让你像操作普通内存一样直接控制单个bit,无需传统的“读-改-写”三步操作,简化代码同时提升效率。

    位带区分为两部分:

    • SRAM位带区:0x20000000 ~ 0x200FFFFF(低1MB SRAM),对应位带别名区0x22000000 ~ 0x23FFFFFF(32MB)
    • 外设位带区:0x40000000 ~ 0x400FFFFF(低1MB外设寄存器),对应位带别名区0x42000000 ~ 0x43FFFFFF(32MB)

      核心原理:位带区的每一个bit,都对应位带别名区的一个32位字。比如要操作SRAM地址0x20000000的第0位,直接写入位带别名区地址0x22000000即可,无需先读整个字节再修改。

    1、SRAM位带区转换公式

      对于SRAM位带区的某个位,记它所在字的地址为 A,位序号为 n(0<=n<=31),则该位在别名区的地址为:          Addr =0x22000000+ (A-0x20000000)84 +n*4

      0X22000000 是SRAM位带别名区的起始地址,0x20000000 是SRAM位带区的起始地址,(A-0x20000000)表示有多少个字节,一个字节有8 位,所以8,一个位膨胀后是4 个字节,所以4,n 表该位在A 地址的序号,因为一个位经过膨胀后是四个字节,所以*4。

    2、外设位带区转换公式

      对于片上外设位带区的某个位,记它所在字的地址为 A,位序号为 n(0<=n<=31),则该位在别名区的地址为:          Addr=0x42000000+ (A-0x40000000)84 +n*4

      0X42000000 是外设位带别名区的起始地址,0x40000000 是外设位带区的起始地址,(A-0x40000000)表示有多少个字节,一个字节有8 位,所以8,一个位膨胀后是4 个字节,所以4,n 表该位在A 地址的序号,因为一个位经过膨胀后是四个字节,所以*4。

    3、统一的公式:   对于片上外设位带区的某个位或SRAM位带区的某个位,记它所在字的地址为 A,位序号为 n(0<=n<=31),则该位在别名区的地址为:

    Addr =((A & 0xF0000000)+0x02000000+((A & 0x000FFFFF)<<5)+(n<<2)


    五、从存储器映射到寄存器映射:编程实战


      了解存储器映射后,寄存器映射是编程的关键——给已经分配好地址的外设寄存器取一个易读的别名,比如把GPIOA的输出数据寄存器地址0x4001080C命名为GPIOA->ODR。

  • 寄存器地址计算 寄存器地址 = 外设基地址 + 寄存器偏移量 比如GPIOA的ODR寄存器: GPIOA外设基地址:0x40010800 ODR寄存器偏移:0x0C 最终地址:0x40010800+ 0x0C = 0x4001080C

  • 结构体封装寄存器 在STM32标准库或HAL库中,通常用结构体封装外设寄存器,让代码更简洁直观:

  • typedef struct {
    volatile uint32_t CRL; // 端口配置低寄存器 4个字节
    volatile uint32_t CRH; // 端口配置高寄存器
    volatile uint32_t IDR; // 端口输入数据寄存器
    volatile uint32_t ODR; // 端口输出数据寄存器
    volatile uint32_t BSRR; // 端口位设置/清除寄存器
    volatile uint32_t BRR; // 端口位清除寄存器
    volatile uint32_t LCKR; // 端口配置锁定寄存器
    } GPIO_TypeDef;

    #define GPIOA ((GPIO_TypeDef *)0x40010800) // 把地址强制转换为结构体指针

      这段代码的核心作用是:将 GPIOA 外设的 7 个核心寄存器,按 ST 官方规定的地址偏移顺序,封装成一个结构体,再将结构体映射到 GPIOA 的硬件基地址(0x40010800)上,最终通过GPIOA->xxx(如GPIOA->ODR)的方式,直接访问 GPIOA 的对应寄存器,实现对 GPIOA 引脚的配置和控制。   GPIOA->ODR |= 1<<5 该程序是将0x40010800(GPIO A口基地址)+0x0c(ODR寄存器偏移地址)的第5位置1,实现控制PB5引脚的LED亮灭。 在这里插入图片描述


      到这里,STM32存储器映射的核心逻辑就拆解完毕啦。如果这篇内容帮你打通了地址空间的“任督二脉”,麻烦点个赞告诉我——你的认可,是我持续输出嵌入式硬核干货的最大动力! 想要学习更多嵌入式知识请关注:芦苇电子 微信公众号

    赞(0)
    未经允许不得转载:171主机测评 » 【STM32】STM3基础知识2--深入理解STM32F1系列存储器映射
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址