MDK 中寄存器地址名称映射 -- 分析

之所以要讲解这部分知识,是因为经常会遇到客户提到不明白 MDK 中那些结构体是怎么与寄存器地址对应起来的。这里我们就做一个简要的分析吧。

首先我们看看 51 中是怎么做的。 51 单片机开发中经常会引用一个 reg51.h 的头文件,下面我们看看他是怎么把名字和寄存器联系起来的:

sfr P0 =0x80;

sfr 也是一种扩充数据类型,点用一个内存单元,值域为 0~255。利用它可以访问 51 单片机内部的所有特殊功能寄存器。如用 sfr P1 = 0x90 这一句定义 P1 为 P1 端口在片内的寄存器。然后我们往地址为 0x80 的寄存器设值的方法是: P0=value;

那么在 STM32 中,是否也可以这样做呢??答案是肯定的。肯定也可以通过同样的方式来做,但是 STM32 因为寄存器太多太多,如果一一以这样的方式列出来,那要好大的篇幅,既不方便开发,也显得太杂乱无序的感觉。所以 MDK 采用的方式是通过结构体来将寄存器组织在一起。下面我们就讲解 MDK 是怎么把结构体和地址对应起来的,为什么我们修改结构体成员变量的值就可以达到操作对应寄存器的值。这些事情都是在 stm32f4xx.h文件中完成的。我们通过 GPIOA 的几个寄存器的地址来讲解吧。

首先我们可以查看《STM32F4 中文参考手册》中的寄存器地址映射表(P193)。这里我们选用 GPIOA 为例来讲解。 GPIOA 寄存器地址映射如下表 4.6.1:
在这里插入图片描述
从这个表我们可以看出, 因为 GIPO 寄存器都是 32 位, 所以每组 GPIO 的 10 个寄存器中, 每个寄存器占有 4 个地址, 一共占用 40 个地址,地址偏移范围为( 0x00~0x24)。这个地址偏移是相对 GPIOA 的基地址而言的。 GPIOA 的基地址是怎么算出来的呢?因为 GPIO都是挂载在 AHB1 总线之上,所以它的基地址是由 AHB1 总线的基地址+GPIOA 在 AHB1总线上的偏移地址决定的。同理依次类推,我们便可以算出 GPIOA 基地址了。下面我们打开 stm32f4xx.h 定位到 GPIO_TypeDef 定义处:

typedef struct
{
	__IO uint32_t MODER;
	__IO uint32_t OTYPER;
	__IO uint32_t OSPEEDR;
	__IO uint32_t PUPDR;
	__IO uint32_t IDR;
	__IO uint32_t ODR;
	__IO uint16_t BSRRL;
	__IO uint16_t BSRRH;
	__IO uint32_t LCKR;
	__IO uint32_t AFR[2];
} GPIO_TypeDef;

然后定位到:

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

可以看出, GPIOA 是将 GPIOA_BASE 强制转换为 GPIO_TypeDef 指针,这句话的意思是,GPIOA 指向地址 GPIOA_BASE, GPIOA_BASE 存放的数据类型为 GPIO_TypeDef。然后双击“ GPIOA_BASE”选中之后右键选中“Go to definition of ”,便可一查看 GPIOA_BASE 的宏定义:

#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000)

依次类推,可以找到最顶层:

#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000)
#define PERIPH_BASE ((uint32_t)0x40000000)

所以我们便可以算出 GPIOA 的基地址位:

GPIOA_BASE= 0x40000000+0x00020000+0x0000=0x40020000

下面我们再跟《STM32F 中文参考手册》比较一下看看 GPIOA 的基地址是不是 0x40020000 。截图 P53 存储器映射表我们可以看到, GPIOA 的起始地址也就是基地址确实是 0x40020000:
在这里插入图片描述
同样的道理,我们可以推算出其他外设的基地址。
上面我们已经知道 GPIOA 的基地址,那么那些 GPIOA 的 10 个寄存器的地址又是怎么算出来的呢??在上面我们讲过 GPIOA 的各个寄存器对于 GPIOA 基地址的偏移地址,所以我们自然可以算出来每个寄存器的地址。

GPIOA 的寄存器的地址=GPIOA 基地址+寄存器相对 GPIOA 基地址的偏移值

这个偏移值在上面的寄存器地址映像表中可以查到。
那么在结构体里面这些寄存器又是怎么与地址一一对应的呢?这里涉及到结构体成员变量地址对齐方式方面的知识,这方面的知识大家可以在网上查看相关资料复习一下,这里我们不做详细讲解。在我们定义好地址对齐方式之后,每个成员变量对应的地址就可以根据其基地址来计算。对于结构体类型 GPIO_TypeDef,他的所有成员变量都是 32 位,成员变量地址具有连续性。所以自然而然我们就可以算出 GPIOA 指向的结构体成员变量对应地址了。
在这里插入图片描述
我们可以把 GPIO_TypeDef 的定义中的成员变量的顺序和 GPIOx 寄存器地址映像对比可以发现,他们的顺序是一致的,如果不一致,就会导致地址混乱了。

这就是为什么固件库里面: GPIOA->BSRR=value;就是设置地址为 0x40020000+0x18 (BSRR 偏移量)=0x40020018 的寄存器 BSRR 的值了。它和 51 里面 P0=value 是设置地址为 0x80 的 P0 寄存器的值是一样的道理。

看到这里你是否会学起来踏实一点呢? STM32 使用的方式虽然跟 51 单片机不一样,但是原理都是一致的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值