#define XBYTE ((unsigned char volatile xdata *)

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

    #define rRTCCON    (*(volatile unsigned char *)0x57000043) //RTC control

      理解#define rRTCCON (*(volatile unsigned char *)0x57000043) //RTC control 这样的定义,总是感觉很奇怪,今天终于有了一点点心得, 嵌入式系统编程,要求程序员能够利用C语言访问固定的内存地址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。所以,知道要访问的内存地址后,比如0x57000043:

      第一步是要把它强制转换为指针类型(unsigned char *)0x57000043,s3c2410的rRTCCON是单字节访问的,所以0x57000043强制转换为指向unsigned char类型。volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意想不到。

     第二步,对指针变量解引用,就能操作指针所指向的地址的内容了

            *(volatile unsigned char *)0x57000043

     第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯。

    在嵌入式系统中经常使用到Volatile,对于volatile的用法,我根据自己的理解做如下阐述,希望大家可以发表评论:

    在c语言中,volatile关键字是一种类型修饰符, 用它声明的类型变量表示该变量可以被某些编译器未知的外部因素(比如:操作系统、硬件或者其它线程)更改. 遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址(定义的变量在内存中的地址)的稳定访问。

    编译器对代码的优化是指:CPU在执行的过程中,因为访问内存的速度远没有cpu的执行速度快,为了提高效率,引入了高速缓存cache. C编译器在编译时如果不知道变量会被其它外部因素(操作系统、硬件或者其它线程)修改,那么就会对该变量进行标识,即优化.那么这个变量在CPU的执行过程中,就会被放到高速缓存cache去,进而达到对变量的快速访问. 在了解了优化的概念后,试想如果我们事先就知道该变量会被外部因素改变,那么我们就在这个变量定义前加上Volatile,这样编译器就不会对该变量进行优化.这样该变量在cpu处理的过程当中,就不会被放到高速缓存cache中.

     为什么要让变量在执行的过程中不被放到cache中去呢?如果变量是被外部因素改变,那么cpu就无法判断出这个变量已经被改变,那么程序在执行的过程中如果使用到该变量,还会继续使用cache中的变量,但是这个变量其实已经被改变了.需要到内存地址中更新其内容了.还有一个原因,在一些寄存器变量或数据端口的使用中,因为寄存器变量本身也是靠cache来处理,为了避免引起错误,也可以使用volatile修饰符.(简单的说使用volatile的目的就是:让对volatile 变量的存取不能缓存到寄存器,每次使用时需要重新存取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值