STM32存儲器映射和寄存器映射


存儲器映射

  對於Cortex-M3來講,有一塊4G大小的存儲器空間。存儲器映射指的是芯片廠商為這個空間分配地址的操作。這4G空間被均勻地划分為8個大小為512MB的存儲塊(block),並且每個塊都各具特色。下面主要介紹Block1~Block2。

Block0

  Block0的地址范圍為0x0000_0000~0x1FFF_FFFF。它被設計用來存放代碼程序,其中主要有FLASH、SYSTEM MEMORY和OPTION BYTES:

    FLASH:起始地址為0x0800_0000,存放用戶程序和掉電保存數據。FLASH容量從16k到512k不等,以STM32F10x8系列為例,8代表FLASH容量為64k,所以結束地址就為0x0801_FFFF。

    SYSTEM MEMORY:系統存儲器,存放了BootLoader程序,禁止用戶改動,該程序主要用於串口下載。

    OPTION BYTES:選項字節,可以配置讀寫保護、看門狗等。

  這里簡單說一下地址分配。整塊4G存儲器開始地址標為0x0000_0000,結束地址為0xFFFF_FFFF,地址表示采用了十六進制,一共8*4=32bit,而2^32剛好就是4G。存儲器的基本單元是一個字節,每個地址都對應這樣一個單元,因此用32位地址來表示,其容量剛好就是4GB。同理,根據Block0的起止地址,也可以計算得到其容量為512MB。

 

Block1

  Block1的地址范圍為0x2000_0000~0x3FFF_FFFF。其中0x2000_0000~0x2000_FFFF被划為SRAM,主要是用於程序運行的堆棧開銷。

 

Block2

  Block2的地址范圍為0x4000_0000~0x5FFF_FFFF。這塊空間被充分用作外設寄存器。根據總線不同,將外設分為三大塊,第一塊是APB1總線外設,起始地址為0x4000_0000;第二塊是APB2總線外設,起始地址為0x4001_0000;最后一塊為AHB總線外設,起始地址為0x4001_8000。

 

 

寄存器映射

  寄存器映射主要針對於Block2。這種映射不同於存儲器映射的分配地址操作,而是在程序中對具有特定功能的內存單元進行命名的過程,每個內存單元都是四個字節,稱作寄存器。例如GPIOA外設有個寄存器地址范圍為0x4001_0800~0x4001_0803,該寄存器是用來配置GPIOA部分端口工作模式的,因此被映射為GPIOA_CRL。由此可知,這種映射方便了對寄存器的訪問操作,因為如果每次訪問寄存器都要查地址范圍的話是很痛苦的。

GPIOA_CRL

  如何實現這種映射的呢?以GPIOA_CRL為例,其映射地址 = 外設總基地址(塊基地址)+ 總線相對於外設總基地址的偏移 + 具體外設基地址相對於總線基地址的偏移 + 寄存器相對於具體外設基地址的偏移。

  外設總基地址恰好就是Block2的起始地址0x4000_0000;

  GPIO屬於APB2總線外設,因此查閱芯片手冊如下圖所示,我們其實直接可以得到APB2起始地址和GPIOA的起始地址,但是程序中一般不這么做,而是以偏移量來表示層次關系。從圖中可計算到總線相對於外設總基地址的偏移為0x1_0000,GPIOA相對於APB2基地址的偏移為0x800。

    

  再查閱GPIO的寄存器組,如下圖所示。可以得到CRL寄存器相對於GPIO基地址偏移為0x00。綜上GPIOA_CRL的基地址為:0x4000_0000+0x1_0000+0x800+0x00。

    

 

程序中地址映射

  STM32固件庫中,有個頭文件叫stm32f10x.h,其中就定義了寄存器的映射,部分代碼如下:

 

  外設基地址PERIPH_BASE:

#define PERIPH_BASE           ((uint32_t)0x40000000)

 

  總線基地址,在外設基地址上加上偏移:

#define APB1PERIPH_BASE       PERIPH_BASE
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)

  

  GPIO外設基地址,在APB2總線基地址上加上偏移:

#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE            (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE            (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE            (APB2PERIPH_BASE + 0x2000)

 

  定義GPIO外設結構體,因為結構體成員在內存中是連續的,這種形式與寄存器組非常類似,所以用結構體能夠很好的管理寄存器:

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

 

  定義GPIOA結構體指針,因為單單定義GPIO外設結構體,並不能確定其內存地址,因此用指針將其綁定到GPIOA外設基地址:

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)

 

  訪問寄存器方式的對比,映射訪問明顯更為直觀:

//直接的地址訪問
*(unsigned int *)(0x4001_0800)|= 0x0001;
//映射訪問
GPIOA->CRL |= 0x0001;

 

 

  

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM