打印

(转)C中的绝对地址及外设寄存器访问(仅供参考)

[复制链接]
3522|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
米其林r|  楼主 | 2011-2-26 22:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
#define XBYTE ((unsigned char volatile xdata *) 0)
解释:XBYTE被定义成(unsigned char volatile xdata *) 0,0为一个基于寄存器的指针(可理解为:只不过这个指针用一个二进制常量标识,且其指向的内存单元在定义时就以固定为该二进制常量且不可更改.),其存储内型为2指向xdata,偏移量为0,这样XBYTE成为指向xdata零地址的指针,通过XBYTE[0]可以读写该地址处的数据;同样通过XBYTE[8000]也可以访问xdata空间绝对地址为8000处的数据。
待证中:通过*XBYTE,*(XBYTE+8000)是否可以对相应地址进行读写?

ARM_ADS:
#define BCFG0  (*((volatile unsigned int *) 0xFFE00000))
解释:首先0xFFE00000为一个指针没错,且该指针为unsigned int型指向0xFFE00000内存单元,BCFG0被定义为对该指针做“*”指针运算,这样,BCFG0可理解为对内存单元0xFFE00000进行标识的一个int32u型普通变量,同过BCFG0可是实现对该寄存器的读写访问。

转载网络上的一些**:
------------------------------------------------------------------------
理解#define SREG    (*(volatile unsigned char *)0x5F)
这样的定义,总是感觉很奇怪,不知道为什么,今天终于有了一点点心得,请大虾们多多批砖~~~

   嵌入式系统编程,要求程序员能够利用C语言访问固定的内存地址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。所以,知道要访问的内存地址后,比如0x5F,
   第一步是要把它强制转换为指针类型
(unsigned char *)0x5F,AVR的SREG是八位寄存器,所以0x5F强制转换为指向
unsigned char类型。
   volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意想不到。
   第二步,对指针变量解引用,就能操作指针所指向的地址的内容了
   *(volatile unsigned char *)0x5F
   第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯,所以#define SREG    (*(volatile unsigned char *)0x5F)

    类似的,如果使用一个32位处理器,要对一个32位的内存地址进行访问,可以这样定义#define RAM_ADDR     (*(volatile unsigned long  *)0x0000555F)
    然后就可以用C语言对这个内存地址进行读写操作了
    读:tmp = RAM_ADDR;
    写:RAM_ADDR = 0x55;
zhiwei 发表于 2005-4-30 18:59 AVR 单片机

定义未volatile是因为它的值可能会改变,大家都知道为什么改变了;
如果在一个循环操作中需要不停地判断一个内存数据,例如要等待SREG的I标志位置位,因为SREG也是映射在SRAM空间,为了加快速度,编译器可能会编译出这样的代码:把SREG读取到Register中,然后不停地判断Register相应位。而不会再读取SREG,这样当然是不行了,因为程序或其它事件(中断等)会改变SREG,结果很可能是一个死循环出不来了。如果定义成volatile型变量,编译的代码是这样的:每次要操作一个变量的时候都从内存中读取一次。
#define SREG (*(volatile unsigned char *)0x5F) 之后,可以进行如下基本操作,
unsigned char temp,*ptr;
temp=SREG;把SREG值保存到temp中
SREG=temp;把temp的值赋给SREG
ptr = & SREG; 不知对否,大家试一下。

-----------------------------------------------------------------------
对于(volatile unsigned char *)0x20我们分析一下,它是由两部分组成:
1)(unsigned char *)0x20,0x20只是个值,前面加(unsigned char *)表示0x20是个地址,而且这个地址类型是unsigned char ,意思是说读写这个地址时,要写进unsigned char 的值,读出也是unsigned char 。
2)volatile,关键字volatile 确保本条指令不会因C 编译器的优化而被省略,且要求每次直接读值。例如用while((unsigned char *)0x20)时,有时系统可能不真正去读0x20的值,而是用第一次读出的值,如果这样,那这个循环可能是个死循环。用了volatile 则要求每次都去读0x20的实际值。那么(volatile unsigned char *)0x20
是一个固定的指针,是不可变的,不是变量。而char  *u则是个指针变量。再在前面加"*":*(volatile unsigned char *)0x20则变成了变量(普通的unsigned char变量,不是指针变量),如果#define i (*(volatile unsigned char *)0x20),那么与unsigned char i是一样了,只不过前面的i的地址是固定的。
=》(*(volatile unsigned char *)0x20)可看作是一个普通变量,这个变量有固定的地址,指向0x20。而0x20只是个常量,不是指针更不是变量

