单片机/MCUwilliam hill官网
直播中

youhuofree0

11年用户 37经验值
擅长:MEMS/传感技术 模拟技术 连接器 EMC/EMI设计 光电显示 控制/MCU
私信 关注
[问答]

关于结构体与对应寄存器地址的问题

//LCD地址结构体
typedef struct
{
        u16 LCD_REG;
        u16 LCD_RAM;
} LCD_TypeDef;
结构体定义成这样,如何让LCD_REG变量对应地址为0x6000 0000,LCD_RAM对应寄存器地址为0x6002 0000?

回帖(11)

dongyumin

2014-1-19 11:03:33
#define LCD ((LCD_TypeDef*)0X60000000 )  

结构体每个成员变量都是16bit的。所以,第一个LCD->LCD_REG的地址0X60000000.第二个LCD->LCD_RAM地址自增2个字节。。。
举报

youhuofree0

2014-1-19 15:49:29
引用: dongyumin 发表于 2014-1-19 11:03
#define LCD ((LCD_TypeDef*)0X60000000 )  

结构体每个成员变量都是16bit的。所以,第一个LCD->LCD_REG ...


首先,谢谢您的回答,这个我知道,问题是我想让这个结构体第二个成员地址为0x6002 0000,后面用到LCD->LCD_REG和LCD->LCD_RAM这两个变量,名字不改了,但对应变量地址需要改变。
举报

dongyumin

2014-1-19 16:08:41
引用: youhuofree0 发表于 2014-1-19 15:49
首先,谢谢您的回答,这个我知道,问题是我想让这个结构体第二个成员地址为0x6002 0000,后面用到LCD->L ...

typedef struct
{
  __IO uint16_t CR1;
  uint16_t  RESERVED0;
  __IO uint16_t CR2;
  uint16_t  RESERVED1;
  __IO uint16_t OAR1;
  uint16_t  RESERVED2;
  __IO uint16_t OAR2;
  uint16_t  RESERVED3;
  __IO uint16_t DR;
  uint16_t  RESERVED4;
  __IO uint16_t SR1;
  uint16_t  RESERVED5;
  __IO uint16_t SR2;
  uint16_t  RESERVED6;
  __IO uint16_t CCR;
  uint16_t  RESERVED7;
  __IO uint16_t TRISE;
  uint16_t  RESERVED8;
} I2C_TypeDef;
库里的结构体,定义了成员变量RESERVED来保留一些不用的地址空间,,,这些寄存器实质都是4字节的,而有效位都只有它们的低16位,软件上为了对齐,定义了保留成员变量用于填充内存。。。

举报

youhuofree0

2014-1-19 18:00:30
引用: dongyumin 发表于 2014-1-19 16:08
typedef struct
{
  __IO uint16_t CR1;


我是不是可以理解为将第一个变量地址指定为0x6000 0000,中间填充保留变量(实际未用),一直到地址为0x6002 0000呢?可是这样定义的保留变量就太多了2的16次方个(65536)。能不能弄到一个结构体中呢?不能的话,怎样定义成该地址的指针变量啊?再次感谢!
举报

dongyumin

2014-1-19 18:10:34
#define LCD_RAM ((volatile u16*)0x60020000)
#define LCD_REG ((volatile u16*)0x60000000)

直接这样变成指针吧、、、呵呵、
举报

youhuofree0

2014-1-19 19:11:21
引用: dongyumin 发表于 2014-1-19 18:10
#define LCD_RAM ((volatile u16*)0x60020000)
#define LCD_REG ((volatile u16*)0x60000000)


好吧,谢谢啦~
举报

fanssun

2015-11-16 12:51:51
本帖最后由 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 寄存器的值是一样的道理。
举报

到此寻梦

2016-4-22 23:51:40
这个真的是极不错的
举报

小老虎嗯嗯啊

2017-3-19 20:10:16
豁然开朗
举报

小老虎嗯嗯啊

2017-3-19 20:14:22
问一个有点傻的问题啊,结构体变量在初定义时不就有自己的初始首地址吗?再强制定义一个这个类型的指针,指向的地址却是已知的啊
举报

rbap

2017-3-19 21:15:29
dddddddddddddddddddddddddddddddd
举报

更多回帖

发帖
×
20
完善资料,
赚取积分