STM32的存储器和寄存器

01 存储器分类

在讲STM32的存储器前,我们首先了解下存储器类别,下面是常见的存储器介绍。

img

RAM

Random Access Memory,随机存取存储器。是与CPU直接交换数据的内部存储器。它可以随时读写(刷新时除外),而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储介质。

一旦断电所存储的数据将随之丢失。

SRAM

Static Random-Access Memory,静态随机存取存储器。是RAM的一种,所谓的“静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持。

集成度较DRAM低,SRAM一般应用于高速缓存(Level2 Cache)。

DRAM

Dynamic Random Access Memory,动态随机存取存储器。是RAM的一种,所谓的“动态”,是指这种存储器存储的数据想要保持,就需要周期性地更新里面所储存的数据。

集成度较SRAM高,一般应用于内存条中。

ROM

Read-Only Memory,只读存储器。是一种存储固定信息的存储器,在正常工作状态下只能读取数据,不能即时修改或重新写入数据。

ROM的最大优点是具有不易失性。

EEPROM

Electrically Erasable Programmable Read Only Memory,带电可擦可编程只读存储器。是可更改的只读存储器(ROM),其可通过高于普通电压的作用来擦除和重写。

常用于存放硬件设置数据,如PC的BIOS。

OTP

One Time Programmable,一次性可编程存储器。数据写入后,将不可再次更改和清除。

常用于写入产品和安全信息。

FLASH

Flash是一种擦写型存储器。Flash不像RAM一样需要电源支持才能保存,但又像RAM一样可重写。在某个级别的低电压下,Flash的内部信息可读不可写,类似于ROM,而在较高的电压下,其内部信息可以更改和删除,又类似于RAM。

1.在单片机应用中,一般用作存储程序代码。

2.注意上述这个“块”字,Flash的擦除操作是以block块为单位的,进行操作时需要留意字节和地址等对齐问题。

02 Cortex-M4的存储器映射

Cortex-M4的存储器会用到FLASH和SRAM,片外RAM可能还会涉及DRAM。Cortex-M4提供了4GB的可寻址空间,包括:

  1. 代码空间;

  2. 片内SRAM;

  3. 片内外设;

  4. 片外RAM;

  5. 片外外设;

  6. 系统级空间。

在系统级空间中保护NVIC、SysTick、MPU等。同时在片内SRAM和片内外设空间存在2MB的“位带区”,支持“位带”(bit-band)操作。

img

额外提一点,Cortex-M4内核支持小端(Little-Endian)模式和大端(Big-Endian)模式,但默认采用小端模式,即字的最低位(LSB)位于低地址字节。

建议总线、外设和数据的设计都统一采用小端模式,避免不必要的麻烦。

img

03 STM32的存储器映射

在讲存储器映射前,先来看下STM32的架构和存储器构成,以下以F407xx系列为例。

系统架构

讲具体映射之前,我们在系统架构中先了解一个概念。从图中可看到主控总线通过一个总线矩阵来连接被控总线。比如数据从SRAM 到DMA1外设,那么数据在交给总线矩阵后,总线矩阵就会仲裁给DMA1,然后通过DMA1所在的 AHB1 传递过去。

img

这里我们只要知道,存储器和外设的数据交互是通过总线的即可。

存储器构成

STM32F407xx系列的存储器有:

  1. 片内SRAM;
  2. 片内Flash;
SRAM

STM32F407xx系列拥有最多高达196KB的SRAM,包括4KB的备份SRAM(电池备份区)和192KB的系统SRAM。

系统SRAM可按字节、半字(16bit)或全字(32bit)访问。读写操作以CPU速度执行,且等待周期为0。系统SRAM共分为三个块:

  1. 映射在地址0x2000 0000到0x2001 FFFF的112KB和16KB块,可供所有AHB主控总线访问;
  2. 映射在地址0x1000 0000到0x1000 FFFF的64KB块,只能提供给CPU通过数据总线访问。

注意:0x1000 0000到0x1000 FFFF的64KB块只能由I-CODE总线访问,DMA是访问不了的。如果IDE默认设置了0x1000 0000 ~ 0x1000 FFFF的数据块为优先访问,这时如果要使用DMA,则需要将其更改为0x2000 0000 ~ 0x2001 FFFF的数据块。

Flash

STM32F407xx系列拥有最多高达1MB的Flash容量,支持的操作:

  1. 128位宽数据读取;
  2. 字节、半字、字和双字数据写入;
  3. 扇区擦除和全部擦除。

Flash的结构:

  1. 主存储器
    包括4个16KB扇区、1个64KB扇区和7个128KB扇区。
  2. 系统存储器
    30KB大小,系统bootloader时从该存储器启动。
  3. OTP存储器
    512字节的OTP,只能一次性编程(写0),用于存储用户数据。另外有16字节,用于锁定对应的OTP数据块。
  4. 选项字节
    用于配置读写保护、BOR级别、软硬件看门狗以及器件处于待机或停止模式下的复位。

image-20220413184130813

随意打开一个STM32F407IGxx的工程,我们可以看到配置里配置了:

  1. FLASH起始地址0x8000 0000和大小0x100000(1MB);
  2. 系统SRAM起始地址0x2000 0000和大小0x20000(128KB)。

img

存储器映射

什么叫存储器映射呢?存储器本身并不具备地址信息,那么CPU要准确找到存储某个信息的存储单元,就必须为这些单元分配一个相互可区分的标识,这个标识就是常说的地址编码。

而STM32中集成多种存储器,同一类型的存储器当作一组block,为每一个block分配一个数值连续,存储单元数相等,以16进制表示的自然数集合作为存储器Block的地址编码。这种自然数集合与存储器Block的对应关系就是存储器映射

将芯片理论上的地址分配给存储器,这就叫作存储器映射。

img

存储器重映射

通常MCU启动都是从0x0000 0000地址处开始,但是MCU为了支持不同的存储介质(FLASH、SRAM等),不同的存储介质被分配到了一个非0地址区域。如果想让MCU从不同存储介质处启动(运行程序),就要进行重映射。

单片机的启动也可以叫做“自举”(bootstrap)。

而通过单片机的自举,我们可以:

  1. 进行系统bootloader(ISP);
  2. 让程序代码在RAM(执行速度快)中先进行调试,待调试完成后再写入Flash中
自举模式和重映射

STM32F4xx系列中,可通过BOOT[1:0]引脚这种硬件机制来选择不同的自举模式。

image-20220414082628142

也可以通过SYSCFG的存储器重映射寄存器(SYSCFG memory remap register)来配置存储器重映射。

例如自举模式选择主Flash作为自举空间,那么0x0800 0000 - 0x080F FFFF这段存储空间就被重映射到0x0000 0000地址开始的代码空间中。

STM32的存储器采用固定的存储器映射,代码区域起始地址为 0x0000 0000(通过 ICode/DCode 总线访问),而数据区域起始地址为 0x2000 0000(通过系统总线访问)。

04 STM32的寄存器映射

什么叫寄存器映射?

存储器映射章节中,我们知道STM32F407xx大部分外设都在0x4000 0000 - 0xA000 0FFF地址中,这些地址以四个字节为一个单元,共32bit。每一个单元对应不同的功能,当我们控制这些单元时就可以驱动相应外设工作。我们可以通过找到每个单元的起始地址,然后通过指针的操作方式来访问这些单元。

但如果每次访问都是通过这种地址的方式,不仅不好记忆还容易出错,这时可以根据每个单元功能的差异,以功能为名给这个内存地址单元取一个别名,这个别名就是常说的寄存器。这个给已经分配好地址的、有特定功能的内存单元取别名的过程就叫寄存器映射。

下表是STM32F407xx的寄存器映射。

image-20220414083231600

image-20220414083345586

image-20220414083321503

image-20220414083451147

下面举一个操作GPIOC数据输出寄存器的例子。

由总线地址映射表得知,GPIOC外设挂在AHB1总线。

img

实现的代码:

/** 外设基地址*/
#define PERIPH_BASE           ((uint32_t)0x40000000)
/** AHB1的地址*/
#define AHB1PERIPH_BASE       (PERIPH_BASE + 0x00020000)
/** GPIOC的地址*/
#define GPIOC_BASE            (AHB1PERIPH_BASE + 0x0800)

