本帖最后由 fanssun 于 2015-11-16 12:54 编辑
*注:一下内容来自 原子STM32开发板配套书籍《STM32F1开发指南-寄存器版本_V3.1 》
首先我们看看 51 中是怎么做的。 51 单片机开发中经常会引用一个 reg51.h 的头文件,下
面我们看看他是怎么把名字和寄存器联系起来的:
sfr P0 =0x80;
sfr 也是一种扩充数据类型,点用一个内存单元,值域为 0~255。利用它可以访问 51 单片
机内部的所有特殊功能寄存器。如用 sfr P1 = 0x90 这一句定义 P1 为 P1 端口在片内的寄存
器。然后我们往地址为 0x80 的寄存器设值的方法是: P0=value;
那么在 STM32 中,是否也可以这样做呢??答案是肯定的。肯定也可以通过同样的方
式来做,但是 STM32 因为寄存器太多太多,如果一一以这样的方式列出来,那要好大的篇
幅,既不方便开发,也显得太杂乱无序的感觉。所以 MDK 采用的方式是通过结构体来将
寄存器组织在一起。下面我们就讲解 MDK 是怎么把结构体和地址对应起来的,为什么我
们修改结构体成员变量的值就可以达到操作对应寄存器的值。这些事情都是在 stm32f10x.h
文件中完成的。我们通过 GPIOA 的几个寄存器的地址来讲解吧。
首先我们可以查看《 STM32 中文参考手册 V10》中的寄存器地址映射表(P159):
ALIENTEK 战舰 STM32F103 V3 开发板教程
132
STM32F1 开发指南(库函数版)
图 4.6.1 GPIO 寄存器地址映像
从这个表我们可以看出, GPIOA 的 7 个寄存器都是 32 位的,所以每个寄存器占有 4
个地址,一共占用 28 个地址,地址偏移范围为( 000h~01Bh)。这个地址偏移是相对 GPIOA
的基地址而言的。 GPIOA 的基地址是怎么算出来的呢?因为 GPIO 都是挂载在 APB2 总线
之上,所以它的基地址是由 APB2 总线的基地址+GPIOA 在 APB2 总线上的偏移地址决定
的。同理依次类推,我们便可以算出 GPIOA 基地址了。这里设计到总线的一些知识,我们
在后面会讲到。下面我们打开 stm32f10x.h 定位到 GPIO_TypeDef 定义处:
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;
然后定位到:
#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 (APB2PERIPH_BASE + 0x0800)
依次类推,可以找到最顶层:
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define PERIPH_BASE ((uint32_t)0x40000000)
所以我们便可以算出 GPIOA 的基地址位:
ALIENTEK 战舰 STM32F103 V3 开发板教程
133
STM32F1 开发指南(库函数版)
GPIOA_BASE= 0x40000000+0x10000+0x0800=0x40010800
下面我们再跟《 STM32 中文参考手册 V10》比较一下看看 GPIOA 的基地址是不是
0x40010800。截图 P28 存储器映射表我们可以看到, GPIOA 的起始地址也就是基地址确实
是 0x40010800:
图 4.6.2 GPIO 存储器地址映射表
同样的道理,我们可以推算出其他外设的基地址。
上面我们已经知道 GPIOA 的基地址,那么那些 GPIOA 的 7 个寄存器的地址又是怎么
算出来的呢??在上面我们讲过 GPIOA 的各个寄存器对于 GPIOA 基地址的偏移地址,所
以我们自然可以算出来每个寄存器的地址。
GPIOA 的寄存器的地址=GPIOA 基地址+寄存器相对 GPIOA 基地址的偏移值
这个偏移值在上面的寄存器地址映像表中可以查到。
那么在结构体里面这些寄存器又是怎么与地址一一对应的呢?这里就涉及到结构体的
一个特征,那就是结构体存储的成员他们的地址是连续的。上面讲到 GPIOA 是指向
GPIO_TypeDef 类型的指针,又由于 GPIO_TypeDef 是结构体,所以自然而然我们就可以算
出 GPIOA 指向的结构体成员变量对应地址了。
寄存器 偏移地址 实际地址=基地址+偏移地址
GPIOA->CRL 0x00 0x40010800+0x00
GPIOA->CRH; 0x04 0x40010800+0x04
GPIOA->IDR; 0x08 0x40010800+0x08
GPIOA->ODR 0x0c 0x40010800+0x0c
GPIOA->BSRR 0x10 0x40010800+0x10
GPIOA->BRR 0x14 0x40010800+0x14
GPIOA->LCKR 0x18 0x40010800+0x18
表 4.6.3 GPIOA 各寄存器实际地址表
我们可以把 GPIO_TypeDef 的定义中的成员变量的顺序和 GPIOx 寄存器地址映像对比
可以发现,他们的顺序是一致的,如果不一致,就会导致地址混乱了。
这就是为什么固件库里面: GPIOA->BRR=value;就是设置地址为 0x40010800
+0x014(BRR 偏移量)=0x40010814 的寄存器 BRR 的值了。它和 51 里面 P0=value 是设置地
址为 0x80 的 P0 寄存器的值是一样的道理。
本帖最后由 fanssun 于 2015-11-16 12:54 编辑
*注:一下内容来自 原子STM32开发板配套书籍《STM32F1开发指南-寄存器版本_V3.1 》
首先我们看看 51 中是怎么做的。 51 单片机开发中经常会引用一个 reg51.h 的头文件,下
面我们看看他是怎么把名字和寄存器联系起来的:
sfr P0 =0x80;
sfr 也是一种扩充数据类型,点用一个内存单元,值域为 0~255。利用它可以访问 51 单片
机内部的所有特殊功能寄存器。如用 sfr P1 = 0x90 这一句定义 P1 为 P1 端口在片内的寄存
器。然后我们往地址为 0x80 的寄存器设值的方法是: P0=value;
那么在 STM32 中,是否也可以这样做呢??答案是肯定的。肯定也可以通过同样的方
式来做,但是 STM32 因为寄存器太多太多,如果一一以这样的方式列出来,那要好大的篇
幅,既不方便开发,也显得太杂乱无序的感觉。所以 MDK 采用的方式是通过结构体来将
寄存器组织在一起。下面我们就讲解 MDK 是怎么把结构体和地址对应起来的,为什么我
们修改结构体成员变量的值就可以达到操作对应寄存器的值。这些事情都是在 stm32f10x.h
文件中完成的。我们通过 GPIOA 的几个寄存器的地址来讲解吧。
首先我们可以查看《 STM32 中文参考手册 V10》中的寄存器地址映射表(P159):
ALIENTEK 战舰 STM32F103 V3 开发板教程
132
STM32F1 开发指南(库函数版)
图 4.6.1 GPIO 寄存器地址映像
从这个表我们可以看出, GPIOA 的 7 个寄存器都是 32 位的,所以每个寄存器占有 4
个地址,一共占用 28 个地址,地址偏移范围为( 000h~01Bh)。这个地址偏移是相对 GPIOA
的基地址而言的。 GPIOA 的基地址是怎么算出来的呢?因为 GPIO 都是挂载在 APB2 总线
之上,所以它的基地址是由 APB2 总线的基地址+GPIOA 在 APB2 总线上的偏移地址决定
的。同理依次类推,我们便可以算出 GPIOA 基地址了。这里设计到总线的一些知识,我们
在后面会讲到。下面我们打开 stm32f10x.h 定位到 GPIO_TypeDef 定义处:
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;
然后定位到:
#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 (APB2PERIPH_BASE + 0x0800)
依次类推,可以找到最顶层:
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define PERIPH_BASE ((uint32_t)0x40000000)
所以我们便可以算出 GPIOA 的基地址位:
ALIENTEK 战舰 STM32F103 V3 开发板教程
133
STM32F1 开发指南(库函数版)
GPIOA_BASE= 0x40000000+0x10000+0x0800=0x40010800
下面我们再跟《 STM32 中文参考手册 V10》比较一下看看 GPIOA 的基地址是不是
0x40010800。截图 P28 存储器映射表我们可以看到, GPIOA 的起始地址也就是基地址确实
是 0x40010800:
图 4.6.2 GPIO 存储器地址映射表
同样的道理,我们可以推算出其他外设的基地址。
上面我们已经知道 GPIOA 的基地址,那么那些 GPIOA 的 7 个寄存器的地址又是怎么
算出来的呢??在上面我们讲过 GPIOA 的各个寄存器对于 GPIOA 基地址的偏移地址,所
以我们自然可以算出来每个寄存器的地址。
GPIOA 的寄存器的地址=GPIOA 基地址+寄存器相对 GPIOA 基地址的偏移值
这个偏移值在上面的寄存器地址映像表中可以查到。
那么在结构体里面这些寄存器又是怎么与地址一一对应的呢?这里就涉及到结构体的
一个特征,那就是结构体存储的成员他们的地址是连续的。上面讲到 GPIOA 是指向
GPIO_TypeDef 类型的指针,又由于 GPIO_TypeDef 是结构体,所以自然而然我们就可以算
出 GPIOA 指向的结构体成员变量对应地址了。
寄存器 偏移地址 实际地址=基地址+偏移地址
GPIOA->CRL 0x00 0x40010800+0x00
GPIOA->CRH; 0x04 0x40010800+0x04
GPIOA->IDR; 0x08 0x40010800+0x08
GPIOA->ODR 0x0c 0x40010800+0x0c
GPIOA->BSRR 0x10 0x40010800+0x10
GPIOA->BRR 0x14 0x40010800+0x14
GPIOA->LCKR 0x18 0x40010800+0x18
表 4.6.3 GPIOA 各寄存器实际地址表
我们可以把 GPIO_TypeDef 的定义中的成员变量的顺序和 GPIOx 寄存器地址映像对比
可以发现,他们的顺序是一致的,如果不一致,就会导致地址混乱了。
这就是为什么固件库里面: GPIOA->BRR=value;就是设置地址为 0x40010800
+0x014(BRR 偏移量)=0x40010814 的寄存器 BRR 的值了。它和 51 里面 P0=value 是设置地
址为 0x80 的 P0 寄存器的值是一样的道理。
举报