======================================|

#define     CAN1_BASE_ADDR      0xE0044000
#define     CAN1MOD              (*((volatile unsigned long *)(CAN1_BASE_ADDR + 0x00)))
1.指针  一个指针是一个地址,是一个常量。而一个指针变量却可以被赋予不同的指针值,是变量。在程序中用“*”符号表示“指向”,例如,i_pointer代表指针变量,而*i_pointeri_pointer所指向的变量的值。
2.强制类型转换
首先,(CAN1_BASE_ADDR + 0x00)表示一个16进制的数据,前面加上(volatile unsigned long *)进行数据到指针的强制类型转换,((volatile unsigned long *)(CAN1_BASE_ADDR + 0x00))将原来的16进制数据变为地址,前面再加一个“*”(表指向)号,表示指向地址为(CAN1_BASE_ADDR + 0x00)的单元的内容。

补充:记住核心原则--C语言里面的强制类型不改变内存数据。
    比如我们可以(int)'A',这样转换后的结果为A的ASCII码数值,因为那块内存本来就存的那个数,只是换个形式使用而已。
    知道上面的原则,我们可以对任何数据类型进行转换,但是转换的结果可能不是你想像的结果,举例(int)'9'的结果为多少?不是9而是0x39。来个高深点的printf("%d",'12');的输出是什么?正确答案是12849,知道是怎么来的吗,提示你0x3231,涉及INTEL的字节地址交换。
    C语言中,在函数体中或在函数体外部定义的数组名可以认为是一个存放地址值的指针变量名,其中地址值是数组第一个元素的地址,也就是数组所占一串连续存储单元的起始地址,定义数组时的类型即是此指针变量的基类型,重要的是:这个指针变量中的地址值不可改变,也就是说,不可以给数组名重新赋值!因此,也可以认为数组名是一个地址常量。

相关帖子

沙发
msp430ing| | 2011-2-27 10:15 | 只看该作者
看完这个对阅读程序相当有帮助

使用特权

评论回复
板凳
FVJFIFE| | 2011-2-27 11:10 | 只看该作者
好久没来好资料真多,都看不过来了

使用特权

评论回复
地板
xsgy123| | 2011-2-27 13:30 | 只看该作者
说的很好

使用特权

评论回复
5
金鱼木鱼| | 2011-3-31 16:28 | 只看该作者
学习了不错!

使用特权

评论回复
6
pkat| | 2011-3-31 18:49 | 只看该作者
值得参考

使用特权

评论回复
7
加班加点| | 2011-4-1 21:09 | 只看该作者
很有价值

使用特权

评论回复
8
csq463276932| | 2011-4-13 17:00 | 只看该作者
学习了:handshake

使用特权

评论回复
9
21IC_wyz| | 2011-6-26 18:23 | 只看该作者
不错

使用特权

评论回复
10
21IC_wyz| | 2011-6-26 18:26 | 只看该作者
不错。只是这句我有点不理解,
“读:tmp = RAM_ADDR;”读操作时,应该是“读:tmp = * RAM_ADDR;”吧,就是读出内存某个地址中的数值。

使用特权

评论回复
11
bit6019| | 2011-6-26 20:40 | 只看该作者
看来没有MMU的芯片还是好入手一些,实在一些啊!

使用特权

评论回复
12
即时生效| | 2011-6-29 22:48 | 只看该作者
mark

使用特权

评论回复
13
muyueye| | 2012-9-6 22:25 | 只看该作者
值得学习!

使用特权

评论回复
14
muyueye| | 2012-9-6 22:27 | 只看该作者
真心希望能解释一下,为什么那个'12'会是12849

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

340

主题

1587

帖子

3

粉丝