第一步確定整個外設區域的基地址
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias 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)
可以發現對應的是對的
第三步我們以AHB1上掛載的GPIO外設的地址封裝為例講解
/*!< 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)
#define CRC_BASE (AHB1PERIPH_BASE + 0x3000)
#define RCC_BASE (AHB1PERIPH_BASE + 0x3800)
#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x3C00)
#define DMA1_BASE (AHB1PERIPH_BASE + 0x6000)
#define DMA1_Stream0_BASE (DMA1_BASE + 0x010)
#define DMA1_Stream1_BASE (DMA1_BASE + 0x028)
#define DMA1_Stream2_BASE (DMA1_BASE + 0x040)
#define DMA1_Stream3_BASE (DMA1_BASE + 0x058)
#define DMA1_Stream4_BASE (DMA1_BASE + 0x070)
#define DMA1_Stream5_BASE (DMA1_BASE + 0x088)
#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0)
#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8)
#define DMA2_BASE (AHB1PERIPH_BASE + 0x6400)
#define DMA2_Stream0_BASE (DMA2_BASE + 0x010)
#define DMA2_Stream1_BASE (DMA2_BASE + 0x028)
#define DMA2_Stream2_BASE (DMA2_BASE + 0x040)
#define DMA2_Stream3_BASE (DMA2_BASE + 0x058)
#define DMA2_Stream4_BASE (DMA2_BASE + 0x070)
#define DMA2_Stream5_BASE (DMA2_BASE + 0x088)
#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0)
#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8)
#define ETH_BASE (AHB1PERIPH_BASE + 0x8000)
#define ETH_MAC_BASE (ETH_BASE)
#define ETH_MMC_BASE (ETH_BASE + 0x0100)
#define ETH_PTP_BASE (ETH_BASE + 0x0700)
#define ETH_DMA_BASE (ETH_BASE + 0x1000)
#define DMA2D_BASE (AHB1PERIPH_BASE + 0xB000)
可以看出我們已經知道了每個外設的基地址 ,我們有公式
寄存器地址=外設的基地址+外設的偏移地址 利用公式那麼說我們的每個外設內需要操作的寄存器的地址,我們都可以進行訪問了。
第四步 看看庫函數是怎麼幫助我們定義外設的寄存器地址的,是怎麼樣對其進行封裝的?4
#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)
可以看出以上庫函數神奇的操作是把一堆的32位的無符號的整數 強制轉換成了結構體類型的指針。
並且(GPIO_TypeDef *) 這個指針只能指向GPIO_TypeDef 這個結構體類型
下面我們打開這個結構體
發現庫函數把我們的GPIO的寄存器類型全都放在了一起 ,因為我們這些具有相同的特徵描述的是同一事物的不同性質,不禁就讓人想到了c語言的結構體,並且C語言的結構體成員變量在我們的sram中存儲的方式是連續的。
第五步看看怎麼用
這是應用
這是一個結構體
我們訪問結構體
我們最終訪問到了GPIOA
閱讀更多 電氣電子大佬辛 的文章