📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨
📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】
📢:文章若有幸对你有帮助,可点赞 👍 收藏 ⭐不迷路🙉
📢:内容若有错误,敬请留言 📝指正!原创文,转载注明出处
文章目录
- 什么是存储器映射呢?
- 为什么叫存储器映射,而不是寄存器映射呢?
- 存储器映射实现
- 什么是寄存器映射?
- 寄存器映射实现
- 为什么要设置存储器映射?
- stm32单片机地址总线访问大小是4G,是不是意味着内存大小也是4G?
什么是存储器映射呢?
存储器本身不具备地址,所以把芯片内核所预先设定好的地址分配给寄存器,就是存储器映射。因为stm32的地址线是32位,也就是2的32次方,正好是对应4G的虚拟存储空间。把内核厂商(也就是ARM公司)定义的这个虚拟空间与芯片厂商(这里是ST)芯片内部外设进行对应,也就是给存储器分配地址,即存储器映射。4个G的地址这么大,用不完没关系,可以保留。
为什么叫存储器映射,而不是寄存器映射呢?
在 STM32 单片机中,“存储器映射” 是指将不同的物理存储区域(如内部闪存、SRAM、外设寄存器等)和一些逻辑存储区域(如系统保留区域等)按照一定的规则,映射到一个统一的地址空间中,使得 CPU 可以通过访问这个统一地址空间中的不同地址来访问不同的物理或逻辑存储区域。因此存储器映射包含寄存器映射。
存储器映射实现
请查看存储器映射关系在代码中是如何体现的。下图是我项目中的 stm32f4xx.h 文件的部分代码。
#define FLASH_BASE ((uint32_t)0x08000000) /*!< FLASH(up to 1 MB) base address in the alias region */
#define CCMDATARAM_BASE ((uint32_t)0x10000000) /*!< CCM(core coupled memory) data RAM(64 KB) base address in the alias region */
#define SRAM1_BASE ((uint32_t)0x20000000) /*!< SRAM1(112 KB) base address in the alias region */
#define SRAM2_BASE ((uint32_t)0x2001C000) /*!< SRAM2(16 KB) base address in the alias region */
#define SRAM3_BASE ((uint32_t)0x20020000) /*!< SRAM3(64 KB) base address in the alias region */
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define BKPSRAM_BASE ((uint32_t)0x40024000) /*!< Backup SRAM(4 KB) base address in the alias region */
#if defined (STM32F40_41xxx)
#define FSMC_R_BASE ((uint32_t)0xA0000000) /*!< FSMC registers base address */
#endif /* STM32F40_41xxx */
#if defined (STM32F427_437xx) || defined (STM32F429_439xx)
#define FMC_R_BASE ((uint32_t)0xA0000000) /*!< FMC registers base address */
#endif /* STM32F427_437xx || STM32F429_439xx */
#define CCMDATARAM_BB_BASE ((uint32_t)0x12000000) /*!< CCM(core coupled memory) data RAM(64 KB) base address in the bit-band region */
#define SRAM1_BB_BASE ((uint32_t)0x22000000) /*!< SRAM1(112 KB) base address in the bit-band region */
#define SRAM2_BB_BASE ((uint32_t)0x2201C000) /*!< SRAM2(16 KB) base address in the bit-band region */
#define SRAM3_BB_BASE ((uint32_t)0x22400000) /*!< SRAM3(64 KB) base address in the bit-band region */
#define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */
#define BKPSRAM_BB_BASE ((uint32_t)0x42024000) /*!< Backup SRAM(4 KB) base address in the bit-band region
/*!< Peripheral memory map */
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000)
#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000)
/*!< APB1 peripherals */
#define TIM2_BASE (APB1PERIPH_BASE + 0x0000)
#define TIM3_BASE (APB1PERIPH_BASE + 0x0400)
#define TIM4_BASE (APB1PERIPH_BASE + 0x0800)
#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00)
#define TIM6_BASE (APB1PERIPH_BASE + 0x1000)
#define TIM7_BASE (APB1PERIPH_BASE + 0x1400)
#define TIM12_BASE (APB1PERIPH_BASE + 0x1800)
#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00)
#define TIM14_BASE (APB1PERIPH_BASE + 0x2000)
#define RTC_BASE (APB1PERIPH_BASE + 0x2800)
#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00)
#define IWDG_BASE (APB1PERIPH_BASE + 0x3000)
#define I2S2ext_BASE (APB1PERIPH_BASE + 0x3400)
#define SPI2_BASE (APB1PERIPH_BASE + 0x3800)
#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00)
#define I2S3ext_BASE (APB1PERIPH_BASE + 0x4000)
什么是寄存器映射?
在存储器Block2这块区域,设计的是片上外设,它们以4个字节为一个单元,共32bit,那么每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过C语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这是我们可以根据每个单元的功能不同,以功能为名给这个存储单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
寄存器映射实现
第一步:宏定义GPIO 口的基地址,AHB1PERIPH_BASE 依次累加 0x400的地址偏移量,就得到GPIOA~GPIOK的基地址。
#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000)
#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400)
#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800)
#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00)
#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000)
#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400)
#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800)
#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00)
#define GPIOI_BASE (AHB1PERIPH_BASE + 0x2000)
#define GPIOJ_BASE (AHB1PERIPH_BASE + 0x2400)
#define GPIOK_BASE (AHB1PERIPH_BASE + 0x2800)
第二步:把这个GPIO的基地址通过加上(GPIO_TypeDef *)这步骚操作,来把地址强转成具有GPIO_TypeDef 性质的指针变量,并且用#define进行宏定义,实现取了个别名的效果。这就是寄存器的映射。
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)
#define GPIOI ((GPIO_TypeDef *) GPIOI_BASE)
#define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE)
#define GPIOK ((GPIO_TypeDef *) GPIOK_BASE)
GPIO_TypeDef结构体的声明:
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */
__IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
} GPIO_TypeDef;
为什么要设置存储器映射?
之所以这样做,有以下几个原因:
- 统一访问接口:CPU通过地址总线访问内存和外设等不同部件时,采用统一的存储器映射方式,就可以使用相同的指令和操作来进行数据的读写。例如,无论是访问内部SRAM中的数据,还是向外设寄存器写入控制命令,都可以通过对相应地址的操作来完成,无需为不同的部件设计不同的访问指令和接口,简化了硬件设计和软件开发的复杂度。
- 灵活的资源分配:存储器映射可以根据不同的应用需求,灵活地将地址空间分配给各种不同的存储介质和外设。例如,在设计一个具体的产品时,可以根据实际需要,将一部分地址空间分配给外部扩展的大容量Flash存储器用于存储程序和数据,将另一部分地址空间分配给特定的外设,如网络控制器、SD卡控制器等,使系统能够高效地利用各种资源,满足不同的功能需求。
- 方便系统扩展:当需要对系统进行扩展时,无论是增加新的存储设备还是添加新的外设,都可以通过合理地分配存储器映射地址来实现。新的设备可以很容易地集成到现有的系统中,只需将其映射到未使用的地址空间,并编写相应的驱动程序来访问这些地址即可,而无需对整个系统的架构进行大规模的修改。
stm32单片机地址总线访问大小是4G,是不是意味着内存大小也是4G?
STM32单片机地址总线访问大小是4G,但这并不意味着其内存大小就是4G。
地址总线的宽度决定了CPU可以访问的地址空间范围。STM32单片机基于ARM Cortex - M内核,其具有32位地址总线,所以理论上可访问的地址空间为(2^{32})= 4G字节。然而,这4G的地址空间是包括了多个部分的,不仅仅是内存(RAM和ROM),还包括以下部分:
- 片上外设寄存器空间:用于访问各种片上外设,如GPIO、USART、SPI等外设的控制寄存器。每个外设都有其特定的地址范围,通过地址总线来访问这些寄存器以实现对外设的控制和数据传输。
- 外部设备扩展空间:如果单片机扩展了外部的Flash、RAM、FPGA等设备,这些设备也会占用一定的地址空间,与片上资源共同构成整个可寻址的4G空间。
- 系统保留区域:一些地址范围可能被保留用于特定的系统功能或未来扩展,并不对应实际的物理存储或外设。
实际上,STM32单片机内部的内存(如SRAM和Flash)容量通常远小于4G。不同型号的STM32其内部SRAM一般在几十KB到几百KB之间,内部Flash存储器一般在几十KB到几MB之间。