显然楼主即将毕业了?而且不幸的是曾经有一个杯具的C语言老师,杯具在于他或她对指针的理解是泛泛的。
第一个问题讲明白了,后面的问题就不是问题了。
从最简单的地方讲起吧,学C的时候,基本都在PC上面做实验,一个最简单的例子:定义一个无符号整形变量x,并给x赋初值5。
unsigned int x=5;
太简单了。
下一个问题:x存储在哪儿了?也就是说变量x对应的存储器地址是多少?
当年我可能回答:不知道,而且并且显然x在哪儿关我屁事啊?它能给我存储数据就行了!
当然,可以有办法知道x对应的存储器地址,不管这关不关我屁事或别的事。
unsigned int * y=&x;
y里面放的就是x的地址了,而且像下面这样就可以把这个地址显示出来。
printf("x的地址=%d\n", (unsigned int)y);
下一个问题:现在想给x赋值6啦,程序只有类似“x=6;”这一种写法吗?
回答:至少还有另一类写法。*y=6;
吃饱了撑的!当年我的第一感觉。
后来我知道这绝对不是吃饱了撑的,原因并不是因为我没吃饱。
下面要说到问题的本质:
1.在PC上练习C的时候,老师以及教材主要教你如何直接给变量们赋值,至于这些变量们存储在哪儿,很多情况下没必要操心(在PC主存里,系统会自动给变量找到安身之所),从而通过指针间接地给变量赋值也就讲不透彻了,以至于有些情况下必须使用指针的问题也讲得稀里糊涂了(比如数组作为函数入口参数的时候)。
2.在嵌入式系统中有一个很大的不同:我们感兴趣的很多数据,不是存放在变量里面,而是存放在寄存器里面。这些寄存器有固定的存储地址并跟主存是统一编址的(于是寄存器可以理解为存储单元),这些存储器里面的数据就不能通过变量方式来读写了(因为变量的地址是不能指定的,而我们想读写指定地址的寄存器里面的数据)。
也就是说,PC里面我们习惯读写“不必知道其存储地址的”变量的值;嵌入式系统里面,我们时常需要读写“指定地址的”存储单元中的值!
这是曾经学过PC的C转而要弄embed的C必须留意的第一步。
好了,哗啦一下急速回到第一个问题:
#define GSTATUS1 (*(volatile unsigned int *)0x560000B0)
基本可以断定,0x560000b0是某个寄存器的地址,在特定的芯片里面,类似这样的寄存器估计得有个几百甚至几千个(比如某种arm芯片吧,arm外围功能强大啊,所以片上外围功能部件很多,相应的寄存器也会很多很多),所以千万别幻想着用51单片机的风格,通过“寄存器名称”访问这些寄存器。
好了,现在想把0x560000b0号寄存器里面的值读出来放到变量a里面,怎么读呢?
unsigned int a=*(volatile unsigned int *)0x560000B0;
这样就行了。
行倒是行了,太恶心了,几百几千个寄存器啊,没准我的系统里会访问哪些寄存器呢!难不成我得把这几百甚至几千个寄存器的地址都记住或者至少我得一个一个从数据手册上查找这些王八蛋寄存器的地址去(真这样的话,就真的要恼火到说粗话了)?
不必恶心,第一个问题里面这样写,就是为了解决这个恶心的,有了这一行,读这个0x560000B0号寄存器就可以这样了:
unsigned int a=GSTATUS1;
想给0x560000B0号寄存器赋值0x55aa55aa,可以这样写:
GSTATUS1=0x55aa55aa;
从视觉感受上说,很像PC的C里面访问变量了,但是本质上是两码事。
每种嵌入式系统主芯片,几乎无一例外地都有官方代码库,里面把所有寄存器都以类似风格用宏定义表示好了,于是我们可以通过宏名称而不是地址来访问这些寄存器了,顺便说一下,宏名称一般都由特定物理意义的单词或简称构成。
顺便解释一下volatile。从宏定义名称GSTATUS1来看,里面包含单词STATUS,基本断定0x560000B0号寄存器是一个状态寄存器,也就是是说,它时时刻刻反应着对应子系统的运行状态,里面的值一直在变化,读回来的值不一定是刚刚写进去的值(也可能不能写,是只读的),连续两次读回来的值也可能不一样。
实际上,大多数情况下,是若干个地址连续的寄存器共同配合完成特定的功能,所以进一步地就是结构以及指向结构的指针。。。。不说了
|