图为 储存器对应控制GPIO 以及时钟外设等地址
编写库函数时 使用到了C语言的强制转换,将指针指向向对应的GPIO外设和时钟等地址,避免了类似寄存器学要多个定义每个特有功能的寄存器。
比较如下:
库函数
#ifndef stm32__H__
#define stm32__H__
#define PERIPH_BASE ((unsigned int)0x40000000) //stmf103外设总基地址
#define AHBPERIPH_BASE (PERIPH_BASE+0x20000)
#define APB1PERIPH_BASE (PERIPH_BASE)
#define APB2PERIPH_BASE (PERIPH_BASE+0x10000)
#define RCC_BASE (AHBPERIPH_BASE+0x1000)
#define GPIOB_BASE (APB2PERIPH_BASE+0x0c00)
#define uint_32t unsigned int
#define uint_16t unsigned short
#define uint_8t unsigned char
typedef struct{
uint_32t CRL;
uint_32t CRH;
uint_32t IDR;
uint_32t ODR;
uint_32t BSRR;
uint_32t BRR;
uint_32t LCKR;
}GPIO_Typedef;
typedef struct{
uint_32t RCC_CR;
uint_32t RCC_CFGR;
uint_32t RCC_CIR;
uint_32t RCC_APB2RSTR;
uint_32t RCC_APB1RSTR;
uint_32t RCC_AHBENR;
uint_32t RCC_APB2ENR;
uint_32t RCC_APB1ENR;
uint_32t RCC_BDCR;
uint_32t RCC_CSR;
uint_32t RCC_AHBRSTR;
}RCC_Typedef;
#define GPIOB ((GPIO_Typedef*)(GPIOB_BASE))
#define RCC ((RCC_Typedef*)(RCC_BASE))
#endif
寄存器
#ifndef STM32_H
#define STM32_H
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB1PERIPH PERIPH_BASE
#define AHBPERIPH_BASE (PERIPH_BASE+0x20000)
#define APB2PERIPH_BASE (PERIPH_BASE+0x10000)
#define RCC_BASE (AHBPERIPH_BASE+0x1000)
//基地址
#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+0x2c00)
#define GPIOG_BASE (APB2PERIPH_BASE+0x3000)
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18)
//低位寄存器
#define GPIOA_CRL *(unsigned int*)(GPIOA_BASE+0x00)
#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE+0x00)
#define GPIOC_CRL *(unsigned int*)(GPIOC_BASE+0x00)
#define GPIOD_CRL *(unsigned int*)(GPIOD_BASE+0x00)
#define GPIOE_CRL *(unsigned int*)(GPIOE_BASE+0x00)
#define GPIOF_CRL *(unsigned int*)(GPIOF_BASE+0x00)
#define GPIOG_CRL *(unsigned int*)(GPIOG_BASE+0x00)
//高位寄存器
#define GPIOA_CRH *(unsigned int*)(GPIOA_BASE+0x04)
#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE+0x04)
#define GPIOC_CRH *(unsigned int*)(GPIOC_BASE+0x04)
#define GPIOD_CRH *(unsigned int*)(GPIOD_BASE+0x04)
#define GPIOE_CRH *(unsigned int*)(GPIOE_BASE+0x04)
#define GPIOF_CRH *(unsigned int*)(GPIOF_BASE+0x04)
#define GPIOG_CRH *(unsigned int*)(GPIOG_BASE+0x04)
//输出寄存器
#define GPIOA_ODR *(unsigned int*)(GPIOA_BASE+0x0c)
#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0c)
#define GPIOC_ODR *(unsigned int*)(GPIOC_BASE+0x0c)
#define GPIOD_ODR *(unsigned int*)(GPIOD_BASE+0x0c)
#define GPIOE_ODR *(unsigned int*)(GPIOE_BASE+0x0c)
#define GPIOF_ODR *(unsigned int*)(GPIOF_BASE+0x0c)
#define GPIOG_ODR *(unsigned int*)(GPIOG_BASE+0x0c)
//输入寄存器
#define GPIOA_IDR *(unsigned int*)(GPIOA_BASE+0x08)
#define GPIOB_IDR *(unsigned int*)(GPIOB_BASE+0x08)
#define GPIOC_IDR *(unsigned int*)(GPIOC_BASE+0x08)
#define GPIOD_IDR *(unsigned int*)(GPIOD_BASE+0x08)
#define GPIOE_IDR *(unsigned int*)(GPIOE_BASE+0x08)
#define GPIOF_IDR *(unsigned int*)(GPIOF_BASE+0x08)
#define GPIOG_IDR *(unsigned int*)(GPIOG_BASE+0x08)
//置位寄存器
#define GPIOA_BSRR *(unsigned int*)(GPIOA_BASE+0x10)
#define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE+0x10)
#define GPIOC_BSRR *(unsigned int*)(GPIOC_BASE+0x10)
#define GPIOD_BSRR *(unsigned int*)(GPIOD_BASE+0x10)
#define GPIOE_BSRR *(unsigned int*)(GPIOE_BASE+0x10)
#define GPIOF_BSRR *(unsigned int*)(GPIOF_BASE+0x10)
#define GPIOG_BSRR *(unsigned int*)(GPIOG_BASE+0x10)
//复位寄存器
#define GPIOA_BRR *(unsigned int*)(GPIOA_BASE+0x14)
#define GPIOB_BRR *(unsigned int*)(GPIOB_BASE+0x14)
#define GPIOC_BRR *(unsigned int*)(GPIOC_BASE+0x14)
#define GPIOD_BRR *(unsigned int*)(GPIOD_BASE+0x14)
#define GPIOE_BRR *(unsigned int*)(GPIOE_BASE+0x14)
#define GPIOF_BRR *(unsigned int*)(GPIOF_BASE+0x14)
#define GPIOG_BRR *(unsigned int*)(GPIOG_BASE+0x14)
#endif
进过比较会发现 每个GPIO 都需要定义 指定的寄存器而且每个都不一样,但是当我们用库函数时会发现我们的工作量少了,只需要把GPIO的基地址映射出来,然后在定义结构体 GPIO_Typdef ,在强制转换为GPIO_Typdef 。然后通过指针操作,相比之下工作轻松多了。
下面我们来讲他的原理 :
寄存器之间偏移地址为0x04 (注意是十六进制,在编写过程中如果用十进制的话会访问不到指定地址)
当我 们进行强转时:
#define GPIOB ((GPIO_Typedef*)(GPIOB_BASE)) 相当于把 GPIOB_BASE 的地址 作为 指针GPIOB的首地址
GPIOB_BASE 的地址是 :0x40010c00
寄存器
GPIOB_CRL 地址 GPIOB_BASE +偏移地址(0x00) 0x40010c00
GPIOB_CRH 地址 GPIOB_BASE +偏移地址(0x04) 0x40010c04
库函数
结构体 GPIO_Typdef 结构 的成员均为int_32T ,偏移地址为4
当我们 访问 GPIOB ->CRL (首个成员就是结构体指针的地址) 那么 地址为:0x40010c00
访问GPIOB->CRH 就是首个成员地址加上偏移地址 那么 地址为:0x40010c04
会发现本质就是地址的访问