高级会员
- 积分
- 658
- 金钱
- 658
- 注册时间
- 2013-11-22
- 在线时间
- 131 小时
|
发表于 2018-2-2 09:36:17
|
显示全部楼层
本帖最后由 zhp 于 2018-2-2 14:44 编辑
为了彻底弄清这个问题,让我们来看看下面的问题
如下两个表达式,怎样去计算它的结果?
它的结果到底是什么?
(1)结构体变量.成员变量
(2)结构体变量指针->成员变量
首先要明白(1)中的结果是一个变量值,类似 int a 一样,都是变量;
(2)中的结果是一个指针指向的值,跟普通的 *(int *a) 也一样,最终结果是变量。
对编译器来说,要知道一个变量的值,必须先找到它的地址,再确定该变量的类型,
知道了类型,自然就知道数据的字节长度了;而要知道一个指针,就是找到它的地址
即可,这时候的地址即可理解为指针,通过指针指向的地址去找它所存储的变量。
在(1)中的结果是个变量:
要确定它的值,就得先确定它的地址,再确定它的变量类型(即数据占用的字节长度)
它的地址 = 结构体变量首地址 + 成员变量在结构体中的偏移地址
它的类型 = 成员变量前面的类型修饰符
至于成员变量的类型也可为普通类型,比如 int, short, char 等,甚至它也可以为结构体
类型、联合类型、枚举类型等。
在(2)中的结果是个指针(即地址)指向的变量:
只要确定出它的地址即可
它的地址 = 结构体变量首地址 + 成员变量在结构体中的偏移地址它的类型 = 成员变量前面的类型修饰符
它的结果就有点类似于:*(地址)
对地址取* ,结果是变量
在某些代码中,你甚至可以看到以下这样的表达:
(3)( (结构体类型 *)0 )->成员变量
这是什么意思呢?它的计算结果是一个地址?!对,它是个地址
将0地址强制转换成一个结构体类型,通过结构体类型就可以访问结构体
内部的成员变量,说它是一个地址,那么它的地址是多少呢?
(3)的地址 = 0 + 成员变量在结构体中的偏移地址
= 成员变量在结构体中的偏移地址
通过上面的计算,可以看出(3)的结果是一个地址,它的大小刚好等于成员变量
在结构体中的偏移地址,所以(3)这种表达式也有一个特殊用途,就是用
来计算一个结构体成员在结构体中的字节偏移量,所以也可以把它当做一个
变量来使用。
接着分析:
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
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;
GPIOA_BASE =0x40000000 + 0x10000 + 0x0800 = (uint32_t) 0x40010800
GPIOA->ODR = ( (GPIO_TypeDef *)0x40010800) ->ODR
= *(0x40010800 + 4*3)
= *(0x40010800 + 12)
= *((GPIO_TypeDef *)0x4001080C)
好,我们来查看STM32F10X的数据手册,查看存储器映像来核对一下 0x4001080C 这个地址
file:///C:/Users/Administrator/Documents/My%20Knowledge/temp/523943fc-1ee0-42ef-b893-70e450d0dbe6/128/index_files/ad3edc74-58d7-4a84-92ab-bf9ee189544b.png
可以看出GPIO端口A的基地址为:0x40010800
再看ODR在结构体内的偏移地址
file:///C:/Users/Administrator/Documents/My%20Knowledge/temp/523943fc-1ee0-42ef-b893-70e450d0dbe6/128/index_files/d206c303-06b4-4027-a561-202fe00b65b3.png
可以看出GPIOx_ODR的偏移为:00CH (x是统称,可以是A,B,C,D,E,F,G)
那么 从手册中,我们可以看出 GPIOA->ODR 最终地址为:0x40010800 + 00ch = 0x4001080C
跟前面的宏展开计算结果一致!那么
GPIOA->ODR = VAL;
就等效于
(*(volatile uint32_t*)(0x4001080C)) = VAL;
|
|