OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5516|回复: 25

看过论坛里面这个问题,并且百度过,但是关于#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)还有一些疑问,希望大神进来see

[复制链接]

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
发表于 2018-4-4 00:53:43 | 显示全部楼层 |阅读模式
5金钱
这是我在论坛看的相关的帖子: #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)        http://www.openedv.com/forum.php ... IO%5C_TypeDef%2B%2B
但是还是有一些疑问,想请各位大神给个解答。
首先谈下个人的理解:宏定义就是预处理的时候将出现GPIOA的全部替换为(GPIO_TypeDef *) GPIOA_BASE
GPIO_TypeDef是一个结构体,里面包含着7个IO端口的寄存器。
(GPIO_TypeDef *) GPIOA_BASE这个语句的意思是将GPIOA_BASE(参照上面的帖子好像是个地址)强制转换为结构体指针变量。
这样GPIO_A就可以访问GPIO_TypeDef结构体中的成员。
那么我有以下几个疑问:
1.地址能够强制转换为指针变量?(百度上说是可以的)
2.即时地址(GPIOA_BASE)能够强制转换为结构体指针变量,那么结构体指针变量需要指向一个变量的地址(指针变量是装地址的)。例如:int a=3;  int * p; p=&a;  那么被强制转换后的结构体指针变量,它指向的是哪个地址?
3.是不是可以这样理解GPIOA_BASE被强制转换为结构体指针变量,那么这个结构体指针变量指向的地址还是原来GPIOA_BASE的地址。而这个地址又是IO端口第一个寄存器的地址?
深夜发帖,必是深思熟虑。刚刚踏入stm32,还请各位感受指点。

最佳答案

