传统51单片机的RAM
传统51单片机RAM大体分为片内RAM(IDATA)和片外RAM(XDATA)。根据需要,可以通过外接RAM芯片的方式,将片外RAM最大扩充至64KB,现阶段不做研究
片内RAM共有256个单元,又可以按照功能分为低128个单元(DATA区)和高128个单元(SFR区)。低128个单元的地址范围:00H~7FH,高128个单元地址范围:80H~FFH。
寄存器的字节地址
DATA区(00H~7FH)是单片机内部真正的RAM,用于存放程序执行时的变量。
SFR区(80H~FFH) 是单片机内部用于对CPU各种功能部件进行控制和监控的特殊RAM单元,即特殊功能寄存器(special Function Register),例如在上一篇中使用的P1口,查表可知,它所对应的寄存器就是90H,其最低位对应P1.0引脚,最高位对应P1.7引脚,通过改变此寄存器的值即可控制P1端口输出的电平,反之,也可通过读取此寄存器的值来读取外部输入到引脚的电平。
特殊功能寄存器名称及地址映像见《stc89c51rc-rd.pdf》第59页。
回到上次的程序1,代码P1=0xDE
可以直接改变90H寄存器的值,从而改变P1端口的输出电平。可是,编译器为什么会知道P1端口的寄存器地址呢?其实,大部分寄存器的名称及其对应的地址已经在头文件reg51.h中定义了。
/* reg51.h部分代码 */
...
sfr P0 = 0x80;
sfr P1 = 0x90;
sfr P2 = 0xA0;
sfr P3 = 0xB0;
...
sfr PCON = 0x87;
sfr TCON = 0x88;
sfr TMOD = 0x89;
sfr TL0 = 0x8A;
sfr TL1 = 0x8B;
sfr TH0 = 0x8C;
sfr TH1 = 0x8D;
sfr IE = 0xA8;
sfr IP = 0xB8;
sfr SCON = 0x98;
sfr SBUF = 0x99;
...
Keil C51对标准C语言的语法进行了扩充,新增了一些关键字,例如sfr、sbit、interrupt、code等,其中,sfr关键字用来定义特殊功能寄存器。所以,我们以后只需要在程序中包含reg51.h头文件就不用我们自己去定义特殊寄存器了。(自行尝试在不包含reg51.h头文件的情况下实现点灯程序)
如果只想单独对一个引脚进行操作,其实还可以按位访问寄存器。sbit关键字用来定义特殊功能寄存器中的位寻址位,例如程序2中的sbit d1=P1^0
,意思是定义了90H寄存器中的第0位为d1。这样,在接下来的程序中对d1进行操作就相当于直接对90H寄存器第0位的值进行操作,例如d1=0。但需要注意的是,只有能被8整除的特殊功能寄存器地址才可以进行位操作,这样的寄存器称为可位寻址寄存器,不能够被8整除的则不可以进行位操作。例如P1口的地址是90H可被8整除,可进行位操作;TCON的地址88H可被8整除,可进行位操作;TMOD的地址89H不能被8整除,不可进行位操作。
寄存器的位地址
可位寻址寄存器中的每一位,都对应了一个位地址,例如P1端口的寄存器地址90H,它第0位的位地址是90H,它的第1位的位地址是91H,以此类推。
特殊功能寄存器的位地址表见《stc89c51rc-rd.pdf》第60页。
所以,下面的程序也可实现程序2的功能:
#include <reg51.h>
sbit d1=0x90; //sbit d1=P1^0;
sbit d6=0x95; //sbit d6=P1^5;
main()
{
d1=0;
d6=0;
while(1){}
}
在reg51.h中,已对一些位地址进行了定义:
/* reg51.h部分代码 */
...
/* TCON */
sbit TF1 = 0x8F;
sbit TR1 = 0x8E;
sbit TF0 = 0x8D;
sbit TR0 = 0x8C;
sbit IE1 = 0x8B;
sbit IT1 = 0x8A;
sbit IE0 = 0x89;
sbit IT0 = 0x88;
/* IE */
sbit EA = 0xAF;
sbit ES = 0xAC;
sbit ET1 = 0xAB;
sbit EX1 = 0xAA;
sbit ET0 = 0xA9;
sbit EX0 = 0xA8;
...
/* SCON */
sbit SM0 = 0x9F;
sbit SM1 = 0x9E;
sbit SM2 = 0x9D;
sbit REN = 0x9C;
sbit TB8 = 0x9B;
sbit RB8 = 0x9A;
sbit TI = 0x99;
sbit RI = 0x98;
...
关于两种地址的区分
例如TCON寄存器的地址是88H,它第1位的位地址是89H,相应代码sbit IE0 = 0x89
;然而TMOD寄存器的地址是89H,相应代码sfr TMOD = 0x89
;看起来,他们的地址不是冲突了吗?然而并不会,用sfr定义的寄存器会按字节地址访问,而sbit是按字节中的位访问的,编译器会自动进行处理。