库文件和参考手册
本文以自己的理解进行总结,如有问题,可提出
寄存器和库函数的联系
学过单片机的都清楚,一款MCU芯片包含内核,外设,存储器等,内部和这些外设都是由系统总线控制。以Cortex-M3为例,32位访问地址,拥有4G的访问空间,0x00000000~0xFFFFFFFF。如图所示,为存储器映射,ARM架构规定了每一块空间是什么。
而库函数对GPIO,USART的操作,最终以系统总线,读写这些地址。如下图所示,为stm32f103的寄存器GPIO的地址。
以HAL库GPIOA为例,可以找到以下三行的定义(stm32f1xx.h):
#define PERIPH_BASE 0x40000000UL
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x00000800UL)
不难看出,GPIOA_BASE == 0x40010800,对应了上图的GPIO Port A。然后HAL又定义的以下宏定义,可以看到是一个指针,在C语言指针,表示指向某一个地址,也就是说GPIOA指向了0x40010800这个地址。
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
到这里可能就想到,为什么是GPIO_TypeDef类型,我们接着往下看,如下图参考手册的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;
从这里是不是可以看出, GPIO_TypeDef这个结构体定义了,GPIO的寄存器。为什么顺序上会一致,如下图,可以看到CRL和CRH里 ADDrees offset, 在看看GPIO_TypeDef 定义的类型 uint32_t。
没错,uint32_t是4个字节,而这个寄存器占用了4个字节,在结构体中,字节会依次从上往下会依次叠加,(当然·这里也有其他东西,结构体里数据类型的存放位置会影响到内存的使用率,后面将介绍),所以GPIO->CRL的地址为 0x40010800,GPIO->CRL的地址为 0x40010804,这样就可以对GPIOA的寄存器配置,实现各个外设的功能,当然·一些外设会需要其他外设配置才能实现功能,比如这里还需要RCC这个寄存器。
我们也可以看看nxp的库,如下代码和图片(MIMXRT1062.h),RT1602这款芯片:
#define GPIO1_BASE (0x401B8000u)
#define GPIO1 ((GPIO_Type *)GPIO1_BASE)
typedef struct {
__IO uint32_t DR; /**< GPIO data register, offset: 0x0 */
__IO uint32_t GDIR; /**< GPIO direction register, offset: 0x4 */
__I uint32_t PSR; /**< GPIO pad status register, offset: 0x8 */
__IO uint32_t ICR1; /**< GPIO interrupt configuration register1, offset: 0xC */
__IO uint32_t ICR2; /**< GPIO interrupt configuration register2, offset: 0x10 */
__IO uint32_t IMR; /**< GPIO interrupt mask register, offset: 0x14 */
__IO uint32_t ISR; /**< GPIO interrupt status register, offset: 0x18 */
__IO uint32_t EDGE_SEL; /**< GPIO edge select register, offset: 0x1C */
uint8_t RESERVED_0[100];
__O uint32_t DR_SET; /**< GPIO data register SET, offset: 0x84 */
__O uint32_t DR_CLEAR; /**< GPIO data register CLEAR, offset: 0x88 */
__O uint32_t DR_TOGGLE; /**< GPIO data register TOGGLE, offset: 0x8C */
} GPIO_Type;
总结
本文只是讲解库函数和参考手册的关系,后续会讲到如何实现一个外设的功能。