查看完整内容[请看2#楼]

GPIOA_BASE实际上就是一串数字,该数字被当作地址的时候,地址总线会按照该数字映射。 比如假定GPIOA_BASE是0xD6(二进制的11010110),那么地址总线的A0到A7就会表现为(高高低高低高高低),此时再有WR之类的信号,那么对应地址的数据就会被搬运到数据总线上(参考一下SRAM芯片的管脚功能)。 再说结构体强制转换的问题。基本上大多地址都是连续的,拿上面的GPIOA_BASE来说,其地址为0xD6,后面还有很多内存空间0xD7、0xD8、0xD ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

33

主题

984

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
8017
金钱
8017
注册时间
2014-8-13
在线时间
1594 小时
发表于 2018-4-4 00:53:44 | 显示全部楼层
本帖最后由 mack13013 于 2018-4-4 08:16 编辑

GPIOA_BASE实际上就是一串数字,该数字被当作地址的时候,地址总线会按照该数字映射。
比如假定GPIOA_BASE是0xD6(二进制的11010110),那么地址总线的A0到A7就会表现为(高高低高低高高低),此时再有WR之类的信号,那么对应地址的数据就会被搬运到数据总线上(参考一下SRAM芯片的管脚功能)。

再说结构体强制转换的问题。基本上大多地址都是连续的,拿上面的GPIOA_BASE来说,其地址为0xD6,后面还有很多内存空间0xD7、0xD8、0xD9....我们描述0xD7、0xD8、0xD9的时候是按照一个字节一个字节的描述,
但是我并不想这样使用,因为不方便,我本意是这样的,在0xD7开始的2个字节内放一个short类型的数字(可能是ADC转换后的数值之类),然后再放一个byte类型的数字(可能用来标记数据的来源,比如ADC通道1),
之后再放一个byte类型的数字(可能用来标记什么东西),最后放一个int类型的数字(可能是某种需要存储的数据),这样,0xD6 0xD7放了一个short类型数据,0xD8、0xD9分别放了一个byte类型数据,从0xDA开始的
0xDA、0xDB、0xDC、0xDD这4个字节合起来我存储一个int类型的数据。那我怎么访问这个怪了巴基的东西呢?总不能一个字节一个字节的读出,然后前面两个字节凑成short,最后4个字节凑成int吧?我们可以直接告
诉编译器,我要按照2、1、1、4的方式读取数据,那么我们就可以定义一个下面这样的结构体
[mw_shl_code=c,true]
typedef struct  _ACTION_DESCRIPTOR
{
  short wActType;
  char cSrcValue;
  char cSrcType;
  int nDstValue;
} ACTION_DESCRIPTOR, *PACTION_DESCRIPTOR;[/mw_shl_code]
同时告诉编译器,我要从0xD6开始读取8个字节,并且按照2、1、1、4也就是ACTION_DESCRIPTOR这种结构体的"格式"来读取或者写入数据。语法上的写法就是 (ACTION_DESCRIPTOR*)0xD6,也就是强行将0xD6转化为
ACTION_DESCRIPTOR类型的指针(地址)。


至于int a=3;  int * p; p=&a;这个问题,
执行int a = 3;时,系统会分配一个地址比如0xAA,从0xAA开始的0xAA、0xAB、0xAC、0xAD四个字节存放3这个整形数字,
执行int *p;时,系统分配一个地址比如0xE0,从0xE0开始的0xE0、0xE1、0xE2、0xE3四个字节(假定32位寻址)"准备"存放p的内容。
执行p=&a;时,我们知道&a的结果是0xAA(在执行int a = 3;时,由系统分配的),那么这句执行之后的结果就是从0xE0开始的0xE0、0xE1、0xE2、0xE3四个字节存储了0xAA。

回复

使用道具 举报

10

主题

196

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
390
金钱
390
注册时间
2018-3-20
在线时间
80 小时
发表于 2018-4-4 07:16:41 来自手机 | 显示全部楼层
1、3合起来说,就是任意一串整数数字,都可以强制转成指向刚才整数为地址的指针,指针类型你可以随意定,
回复

使用道具 举报

10

主题

196

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
390
金钱
390
注册时间
2018-3-20
在线时间
80 小时
发表于 2018-4-4 07:18:48 来自手机 | 显示全部楼层
比如要指向内存,那就把0x20000000强制转成指针即可。指向flash,就用0x8000000强制转一个甚至可以做加减运算过的数字
回复

使用道具 举报

5

主题

40

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
229
金钱
229
注册时间
2016-7-30
在线时间
82 小时
发表于 2018-4-4 08:37:40 | 显示全部楼层
GPIOA_BASE包括其他IO口这个是确定的硬件寄存器入口(基地址),结构体里面的是地址偏移量,每个IO的寄存器偏移量应该是一致的,只需要把基地址类型转化为结构体指针,才能直接通过结构体的方式很方便的访问偏移量(也就是每个IO口相关的寄存器)
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 09:23:54 来自手机 | 显示全部楼层
rmrmrm 发表于 2018-4-4 08:37
GPIOA_BASE包括其他IO口这个是确定的硬件寄存器入口(基地址),结构体里面的是地址偏移量,每个IO的寄存器 ...

谢谢你的回答,GPIO_BASE(基地址)被强制转化为结构体指针变量,但是指针变量不得指向一个地址,那么基地址被强制转化为结构体指针变量后,结构体指针变量是不是指向本身GPIO_BASE的地址?如我问题3所述的那样
回复

使用道具 举报

5

主题

30

帖子

0

精华

初级会员

Rank: 2

积分
106
金钱
106
注册时间
2018-3-15
在线时间
54 小时
发表于 2018-4-4 09:31:13 | 显示全部楼层
推荐你阅读《C Primer Plus》,懂得指针到底是个啥,以上几个问题自会迎刃而解。个人愚见两个主要特点:1.指针是个指向某一起始地址的一个变量 2.这一起始地址后面的多长内容属于这个指针自己的地盘,取决于指针类型:即int * 还是char *  亦或GPIO_TypeDef *......(这个很重要)
对于你的问题:1.指针变量的值本身就是地址
                     2和3:GPIOA_BASE并没有被强制转换成别的东西,只是把GPIOA_BASE这个地址从单纯数字转换为GPIO_TypeDef *类型指针的起始地址,然后宏替换给GPIOA   就像a=(int)b;    b起始没被转换,还是b,只是转换的结果付给a

第一次回答问题,错误之处望海涵
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 09:31:28 来自手机 | 显示全部楼层
孟亮 发表于 2018-4-4 07:16
1、3合起来说,就是任意一串整数数字,都可以强制转成指向刚才整数为地址的指针,指针类型你可以随意定,

基地址是个地址,本身也就是由0和1组成的。那么按着你说的,假设GPIO_BASE的地址为010101(假设),那么GPIO_BASE被强制转化为结构体指针变量,那么结构体指针变量是不是就是指向010101这个地址了?
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 09:35:20 来自手机 | 显示全部楼层
孟亮 发表于 2018-4-4 07:16
1、3合起来说,就是任意一串整数数字,都可以强制转成指向刚才整数为地址的指针,指针类型你可以随意定,

(GPIO_TypeDef *) GPIOA_BASE通过这个强制转化将GPIOA_BASE(基地址)转化为结构体指针变量,那么此时结构体指针变量名是不是就是GPIOA_BASE它了?比如int *p   p就是指针变量名
回复

使用道具 举报

5

主题

40

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
229
金钱
229
注册时间
2016-7-30
在线时间
82 小时
发表于 2018-4-4 09:54:20 | 显示全部楼层
少说多做 发表于 2018-4-4 09:23
谢谢你的回答,GPIO_BASE(基地址)被强制转化为结构体指针变量,但是指针变量不得指向一个地址,那么基 ...

基地址被强制转换成结构体指针后,就相当于从这个基地址开始往下申请一段内存空间,大小由此结构体成员变量类型决定,然后你可以用结构体变量访问这些地址里面的数据,就相当于更改寄存器
回复

使用道具 举报

10

主题

196

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
390
金钱
390
注册时间
2018-3-20
在线时间
80 小时
发表于 2018-4-4 10:11:29 | 显示全部楼层
本帖最后由 孟亮 于 2018-4-4 10:17 编辑
少说多做 发表于 2018-4-4 09:35
(GPIO_TypeDef *) GPIOA_BASE通过这个强制转化将GPIOA_BASE(基地址)转化为结构体指针变量,那么此时结 ...

这里不是。要想达到你要的效果,需要#define  GPIOA        (GPIO_TypeDef *) GPIOA_BASE
这样,此结构体指针的名字 才是  GPIOA

或者你做声明  GPIO_TypeDef *    GPIO_myown =(GPIO_TypeDef *) GPIOA_BASE;把值赋给某个内存变量名,你上面仅仅是强制转换,结果是个值,类似于123,要赋给左值才可以保存下来。


回复

使用道具 举报

10

主题

196

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
390
金钱
390
注册时间
2018-3-20
在线时间
80 小时
发表于 2018-4-4 10:19:23 | 显示全部楼层
少说多做 发表于 2018-4-4 09:31
基地址是个地址,本身也就是由0和1组成的。那么按着你说的,假设GPIO_BASE的地址为010101(假设),那么G ...

是假设  GPIO_BASE=010101,或者就是010101,再强制转换,就会变成指向010101(内存,寄存器,flash,外设)地址的指针
回复

使用道具 举报

7

主题

199

帖子

0

精华

高级会员

Rank: 4

积分
711
金钱
711
注册时间
2017-5-20
在线时间
96 小时
发表于 2018-4-4 10:35:36 | 显示全部楼层
本帖最后由 林光华ing 于 2018-4-4 10:49 编辑

1.        
指针就是专门针对地址的变量。这是C语言的很基础的知识。你的问题都是C语言里面的东西。
在32位MCU中,一个不超过U32的数字,当然可以转化为指针。对一个数字取*,就是取这个地址的值。而取多少,怎么取出来,是编译器根据的强制类型去操作。
(GPIO_TypeDef *) GPIOA_BASE。这里就很直观。
GPIOA_BASE是一个确定的数字(地址),结果值以GPIO_TypeDef的形式去取出来(其实就是看这个结构体多大,以GPIOA_BASE为基础地址,连续取后面sizeof(GPIO_TypeDef )字节的内容)。
需要注意的是,有些编译器可能不太支持过于复杂的指针变形。。。这个时候不能一步到位,要一步步去变形。。。
2.
就是指向变量a的内存地址
3.
你的理解是OK的
回复

使用道具 举报

7

主题

162

帖子

0

精华

高级会员

Rank: 4

积分
541
金钱
541
注册时间
2017-4-6
在线时间
67 小时
发表于 2018-4-4 11:13:48 | 显示全部楼层
借楼问个问题:
我上午本来打了很长一段字打算回答这个问题,后面忽然想到了一个问题,就没有回答了。
这个问题是:
为什么使用 GPIO_TypeDef *,而不是GPIO_TypeDef 来强制转换这个GPIO_BASE? 原来是GPIOA->,改成后面的写法就变成GPIO.这样难道不行吗?
我现在大概有个模糊的答案,不过希望还是有人来解惑,或许比较简单,可能是我想的复杂了,请高手不吝赐教
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 20:43:19 | 显示全部楼层
mack13013 发表于 2018-4-4 08:10
GPIOA_BASE实际上就是一串数字,该数字被当作地址的时候,地址总线会按照该数字映射。
比如假定GPIOA_BASE ...

谢谢了,我感觉我应该懂了。谢谢各位大神
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 20:45:39 | 显示全部楼层
StayHungry123 发表于 2018-4-4 09:31
推荐你阅读《C Primer Plus》,懂得指针到底是个啥,以上几个问题自会迎刃而解。个人愚见两个主要特点:1.指 ...

谢谢你的回答,给了我一些启发。
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 20:47:16 | 显示全部楼层
孟亮 发表于 2018-4-4 10:11
这里不是。要想达到你要的效果,需要#define  GPIOA        (GPIO_TypeDef *) GPIOA_BASE
这样,此结构 ...

我想我想明白了
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 20:49:49 | 显示全部楼层
楼上的各位大神,谢谢你们的解答,你们每个人说的都很有道理。建议和我有同样疑惑的同学们,都看看楼上大神的说法。
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 20:51:05 | 显示全部楼层
林光华ing 发表于 2018-4-4 10:35
1.        
指针就是专门针对地址的变量。这是C语言的很基础的知识。你的问题都是C语言里面的东西。
在32 ...

谢谢大神的帮忙
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 20:54:11 | 显示全部楼层
mack13013 发表于 2018-4-4 08:10
GPIOA_BASE实际上就是一串数字,该数字被当作地址的时候,地址总线会按照该数字映射。
比如假定GPIOA_BASE ...

谢谢了,感觉讲的很好
回复

使用道具 举报

3

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2018-3-11
在线时间
11 小时
 楼主| 发表于 2018-4-4 20:58:20 | 显示全部楼层
我觉得你们每个人都说的很好,但是只能设置一个最佳答案。对不起了
回复

使用道具 举报

10

主题

196

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
390
金钱
390
注册时间
2018-3-20
在线时间
80 小时
发表于 2018-4-5 08:26:09 来自手机 | 显示全部楼层
JUSTNIUB 发表于 2018-4-4 11:13
借楼问个问题:
我上午本来打了很长一段字打算回答这个问题,后面忽然想到了一个问题,就没有回答了。
这 ...

个人感觉,是因为C不支持ref 和 out 关键词,这样形参传入函数时就没法被直接更改,甚至会重新分配内存。因此采用*做参数,内存开销小,同时省去每次要传入需要修改的参数时,&+变量名 的操作。有些例子Gpio_typedef 后面就是不带*的,但是使用时要&一下传入。总体感觉最重要的就是直接传入地址,比传入整个结构体,省内存~
回复

使用道具 举报

7

主题

162

帖子

0

精华

高级会员

Rank: 4

积分
541
金钱
541
注册时间
2017-4-6
在线时间
67 小时
发表于 2018-4-8 09:49:21 | 显示全部楼层
孟亮 发表于 2018-4-5 08:26
个人感觉,是因为C不支持ref 和 out 关键词,这样形参传入函数时就没法被直接更改,甚至会重新分配内存。 ...

谢谢回答,不过你可能理解错我的意思了,我的意思是在定义GPIOA这个宏时,为什么不使用
#define  GPIOA        (GPIO_TypeDef ) GPIOA_BASE。
你其实解释的是做为变量的使用了,做为变量的话实际上它已经有分配内存,至于怎么寻址其实根据具体需求来定。而你说的不带*的定义,在我的印象里STM32的库函数中,这类类型大多数都不是直接与寄存器相关的。

回复

使用道具 举报

10

主题

196

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
390
金钱
390
注册时间
2018-3-20
在线时间
80 小时
发表于 2018-4-8 10:02:16 | 显示全部楼层
JUSTNIUB 发表于 2018-4-8 09:49
谢谢回答,不过你可能理解错我的意思了,我的意思是在定义GPIOA这个宏时,为什么不使用
#define  GPIOA  ...

#define PERIPH_BASE           ((uint32_t)0x40000000)
#define APB1PERIPH_BASE       PERIPH_BASE
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
他之前仅被定义为一个(uint32_t)整数,
你再强制转换成
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;
7个(uint32_t)数,会被报错或溢出之类的吧。。。。。
回复

使用道具 举报

7

主题

162

帖子

0

精华

高级会员

Rank: 4

积分
541
金钱
541
注册时间
2017-4-6
在线时间
67 小时
发表于 2018-4-9 09:01:01 | 显示全部楼层
孟亮 发表于 2018-4-8 10:02
#define PERIPH_BASE           ((uint32_t)0x40000000)
#define APB1PERIPH_BASE       PERIPH_BASE
# ...

嗯,你这个说的有道理,两个类型是不同的。这两个类型是无法互相转换的,所以只能采用*的方式。不过我最近在看汇编,一下子没想到这点,一直在纠结它们在汇编时的表现形式,我看到的大多数都是采用 LDR R1,[R0,#offset]这种表现形式,跟指针的用法非常像,下意识的就把结构体变量与指针弄混了。
回复

使用道具 举报

5

主题

18

帖子

0

精华

初级会员

Rank: 2

积分
96
金钱
96
注册时间
2019-5-11
在线时间
14 小时
发表于 2019-5-11 11:21:09 | 显示全部楼层
你第二条的意思是,就像你举得那个例子那样,最后需要P=&a这个操作来,表明指针P是指向变量a的地址,但是((GPIO_TypeDef *) GPIOA_BASE)这里,只是将GPIOA_BASE强制转换为(结构体)指针,相当于int * p的操作,但没有进行把这个新的指针变量GPIOA_BASE进行指向哪个变量的操作(相当于缺少p=&a)这个操作,所以就奇怪它指到哪里去了?
这里的事实是他就指向了自身的地址,那是C语言中某个地方规定的吗:对一个地址进行强制转换为指针,如果不对他进行指向操作,那么它就指向它本身的地址??
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-5 09:46

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表