📢:如果你也對機器人、人工智能感興趣,看來我們志同道合?
📢:不妨瀏覽一下我的博客主頁【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之間。