absacc.h的理解

absacc.h的理解

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 变量的存取不能缓存到寄存器,每次使用时需要重新存取。

[1]https://blog.csdn.net/chenhssadf/article/details/8998743

  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值