1、stm32存儲(chǔ)器映射概述
如果說時(shí)鐘是單片機(jī)的心臟,那么存儲(chǔ)器映射就像是單片機(jī)的神經(jīng)網(wǎng)絡(luò),傳輸大腦中樞(cpu)到各個(gè)外設(shè)的神經(jīng)信號(hào)。由此可知,這塊的知識(shí)點(diǎn)在整個(gè)單片機(jī)知識(shí)體系架構(gòu)里面,也是非常重要的。
可能有一部分人會(huì)覺得,現(xiàn)在我們都是基于標(biāo)準(zhǔn)庫、HALL庫,甚至LL庫來進(jìn)行開發(fā),所有的資源都是函數(shù)庫封裝好的,直接調(diào)用非常方便。對(duì)于這些底層的東西,我們沒有必要還像學(xué)習(xí)51單片機(jī)那樣,來研究它了。
然而我想說,事實(shí)并不是這樣的。對(duì)于一些比較簡單的項(xiàng)目,功能比較單一,代碼量不大項(xiàng)目,沒什么問題。甚至一些沒有用到特別復(fù)雜外設(shè)的工作量稍大的項(xiàng)目,也問題不大。一般常規(guī)的問題類似于我這篇文章所寫的內(nèi)容 :【基礎(chǔ)知識(shí)】單片機(jī)編程注意事項(xiàng)及易錯(cuò)點(diǎn)總結(jié)。大部分的bug都能夠得到解決。但是如果對(duì)于更大的項(xiàng)目,功能更復(fù)雜,外設(shè)更多。還需要注意代碼的模塊化,可移植性。這樣各個(gè)層級(jí)的文件會(huì)增加,全局變量、局部變量的使用都會(huì)大大增加。這個(gè)時(shí)候代碼的復(fù)雜度就會(huì)成倍增加。這個(gè)時(shí)候,往往就更需要我們知道單片機(jī)的內(nèi)部是怎么工作的。
那么對(duì)于存儲(chǔ)器映射的學(xué)習(xí),是我們了解單片機(jī)內(nèi)部工作的開始。后面我們還會(huì)總結(jié)分析下stm32的啟動(dòng)文件,map文件等等。這些知識(shí)點(diǎn),也是技術(shù)能力邁向更高水平必須要學(xué)習(xí)理解的。也有人說,掌握了單片機(jī)的存儲(chǔ)器映射,就掌握了這款單片機(jī)40%的知識(shí)點(diǎn),這塊知識(shí)點(diǎn)的重要性可見一斑
2、什么是存儲(chǔ)器映射
存儲(chǔ)器本身不具備地址,所以把芯片內(nèi)核所預(yù)先設(shè)定好的地址分配給寄存器,就是存儲(chǔ)器映射。因?yàn)閟tm32的地址線是32位,也就是2的32次方,正好是對(duì)應(yīng)4G的虛擬存儲(chǔ)空間。把內(nèi)核廠商(也就是ARM公司)定義的這個(gè)虛擬空間與芯片廠商(這里是ST)芯片內(nèi)部外設(shè)進(jìn)行對(duì)應(yīng),也就是給存儲(chǔ)器分配地址,即存儲(chǔ)器映射。4個(gè)G的地址這么大,用不完沒關(guān)系,可以保留。
3、stm32f4存儲(chǔ)器映射分析
下圖是stm32f40x 的映射block diagram
cortex-M4內(nèi)核如cortex-m3內(nèi)核一樣,都是把4G空間分成block 0 ~ block7 共 8個(gè)塊,每塊大小為512M,并指出各個(gè)block是怎么分配的。下面我們就詳細(xì)闡述一下。
Block 0代碼區(qū)
Aliased to flash ,system memory or SRAM depending on the Boot pins
開始運(yùn)行,BOOT1、BOOT0這兩個(gè)引腳的電平值選擇0X0000 0000--0X001F FFFF映射到不同的存儲(chǔ)器上,通過BOOT引腳選擇啟動(dòng)模式。篇幅所限,關(guān)于BOOT1、BOOT0的引腳設(shè)置選擇不同的啟動(dòng)方式,這里就不詳細(xì)展開了。
主Flash:用于保存數(shù)據(jù)的區(qū)域,每個(gè)芯片都有一個(gè)參數(shù)Flash空間大小,指的就是這部分。
CCM data RAM:Code區(qū)域是用I-Code和D-Code訪問,作用是為了加快數(shù)據(jù)處理速度。
system memory :STM32在出廠時(shí),已經(jīng)固化了一段程序在System memory(medium-density devices的地址為:0x1FFF_F000,大小為2KB)存儲(chǔ)器中。這段程序就是一個(gè)固定好的,并且沒法修改的Boot Loader
Options Bytes :可以按照用戶的需要進(jìn)行配置(如配置看門狗為硬件實(shí)現(xiàn)還是軟件實(shí)現(xiàn));
PS:存儲(chǔ)器的重映射
通常系統(tǒng)啟動(dòng)都是從0地址處開始,但是為了支持不同的存儲(chǔ)介質(zhì),不同的存儲(chǔ)介質(zhì)被分配了一個(gè)非0地址區(qū)域。這就是為什么要進(jìn)行重映射。
因此重映射主要發(fā)生在兩種情況下,一是系統(tǒng)啟動(dòng)的過程中;二是如果中途遇到需要在不同的存儲(chǔ)器之間進(jìn)行切換的時(shí)候也需要進(jìn)行重映射
我們一般的單片機(jī)自舉(啟動(dòng))單片機(jī)地址,都是從0開始運(yùn)行的,STM32啟動(dòng)需要重映射地址,F(xiàn)4xx的0X0000 0000~0x001F FFFF地址映射了到什么存儲(chǔ)器上,那么就從該存儲(chǔ)器上讀取指令。
Block 1
SRAM運(yùn)行時(shí)臨時(shí)存放代碼的地方。不同類型的STM32單片機(jī)的SRAM大小是不一樣的,但是他們的起始地址都是0x2000 0000,終止地址都是0x2000 0000+其固定的容量大小。SRAM的理解比較簡單,其作用是用來存取各種動(dòng)態(tài)的輸入輸出數(shù)據(jù)、中間計(jì)算結(jié)果以及與外部存儲(chǔ)器交換的數(shù)據(jù)和暫存數(shù)據(jù),用于程序運(yùn)行的堆棧開銷。設(shè)備斷電后,SRAM中存儲(chǔ)的數(shù)據(jù)就會(huì)丟失。
Block 2
Block2 用于設(shè)計(jì)片內(nèi)外設(shè),根據(jù)總線速度的不同,Block2被分為了APB和AHB。在上圖所示的stm32f40x的映射框圖中可以看到,APB分為APB1和APB2,AHB分為AHB1和AHB2,AHB3(不在Block2的映射范圍)
ps:什么是寄存器映射
在存儲(chǔ)器Block2這塊區(qū)域,設(shè)計(jì)的是片上外設(shè),它們以4個(gè)字節(jié)為一個(gè)單元,共32bit,那么每一個(gè)單元對(duì)應(yīng)不同的功能,當(dāng)我們控制這些單元時(shí)就可以驅(qū)動(dòng)外設(shè)工作。我們可以找到每個(gè)單元的起始地址,然后通過C語言指針的操作方式來訪問這些單元,如果每次都是通過這種地址的方式來訪問,不僅不好記憶還容易出錯(cuò),這是我們可以根據(jù)每個(gè)單元的功能不同,以功能為名給這個(gè)存儲(chǔ)單元取一個(gè)別名,這個(gè)別名就是我們經(jīng)常說的寄存器,這個(gè)給已經(jīng)分配好地址的有特定功能的內(nèi)存單元取別名的過程就叫寄存器映射。
舉例說明:
第一步:宏定義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)
第二步:把這個(gè)GPIO的基地址通過加上(GPIO_TypeDef *)這步騷操作,來把地址強(qiáng)轉(zhuǎn)成具有GPIO_TypeDef 性質(zhì)的指針變量,并且用#define進(jìn)行宏定義,實(shí)現(xiàn)取了個(gè)別名的效果。這就是寄存器的映射。
#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)
ps:GPIO_TypeDef結(jié)構(gòu)體的聲明:
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;
APB1總線外設(shè)地址
APB2總線外設(shè)地址
AHB1總線外設(shè)地址
AHB2總線外設(shè)地址
AHB3總線外設(shè)地址
Block3/Block4/Block5
FSMC Bank 的四個(gè)分區(qū)及控制寄存器
Block6
Reserved(保留)
Block7
internal peripherals,cortex-m4 內(nèi)核內(nèi)部外設(shè)地址。
4、代碼展示
然后看下存儲(chǔ)器的映射關(guān)系在代碼中是如何體現(xiàn)的,下圖是我項(xiàng)目中的stm32f4xx.h文件的部分代碼。存儲(chǔ)器的映射關(guān)系都在這個(gè)文件中。復(fù)制了部分代碼,供大家參考學(xué)習(xí)。感興趣的朋友可以對(duì)照自己的代碼和對(duì)應(yīng)芯片規(guī)格書進(jìn)行研究學(xué)習(xí)。
#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)
/*!< AHB1 peripherals */
#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)
/*!< APB2 peripherals */
#define TIM1_BASE (APB2PERIPH_BASE + 0x0000)
#define TIM8_BASE (APB2PERIPH_BASE + 0x0400)
#define USART1_BASE (APB2PERIPH_BASE + 0x1000)
#define USART6_BASE (APB2PERIPH_BASE + 0x1400)
#define ADC1_BASE (APB2PERIPH_BASE + 0x2000)
#define ADC2_BASE (APB2PERIPH_BASE + 0x2100)
#define ADC3_BASE (APB2PERIPH_BASE + 0x2200)
#define ADC_BASE (APB2PERIPH_BASE + 0x2300)
#define SDIO_BASE (APB2PERIPH_BASE + 0x2C00)
#define SPI1_BASE (APB2PERIPH_BASE + 0x3000)
/*!< FSMC Bankx registers base address */
#define FSMC_Bank1_R_BASE (FSMC_R_BASE + 0x0000)
#define FSMC_Bank1E_R_BASE (FSMC_R_BASE + 0x0104)
#define FSMC_Bank2_R_BASE (FSMC_R_BASE + 0x0060)
#define FSMC_Bank3_R_BASE (FSMC_R_BASE + 0x0080)
#define FSMC_Bank4_R_BASE (FSMC_R_BASE + 0x00A0)
今天分享的內(nèi)容就到這里。結(jié)合我自己理解的同時(shí),查閱了大量的相關(guān)文檔。本人能力有限,難免存在疏漏之處。如有問題,歡迎指正。
評(píng)論
查看更多