xdata是large存储类型,volatile是通过硬件来改变指针指向的内容。
#define XBYTE ((unsigned char volatile xdata *) 0)
定义 XBYTE 为 指向 xdata 地址空间unsigned char 数据类型的指针,指针值为0
这样,可以直接用XBYTE[0xnnnn]或*(XBYTE+0xnnnn)访问外部RAM了。
至于 volatile的作用就是让编译器不至于优化掉它的操作:
volatile修饰的变量,对于读操作来说,其值是不可预知的;对于写操作来说,即使是写入两个同样的值,影响也是不一样的。所以编译器不会对访问该变量的操作优化。如果不加volatile关键词,有些编译器会将一些它认为无意义的或者无效的操作给优化掉,但实际上这并不是你要的结果。例如一些特殊的寄存器,一些IO等。
I=0;
I=0; //如果I未声明成volatile的,则可能被优化掉
if(I==1) //如果I未声明成volatile的,则可能被优化掉
{
}
volatile [v?l?tail] adj. 易变的, 反复无常的, 易激动的,(液体或油)易挥发的
使用XBYTE时,初始化方法是XBYTE[0xXXXX],这是C51中专有的东西,在C51编程中只要有外部扩展RAM就得这样用。在51单片机中,RAM空间有内外之分,而内部RAM与外部256字节的RAM同地址,在有扩展外部RAM的系统中,有的会用上外部RAM的前256字节,有的前256字节没有用。以上的定义就是用上了前256字节,也就是说外部RAM从0地址开始寻址。
//8051 特有的内存型态
code 以 MOVC @A+DPTR 读取的程序内存
data 可以直接存取的内部数据存储器
idata 以 Mov @Rn 存取的内部数据存储器
bdata 可以位寻址(Bit Addressable)的内部存储器
xdata 以 MOVX @DPTR 存取的外部数据存储器
pdata 以 MOVX @Rn 存取的外部数据存储器
特殊资料型态
bit 一般位(bit)变量
sbit 绝对寻址的位(bit)变量
语法:sbit my_flag = location; (location 范围从 0x00 ~ 0x7F)
范例:sbit EA = 0xAF;
或是配合 bdata 宣告的位(bit)变量
char bdata my_flags;
sbit flag0 = my_flags ^ 0;
(注意 sbit 前不可以加 static)
sfr 特殊功能缓存器(Special Function Register)
语法:sfr my_sfr = location; (location 范围从 0x80 ~ 0xFF)
范例:sfr P0 = 0x80;
关于XBYTE的问题
2009-08-30 20:43
以前还真没好好琢磨过它 以前还真没好好琢磨过它 从其它嵌入式的C带来的习惯,一直使用*(unsigned char volatile xdata *)(0x2000)=0xFF这类的方式来进行对外部绝对地址的字节访问。 刚才查了一下ABSACC.h,XBYTE的定义是这样的, #define XBYTE ((unsigned char volatile xdata *) 0), 其实XBYTE就相当于一个指向外部数据区的无符号字符型变量的指针(的名称,且当前的指针指向外部RAM的0地址),而在C里面指针一般和数组是可以混用的。这样也就相当于存在 一个定义在外部数据存储器的数组XBYTE[65536],直接使用下标就可访问其中的每个单元。 //下面是猜想,欢迎大家验证 这里的XBYTE是定义在0地址的,如果将其定义在其它地址则可以禁止通过XBYTE对低端存储区的访问,如 #define XBYTE ((unsigned char volatile xdata *) 0x2000), 则XBYTE[9]访问的应是0X2009这一外部RAM的单元,而不是外部RAM的0x0009单元的内容,因为数组访问不能使用负数下标,所以通过这种方式就无法访问到小于0x2000的外部RAM单元的内容了。 TOP 发送短消息 查看公共资料 查找该会员全部帖子 UID8917 精华 0 威望100 金钱0 元 来自 状态 离线 ayb_ice3237 组别新手上路 性别男 积分489 帖子793 注册时间 2006-08-19 ayb_ice3237 2007-08-22 09:13 树型| 收藏| 小 中 大 4 # 啥叫"而在C里面指针一般和数组是可以混用的"... 啥叫"而在C里面指针一般和数组是可以混用的"... 应试说本质是相同的... TOP 发送短消息 查看公共资料 查找该会员全部帖子 UID8689 精华 0 威望100 金钱0 元 来自 状态 离线 computer004590 组别新手上路 性别男 积分1097 帖子964 注册时间 2006-08-13 computer004590 2007-08-22 09:13 树型| 收藏| 小 中 大 5 # 呵呵,倒过来写都行... 呵呵,倒过来写都行... 例如, 200[XBYTE]=xxx; char buff[10]; //或者用 char *buff=&buffer; buff[3]=0xaa; 3[buff]=0xaa; //居然是一样的,倒塌... 因此,我认为编译器是这么干的:对于形如xxx[yyy]这样的表达式,会转化为*(xxx+yyy), 因此写成xxx[yyy]或者写成yyy[xxx]都无所谓了...非典用法,请勿乱用,出了事偶不负责... TOP 发送短消息 查看公共资料 查找该会员全部帖子 UID1602 精华 0 威望100 金钱0 元 来自 状态 离线 ayb_ice3237 组别新手上路 性别男 积分489 帖子793 注册时间 2006-08-19 ayb_ice3237 2007-08-22 09:13 树型| 收藏| 小 中 大 6 # 本质就是强制指针变换,有什么神秘的... 本质就是强制指针变换,有什么神秘的...
TOP 发送短消息 查看公共资料 查找该会员全部帖子 UID8689 精华 0 威望100 金钱0 元 来自 状态 离线 infree5136 组别新手上路 性别男 积分660 帖子367 注册时间 2006-08-19 infree5136 2007-08-22 09:13 树型| 收藏| 小 中 大 7 # 个人觉得5楼的解释很合理 个人觉得5楼的解释很合理 佩服! 我记得以前这里也有过关于C语言的这类问题的讨论,可是当时没有进入到更深的层次,好像只讨论到5楼所提到的两种方式在C中均可使用的地步。呵呵,记得不是很清楚了。当然,这类问题可能对使用C语言的不同的人可能有不同的意义,有些人可能一辈子都不会遇到和想到,有人可能刚开始用C时就接触和理解了。不过我觉得把这个当成一个问题提出来是一个很好的态度。说出自己的见解也是一个很好的态度。 说实话,在这个论坛里我喜欢那些好为人师的人,也许正是因为有他们在,这里才有一点人气(虽然他们也有说错的时候)。 总之,学习了。 签: C51 XBYTE C51里面XBYTE的使用 1. 首先给个链接,C51的用户向导,http://www.keil.com/support/man/docs/c51/c51_xbyte.htm ,瞒有用。 2. 这个主要是在用C51的P0,P2口做外部扩展时使用,其中XBYTE [0x0002],P2口对应于地址高位,P0口对应于地址低位。一般P2口用于控制信号,P0口作为数据通道。 比如:P2.7接WR,P2.6接RD,P2.5接CS,那么就可以确定个外部RAM的一个地址,想往外部RAM的一个地址写一个字节时,地址可以定为XBYTE [0x4000],其中WR,CS为低,RD为高,那就是高位的4,当然其余的可以根据情况自己定,然后通过 XBYTE [0x4000] = 57; 这赋值语句,就可以把57写到外部RAM的0x4000处了,此地址对应一个字节。 系统分类: 单片机 | 用户分类: 单片机 | 来源: 原创 | 【推荐给朋友】 | 【添加到收藏夹】 阅读(4583) 回复(4) 投一票您将和博主都有获奖机会! 最新评论 王 2008/4/1 10:15:27 你好看了你的说明,我还是有很多不明白的地方,我是个新手,能不能指教一下? QQ:56222420 Email:56222420@qq.com cyzgood 2008/4/2 17:05:04 请把不明白的地方具体指出来 王 2008/4/5 11:12:54 P2.7接WR,P2.6接RD,P2.5接CS,那么就可以确定个外部RAM的一个地址 这是怎么确定地址的呢?就是说我现在知道硬件是怎么接的,怎么确定这个地址的值? cyzgood 2008/4/5 21:07:21 对于这个问题,个人理解是这样的:对于地址的确定,其实还是P0,P2口16根线确定的,当你执行XBYTE [0x4000] = 57;这条语句时,首先通过0x4000来确定16根线的状态,也就唯一确定了外部的一个地址,那么此时由于P2.7接WR,P2.6接RD,P2.5接CS,即WR为低,RD为高,CS为低,对应的是一个写状态,然后接着才会把57通过P0口写到0x4000这个位置. 如何理解XBYTE(转载) 默认分类 2008-08-07 23:40 阅读1124 评论0 字号: 大大 中中 小小 8051 特有的内存型态 code 以 MOVC @A+DPTR 读取的程序内存 data 可以直接存取的内部数据存储器 idata 以 Mov @Rn 存取的内部数据存储器 bdata 可以位寻址(Bit Addressable)的内部存储器 xdata 以 MOVX @DPTR 存取的外部数据存储器 pdata 以 MOVX @Rn 存取的外部数据存储器 特殊资料型态 bit 一般位(bit)变量 sbit 绝对寻址的位(bit)变量 语法 sbit my_flag = location; (location 范围从 0x00 ~ 0x7F) 范例 sbit EA = 0xAF; 或是配合 bdata 宣告的位(bit)变量 char bdata my_flags; sbit flag0 = my_flags ^ 0; (注意 sbit 前不可以加 static) sfr 特殊功能缓存器(Special Function Register) 语法 sfr my_sfr = location; (location 范围从 0x80 ~ 0xFF) 范例 sfr P0 = 0x80; 指定绝对地址的变量
在单一模块内可以使用下面的语法宣告 [memory_space] type variable_name _at_ location 范例 pdata char my_pdata _at_ 0x80; 如果该变量必须为多个模块所使用(Global Variable)则以 抽象指针(Abstract Pointer)的方式在标头档(Header File)定义较为方便。 #define variable_name *((data_type *) location) 范例 #define my_pdata *((char pdata *) 0x80) (注意 char 与 pdata 的顺序) ABSACC.H 提供了下列方便的宏(Macro)定义。 #define CBYTE ((unsigned char volatile code *) 0) #define DBYTE ((unsigned char volatile data *) 0) #define PBYTE ((unsigned char volatile pdata *) 0) #define XBYTE ((unsigned char volatile xdata *) 0) #define CWORD ((unsigned int volatile code *) 0) #define DWORD ((unsigned int volatile data *) 0) #define PWORD ((unsigned int volatile pdata *) 0) #define XWORD ((unsigned int volatile xdata *) 0) 隐藏的初始化程序 80C51 在电源重置后(Power On Reset)所执行的第一个程序模块并不是使用者的主程序 main(),而是一个隐藏在 KEIL-C51 标准链接库中称为 startup.a51 的程序模块。 startup.a51 的主要工作是把包含 idata、xdata、pdata 在内的内存区块清除为 0,并 且初始化递归指针。接着 startup.a51 被执行的仍然是一个隐藏在 KEIL-C51 标准链接库 中称为 init.a51 的程序模块。而 init.a51 的主要工作则是初始化具有非零初始值设定的 变量。 在完成上述的初始化程序之后,80C51 的控制权才会交给 main() 开始执行使用者的程序。 #define XBYTE ((unsigned char volatile xdata *) 0) 定义 XBYTE 为 指向 xdata 地址空间unsigned char 数据类型的指针,指针值为0 这样,可以直接用XBYTE[0xnnnn]或*(XBYTE+0xnnnn)访问外部RAM了 芯片为AT89S52,P2.1口与T6963C的片选相连,P2.0口与T6963C读/写使能控制口相连,其它5个口用于别的芯片控制,P0口与T6963C的数据/命令口(8位)相连,但我总是搞不明白为什么XBYTE[0xXXXX]定义后的宏是怎么回事。 #include <reg51.h> #include <absacc.h> #define d_add XBYTE[0xfc00]//为什么说这就是数据通道 #define c_add XBYTE[0xfd00]//为什么说这就是命令通道 void main() { /*比如说*/ d_add=0x90; //为什么这就是将数据写到T6963C目前显示存储器地址(地址由其它命令设定 <0~8KB>)中,而不是将数据0x90写到外部RAM地址为0xfc00中呢(再说我们的外部RAM也没这么大)??有人讲等同汇编的 MOV DPTR #0FC00H MOV A #90H MOVX @DPTR,A (这真是不可理喻了?) /*比如说*/ c_add=0xC0; //为什么这是一条控制T6963C的命令,而不是将数据0xc0写到外部RAM地址为0xfd00中呢!!! } 找了些资料没看明白,很多人只是COPY一些杂碎,希望得到高人指点,我看到也有不少人问这个问题,如果能找到答案,我一定转贴,让大家一起分享。。。。。
对我有用[0] 丢个板砖[0] 引用 举报 管理 TOP 回复次数:4 woldcow (woldcow) 等 级: #1楼 得分:0回复于:2009-03-13 21:00:57为什么没人回答呢? 对我有用[0] 丢个板砖[0] 引用 举报 管理 TOP 精华推荐:printf("%f",10/3); 答案是多少? Julius0072008 (Julius0072008) 等 级: #2楼 得分:0回复于:2009-07-28 13:57:04哎!不懂呀!好象是总线方式用的吧!绝对地址。 对我有用[0] 丢个板砖[0] 引用 举报 管理 TOP 精华推荐:终极挑战:一读一写情况下,无锁队列的实现 sbxzlkz (sbxzlkz) 等 级: #3楼 得分:0回复于:2009-08-13 11:07:27粗略地说d_add就等价于0xfc00,即二进制的1111 1100 0000 0000。 总线方式时先是P2口输出高八位地址,P0口输出低八位地址,维持短暂的时间后(好像是一条指令的时间),P0口又输出数据,P2口仍保持之前的高8位地址,这些是单片机硬件自动完成的。 此处P0口输出低八位地址,即上面的低八位的8个0,P2口输出高八位地址,即上面的的高8位的1111 1100,此时是不是刚好P2.0=0(T6963写有效,为1时读有效?),P2.1=0(T6963片选有效),而P2.2~P2.7=1呢?这样是不是选中了T6963,同时写信号也有效呢,跟你单个口线赋值的效果是一样的。之后数据0x90出现在P0上,t6963会把这个数据读走的。这样就完成了一次数据的写。 你没有外部RAM ,当然不会写到外部RAM里去,其实如果有的话写外部RAM的方式跟以上类似的。 对我有用[0] 丢个板砖[0] 引用 举报 管理 TOP 精华推荐:危盛的一套比试题,请大家一起来锻炼一下脑子! sbxzlkz (sbxzlkz) 等 级: #4楼 得分:0回复于:2009-08-13 11:18:24忘记说了 ,别的朋友给你的那段汇编语句跟你的d_add=0x90确实是等效的,不懂得话学学汇编,你就会恍然大 对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。如果系统结构支持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因为C语言并没有提供真正的“端口”的概念。如果是内存映射,那就方便的多了。 以 #define IOPIN (*((volatile unsigned long *) 0xE0028000)) 为例:作为一个宏定义语句,define是定义一个变量或常量的伪指令。首先( volatile unsigned long * )的意思是将后面的那个地址强制转换成 volatile unsigned long * ,unsigned long * 是无符号长整形,volatile 是一个类型限定符,如const一样,当使用volatile限定时,表示这个变量是依赖系统实现的,以为着这个变量会被其他程序或者计算机硬件修改,由于地址依赖于硬件,volatile就表示他的值会依赖于硬件。 volatile 类型是这样的,其数据确实可能在未知的情况下发生变化。比如,硬件设备的终端更改了它,现在硬件设备往往也有自己的私有内存地址,比如显存,他们一般是通过映象的方式,反映到一段特定的内存地址当中,这样,在某些条件下,程序就可以直接访问这些私有内存了。另外,比如共享的内存地址,多个程序都对它操作的时候。你的程序并不知道,这个内存何时被改变了。如果不加这个voliatile修饰,程序是利用catch当中的数据,那个可能是过时的了,加了 voliatile,就在需要用的时候,程序重新去那个地址去提取,保证是最新的。归纳起来如下: 1. volatile变量可变 允许除了程序之外的比如硬件来修改他的内容 2. 访问该数据任何时候都会直接访问该地址处内容,即通过cache提高访问速度的优化被取消 对于((volatile unsigned long *) 0xE0028000)为随硬件需要定义的一种地址,前面加上“*”指针,为直接指向该地址,整个定义约定符号IOPIN代替,调用的时候直接对指向的地址寄存器写内容既可。这实际上就是内存映射机制的方便性了。其中volatile关键字是嵌入式系统开发的一个重要特点。上述表达式拆开来分析,首先(volatile unsigned long *) 0xE0028000的意思是把0xE0028000强制转换成volatile unsigned long类型的指针,暂记为p,那么就是#define A *p,即A为P指针指向位置的内容了。这里就是通过内存寻址访问到寄存器A,可以读/写操作。 对于(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 rRTCCON (*(volatile unsigned char *)0x57000043) 2009-09-30 16:38
|