#define GPIOC                ((GPIO_TypeDef *) GPIOC_BASE)

/** GPIOx 寄存器*/
typedef struct
{
  __IO uint32_t MODER;    /*!< Address offset: 0x00      */
  __IO uint32_t OTYPER;   /*!< Address offset: 0x04      */
  __IO uint32_t OSPEEDR;  /*!< Address offset: 0x08      */
  __IO uint32_t PUPDR;    /*!< Address offset: 0x0C      */
  __IO uint32_t IDR;      /*!< Address offset: 0x10      */
  __IO uint32_t ODR;      /*!< Address offset: 0x14      */
  __IO uint16_t BSRRL;    /*!< Address offset: 0x18      */
  __IO uint16_t BSRRH;    /*!< Address offset: 0x1A      */
  __IO uint32_t LCKR;     /*!< Address offset: 0x1C      */
  __IO uint32_t AFR[2];   /*!< Address offset: 0x20-0x24 */
} GPIO_TypeDef;

/** 操作GPIOC数据输出寄存器*/
GPIOC->ODR = 0x00000000;

这里扩展下另一种定义和操作寄存器的方法。在ODR代码中可以看到使用了联合体嵌套结构体的方法,将32bit ODR寄存器按bit的方式定义,这样可以直接访问到bit,方便进行精细化操作。

/** 外设基地址*/
#define PERIPH_BASE           ((uint32_t)0x40000000)
/** AHB1的地址*/
#define AHB1PERIPH_BASE       (PERIPH_BASE + 0x00020000)
/** GPIOC的地址*/
#define GPIOC_BASE            (AHB1PERIPH_BASE + 0x0800)

#define GPIOC                ((GPIO_TypeDef *) GPIOC_BASE)

/** GPIOx 寄存器*/
typedef struct
{
    union
    {
        __IO uint32_t MODER;    /*!< Address offset: 0x00      */

        struct
        {
            ...           
        } MODER_B;
        
    };

    union
    {
        __IO uint32_t OTYPER;   /*!< Address offset: 0x04      */

        struct
        {
            ...           
        } OTYPER_B;
    };

    union
    {
        __IO uint32_t OSPEEDR;  /*!< Address offset: 0x08      */

        struct
        {
            ...           
        } OSPEEDR_B;
    };

    union
    {
        __IO uint32_t PUPDR;    /*!< Address offset: 0x0C      */

        struct
        {
            ...           
        } PUPDR_B;
    };

    union
    {
        __IO uint32_t IDR;      /*!< Address offset: 0x10      */

        struct
        {
            ...           
        } IDR_B;
    };

    union
    {
        __IO uint32_t ODR;      /*!< Address offset: 0x14      */

        struct
        {
            __IO uint32_t ODR0                  : 1;
            __IO uint32_t ODR1                  : 1;
            __IO uint32_t ODR2                  : 1;
            __IO uint32_t ODR3                  : 1;
            __IO uint32_t ODR4                  : 1;
            __IO uint32_t ODR5                  : 1;
            __IO uint32_t ODR6                  : 1;
            __IO uint32_t ODR7                  : 1;
            __IO uint32_t ODR8                  : 1;
            __IO uint32_t ODR9                  : 1;
            __IO uint32_t ODR10                 : 1;
            __IO uint32_t ODR11                 : 1;
            __IO uint32_t ODR12                 : 1;
            __IO uint32_t ODR13                 : 1;
            __IO uint32_t ODR14                 : 1;
            __IO uint32_t ODR15                 : 1;
            __IO uint32_t RESERVED              : 16;
        } ODR_B;
    };
    ......
    ......
    ......
} GPIO_TypeDef;

/** 操作GPIOC数据输出寄存器*/
GPIOC->ODR_B.ODR0 = 0;

注意Cortex-M4默认小端模式。

05 参考文献

  1. 百度百科
  2. [Cortex-M3权威指南]
  3. [Cortex-M4权威指南]
  4. STM32F407参考手册
  5. STM32F407产品规格书
  6. STM32F407存储区映射
  7. STM32内存映射
  • 5
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MorroMaker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值