APB1 总线挂载外设 及地址
APB2 总线挂载外设及地址
AHB1 挂载外设及地址
AHB2 及 AHB3 挂载外设及基地址
第一步确定整个外设区域的基地址
#define PERIPH_BASE ((uint32_t)0x40000000) /*!<peripheral=""base="" address=""in="" the=""alias="">
第二步确定不同总线的基地址, 因为不同总线的上边挂载着不同的系统外设
- /*!<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
来源: http://www.92to.com/bangong/2018/07-10/33982252.html