资源描述
第一课 建立您的第一个C工程
使用C语言肯定要使用到C编译器,以便把写好的C程序编译为机器码,这样单片机才能执行编写好 的程序。KEIL UVISI0N2是众多单片机应用开发软件中优秀的软件之一,它支持众多不同公司的MCS51架 构的芯片,它集编辑,编译,仿真等于一体,同时还支持,PLM,汇编和C语言的程序设计,它的界面和常 用的微软VC++的界面相似,界面友好,易学易用,在调试程序,软件仿真方面也有很强大的功能。因此很 多开发51应用的工程师或普通的单片机爱好者,都对它十肥也欢。
以上简单介绍了 KEIL51软件,要使用KEIL51软件,必需先要安装它。KEIL51是一个商业的软件,对 乎我们这些普通爱好者可以到KEIL中国代理周立功公司的网站上下载一份能编译2K的DEMO版软件,基本 可以满足一般的个人学习和小型应用的开发。(安装的方法和普通软件相当这里就不做介绍了)
安装好后,你是不是迫不及待的想建立自己的第一个C程序工程呢?下面就让我们一起来建立一个小 程序工程吧。或许你手中还没有一块实验板,甚至没有一块单片机,不过没有关系我们可以通过KEIL软件 仿真看到程序运行的结果。
首先当然是运行KEIL51软件。怎么翻开?噢,天!那你要从头学电脑了。呵呵,开个玩笑,这个问题 我想读者们也不会提的了: P。运行几秒后,出现如图的屏幕。
接着按下面的步骤建立您的第一个工程:
(1)点击Project菜单,选择弹出的下拉式菜单中的New Project,如图1—2。接着弹 出一个标准Windows文件对话窗口,如图1-3,这个东东想必大家是见了 N次的了,用法 技巧也不是这里要说的,以后的章节中出现类似情况将不再说明。在“文件名”中输入您的第 一个C程序工程名称,这里我们用“test”,这是笔者惯用的名称,大家不必照搬就是了,只 要符合Windows文件规那么的文件名都行。“保存”后的文件扩展名为uv2,这是KEILuVision2 工程文件扩展名,以后我们可以直接点击此文件以翻开先前做的工程。
图 1 —2 New Project 菜单
图1一3文件窗口
(2)选择所要的单片机,这里我们选择常用的Atcml公司的AT89c51。此时屏幕如图 1一4所示。AT89C51有什么功能、特点呢?不用急,看图中右边有简单的介绍,稍后的章 节会作较详细的介绍。完成上面步骤后,我们就可以进行程序的编写了。
(3)首先我们要在工程中创立新的程序文件或加入旧程序文件。如果你没有现成的程图3 — 2工程选项窗口
图3 — 2工程选项窗口
[=1 Files 苧…
xJ
2J
Build target 'Target 11compiling test 1.c...
linking...
Program Size: data=30.1 xdata=0 code=1100
creatinq hex file from "test”...
"test" - 0 Error(s), 0 Warning(s).
小 Build 卜 Command 入 Find in Files /图3—3编译信息窗口
或许您已把编译好的文件烧到了芯片上,如果您购买或自制了带串口输出元件的学习实验板,那您就 可以把串口和PC机串口相联用串口调试软件或Windows的超级终端,将其波特率设为1200,就可以看到不 停输出的"Hell。World!”字样。也许您还没有实验板,那这里先说说AT89c51的最小化系统,再以一实例 程序验证最小化系统是否在运行,这个最小化系统也易于自制用于实验。图3—4便是AT89c51的最小化系 统,不过为了让我们可以看出它是在运行的,我加了一个电阻和一个LED,用以显示它的状态,晶振可以根 据自己的情况使用,一般实验板上是用11.0592MHz或12MHz,使用前者的好外是可以产生标准的串口波特 率,后者那么一个机器周期为1微秒,便于做精确定时。在自己做实验里,注意的是VCC是+5V的,不能高 于此值,否那么将损坏单片机,太低那么不能正常工作。在31脚要接高电平,这样我们才能执行片内的程序, 如接低电平那么使用片外的程序存储器。下面,我们建•个新的工程名为OneLED来验证最小化系统是否可以 工作。程序如下: #include 〃预处理命令 void ma in (void) //主函数名 〃这是第一种注释方式unsigned int a; 〃定义变量a为int类型 这是第二种注释方式do{ //do while组成循环
for (a=0; a<50000; a++); 〃这是一个循环 Pl_0 = 0; 〃设Pl. 0 口为低电平,点亮LED for (a=0; a<50000; a++); //这是一个循环 Pl 0 = 1; 〃设PL 0 口为高电平,熄火LED )while(l);
1
2
J 560
3
4
DI
5
7 EE
D
6
s
vrc C ,
13
12
Cl 30pF
15
R1 S.2K17
——C316
Fj=lOuF
AT89C51
P1.0
PO.O(ADO)
P1.1
PO.1(AD1)
P1.2
P0.2(AD2)
P1.3
P0.3(AD3)
P1.4
P0.'1(AIM)
P1.5
PO.5(AD5)
P1.6
P0.6(AD6)
P1.7
P0.7(AD7)
INT1(P3.3)
P2.0(A8)
INT0(P3.2)
P2.1(A9)
TL(P3.5)
P2.2(A10)
P2.3(A11)
T0(P3.4)
P2.4(A12)
EA.W
P2.5(A13)
P2.6(A14)
XTAL1
XTAL2
P2.7(A15)
VCC
RESET
GND RXD(P3.0)
TXD(P3.1)
RD(P3.7)
ALEZPMG
WR(P3.6)
PSEN
9
3
20
010 9
1-1-3 2
ovcc
GND
vcc
图3 - 4 AT89c51最小化系统
这里先讲讲KEILC编译器所支持的注粒语句。一种是以"//”符号开始的语句,符号之后的语句都被视 为注鄂,直到有回车换行。另一种是在"/*"和"*/"符号之内的为注释。注释不会被C编译器所编译。一个 C应用程序中应有一个main主函数,main函数可以调用别的功能函数,但其它功能函数不允许调用main 函数。不管main函数放在程序中的那个位置,总是先被执行。用上面学到的知识编译写好的OneLED程序, 并把它烧到刚做好的最小化系统中。上电,刚开始时LED是不亮的(因为上电复位后所有的10 口都置1引 脚为高电平),然后延时一段时间(for (a=0: a<50000; a++)这句在运行),LED亮,再延时,LED熄灭, 然后交替亮、灭。第一个真正的小应用就做完,呵呵,先不要管它是否实用哦。如果没有这样的效果那么 您就要认真检查一下电路或编译烧写的步骤了第四课数据类型
先来简单说说C语言的标识符和关键字。标识符是用来标识源程序中某个对象的名字的,这些对象可以是 语句、数据类型、函数、变量、数组等等。C语言是大小字敏感的一种高级语言,如果我们要定义一个定 时器1,可以写做“Timerl",如果程序中有"TIMER/,那么这两个是完全不同定义的标识符。标识符由字 符串,数字和卜.划线等组成,注意的是第一个字符必须是字母或卜划线,如"ITimer”是错误的,编译时便 会有错误提示。有些编译系统专用的标识符是以下划线开头,所以一般不要以下划线开头命名标识符。标 识符在命名时应当简单,含义清晰,这样有助于阅读理解程序。在C51编译器中,只支持标识符的前32位 为有效标识,一般情况下也足够用了,除非你要写天书:P。
关健字那么是编程语言保存的特殊标识符,它们具有固定名称和含义,在程序编写中不允许标识符与关
键资亦同。在KEIL uVision2中的关键字除了有ANSI C标准的32个关键字外还根据51单片机的特点扩展 了相关的关键字。其实在KEIL uVision2的文本编辑器中编写C程序,系统可以把保存字以不同颜色显示, 缺省颜色为天蓝色。(标准和扩展关键字请看附录一中的附表1T和附表1-2)
先看表4—1,表中列出了 KEIL uVision2 C51编译器所支持的数据类型。在标准C语言中基本的数据 类型为 char, int, short, long, float 和 double,而在 C51 编译器中 int 和 short 相同,float 和 double 相 同,这里就不列出说明了。下面来看看它们的具体定义:
数据类型
长度
值域
unsigned char
单字节
0-255
signed char
单字节
-128〜+127
unsigned int
双字节
0-65535
signed int
双字节
-32768〜+32767
unsigned long
四字节
0-4294967295
signed long
四字节
-2147483648〜+2147483647
float
四字节
±1. 175494E-38 〜±3J02823E+38
*
1〜3字节
对象的地址
bit
位
0或1
sfr
单字节
0-255
sfrl6
双字节
0-65535
sbit
位
。或1
表KEIL uVision2 C51编译器所支持的数据类型char字符类型
char类型的长度是•个字节,通常用于定义处理字符数据的变量或常量。分无符号字符类型unsigned char和有符号字符类型signed char,默认值为signed char类型。unsigned char类型用字节中所有的 位来表示数位,所可以表达的数值范围是255。signed char类型用字节中最高位字节表示数据的符号, "0"衣示正数,"1”表示负数,负数用补码及示。所能表示的数值范围是T28〜+127。unsigned char常用 于处理ASCII字符或用于处理小于或等于255的整型数。
*正数的补码与原码相同,负二进制数的补码等于它的绝对值按位取反后加1。
1. int整型
int整型长度为两个字节,用于存放一个双字节数据。分有符号int整型数signed int和无符号整型 数unsigned int,默认值为signed int类型。signed int表示的数值范围是-32768~+32767,字节中最 高位表示数据的符号,"0"表示正数,"1”表示负数。unsigned int表示的数值范围是0-65535。
好了,先停一下吧,我们来写个小程序看看unsigned char和unsigned int用于延时的不同效果,说 明它们的长度是不同的,呵,尽管它并没有实际的应用意义,这里我们学习它们的用法就行。依旧用我们 上一课的最小化系统做实验,不过要加多一个电阻和LED,如图4-1。实验中用D1的点亮说明正在用 unsigned int数值延时,用D2点亮说明正在用unsigned char数值延时。
vcco-
13
12
15
14
31
19
I?
9
RI S.2K17
-1-C3 16
Ff=lOuF一
PLO
PO.O(ADO)
Pl.l
PO.1(AD1)
Pl.2
P0.2(AD2)
Pl .3
P0.3(AD3)
Pl.4
P0.4(ADd)
Pl.5
PO.5(AD5)
Pl .6
P0.6(AD6)
Pl .7
P0.7(AD7)
INT1(P3.3)
P2.0(A8)
INT0(P3.2)
P2.1(A9)
P2.2(A10)
T1(P3.5)
P2.3(A11)
T0(P3.4)
P2.4(A12)
P2.5(A13)
EA/VP
XTAL1
P2.6(A14)
P2.7(A15)
XTAL2
VCC
GND
RESET
RXD(P3.0)
RD(P3.7)
TXP(P3.1)
ALEZPROG
WR(P3.6)
PSEN
ATS9C51
9 8 7 6-5432- 1
3 3 3 3-3333- 2
25262728
0 0 0-109
4 2 1-13 2
c
VC
O
VCC
图4-1第4课实验用电路我们把这个工程称为TwoLED,实验程序如下:
■ include 〃预处理命令void ma in (void) 〃主函数名{
unsigned int a; 〃定义变量 a 为 unsigned int 类型unsigned char b; 〃定义变量 b 为 unsigned char 类型
do{ //do while组成循环
for (a=0; a<65535; a++)Pl_0 = 0; //65535次设PLO 口为低电平,点亮LED
Pl_0 = 1; 〃设PLO □为高电平,熄灭LEDfor (a=0: a<30000: a++); 〃空循环
for (b=0; b<255; b++)Pl_l = 0; 〃255次设PL1 口为低电平,点亮LED
Pl 1 = 1; 〃设PL1 □为高电平,熄灭LEDfor (a=0; a<30000; a++); //空循环
)while(l);
)
同样编译烧写,上电运行您就可以看到结果了。很明显D1点亮的时间长于D2点亮的时间。程序中的 循环延时时间并不是很好确定,并不太适合要求精确延时的场合,关于这方面我们以后也会做讨论。这里 必须要讲的是,当定义一个变量为特定的数据类型时,在程序使用该变量不应使它的值超过数据类型的值 域。如本例中的变量b不能赋超出0〜255的值,如for (b=0; b<255; b++)改为for (b=0; b<256; b++), 编译是可以通过的,但运行时就会有问题出现,就是说b的值永远都是小于256的,所以无法跳出循环执 行下一句从而造成死循环。同理a的值不应超出0〜65535。大家可以烧片看看实验的运行结果, 同样软件仿真也是可以看到结果的。
2. long长整型
long长整型长度为四个字节,用于存放一个四字节数据。分有符号long长整型signed long和无符 号长整型unsigned long,默认值为signed long类型。signed int表示的数值范围是-2147483648〜 +2147483647,字节中最高位表示数据的符号,"0”表示正数,"1”表示负数。unsigned long表示的数值范 国是。〜4294967295。
3. float浮点型
float浮点型在十进制中具有7位有效数字,是符合IEEE -754标准的单精度浮点型数据,占用四个 字节。因浮点数的结构较复杂在以后的章节中再做详细的讨论。
4. *指针型
指针型本身就是一个变量,在这个变量中存放的指向另一个数据的地址。这个指针变量要占据一定的 内存单元,对不同的处理器长度也不尽相同,在C51中它的长度一般为1〜3个字节。指针变量也具有类型, 在以后的课程中有专门•课做探讨,这里就不多说 6. bit位标量
bit位标量是C51编译器的一种扩充数据类型,利用它可定义一个位标量,但不能定义位指针,也不 能定义位数组。它的值是一个二进制位,不是0就是1,类似一些高级语言中的Boolean类型中的True和 False。
7. sfr特殊功能寄存器
sfr也是一种扩充数据类型,点用一个内存单元,值域为。〜255。利用它可以访问51单片机内部的所 有特殊功能寄存器。如用sfr Pl = 0x90这一句定Pl为Pl端口在片内的寄存器,在后面的语句中我们用 以用P1 = 255 (对P1端口的所有引脚置高电平)之类的语句来操作特殊功能寄存器。
* AT89c51的特殊功能寄存器表请看附录二sfrl6 16位特殊功能寄存器
sfrl6占用两个内存单元,值域为0〜65535。sfrl6和sfr一样用于操作特殊功能寄存器,所不同的 是它用于操作占两个字节的寄存器,好定时器TO和T1。
8. sbit可录址位
sbit同位是C51中的一种扩充数据类型,利用它可以访问芯片内部的RAM中的可寻址位或特殊功能寄 存器中的可寻址位。如先前我们定义了sfr Pl = 0x90; 〃因Pl端口的寄存器是可位寻址的,所以我们可以定义
sbit Pl_l = Pl A 1; //Pl_l 为 Pl 中的 PL1 引脚〃同样我们可以用Pl. 1的地址去写,如sbit Pl_l = 0x91;
这样我们在以后的程序语句中就可以用Pl_l来对PL 1引脚进行读写操作fo通常这些可以直接使用系统 提供的预处理文件,里面已定义好各特殊功能寄存器的简单名字,直接引用可以省去一点时间,我自己是 一直用的。当然您也可以自己写自己的定义文件,用您认为好记的名字。
关于数据类型转换等相关操作在后面的课程或程序实例中将有所提及。大家可以用所讲到的数据类型 改写•下这课的实例程序,加深对各类型的认识。
第五课常量
上一节我们学习了 KE1LC51编译器所支持的数据类型。而这些数据类型又是怎么用在常量和变量的定义 中的呢?又有什么要注意的吗?下面就来看看吧。晕!你还区分不清楚什么是常量,什么是变量。常量是 在程序运行过程中不能改变值的量,而变量是可以在程序运行过程中不断变化的量。变量的定义可以使用 所有C51编译器支持的数据类型,而常量的数据类型只有整型、浮点型、字符型、字符串型和位标量。这 一节我们学习常量定义和用法,而下一节那么学习变量。
常量的数据类型说明是这样的
1 .整型常量可以表示为十进制如123,0, -89等。十六进制那么以Ox开头如0x34,-0x3B等。长整型就 在数字后面加字母L,如104L, 034L, 0xF340等。
2 .浮点型常量nJ•分为十进制和指数表示形式。卜进制由数字和小数点组成,如0.888, 3345. 345, 0.0 等,整数或小数局部为0,可以省略但必须有小数点。指数表示形式为[土]数字[.数字]e[土]数字,口中 的内容为可选项,其中内容根据具体情况可有可无,但其余局部必须有,如125e3,7e9,-3.0e-3。
3 .字符型常量是单引号内的字符,如'a','d'等,不可以显示的控制字符,可以在该字符前面加一个 反斜杠组成专用转义字符。常用转义字符表请看表5-U
4 .字符串型常量由双引号内的字符组成,如"test", "0K"等。当引号内的没有字符时,为空字符串。 在使用特殊字符时同样要使用转义字符如双引号。在C中字符串常量是做为字符类型数组来处理的,在存 储字符串时系统会在字符串尾部加上'。转义字符以作为该字符串的结束符。字符串常量"A"和字符常量'A' 是不同的,前者在存储时多占用一个字节的字间。
5 .位标量,它的值是一个:进制。
转义字符
含义
ASCII 码(16/10 进制)
\o
空字符(NULL)
00H/0
\n
换行符(LF)
0AH/10
\r
回车符(CR)
0DH/13
\t
水平制表符(HT)
09H/9
\b
退格符(BS)
08H/8
\f
换页符(FF)
0CH/12
单引号
27H/39
双引号
22H/34
\\
反斜杠
5CH/92
表5—1常用转义字符表
常量可用在不必改变值的场合,如固定的数据表,字库等。常量的定义方式有几种,下面来加以说明。 #difinc False 0x0; //用预定义语句可以定义常量 ttdifine True 0x1; 〃这里定义 False 为 0, True 为 1〃在程序中用到False编译时自动用0替换,同理True替换为1
unsigned int code a=100; //这一句用code把a定义在程序存储器中并赋值 const unsigned int c=100: //用const定义c为无符号int常量并赋值
以上两句它们的值都保存在程序存储器中,而程序存储器在运行中是不允许被修改的,所以如果在这 两句后面用了类似a=U0, a++这样的赋值语句,编译时将会出错。
说了 •通还不如写个程序来实验•下吧。写什么程序呢?跑马灯!对,就写这个简单易懂的吧,这个也好 说明典型的常量用法。先来看看电路图吧。它是在我们上•课的实验电路的基础.卜・增加6个LED组成的, 也就是用P1 口的全部引脚分别驱动一个LED,电路如图5-1所示。
新建一个RunLED的工程,主程序如下:
^include 〃预处理文件里面定义了特殊寄存器的名称如Pl 口定义为Pl void main(void){
〃定义花样数据cons tunsigned char design[32] = {OxEF, OxFE, OxFD, OxFB, 0xE7, OxEF, OxDF, OxBF, 0x7F,
0x7F, OxBF, OxDF, OxEF, 0xF7, OxFB, OxFD, OxFE, OxFF,OxFF, OxFE, OxFC, 0xF8, OxFO, OxEO, OxCO, 0x80, 0x0,
0xE7, OxDB, OxBD, 0x7E, OxFF):
unsigned int a; 〃定义循环用的变量unsigned char b; 〃在C51编程中因内存有限尽可能注意变量类型的使用
〃尽可能使用少字节的类型,在大型的程序中很受用 do{for (b=0; b<32; b++) (
for(a=0; a<30000; a++); 〃延时一段时间Pl = design[b]; 〃读已定义的花样数据并写花样数据到Pl 口
}}while(l);
)
程序中的花样数据可以自以去定义,因这里我们的LED要AT89C51的P1引脚为低电平才会点亮,所以 我们要向P1 口的各引脚写数据0对应连接的LED才会被点亮,P1 口的八个引脚刚好对应P1 口特殊寄存器 的八个二进位,如向P1 口定数据OxFE,转成二进制就是11111110,最低位D0为0这里P1. 0引脚输出低电 平,LED1被点亮。如此类推,大家不难算出自己想要做的效果了。大家编译烧写看看,效果就出来,显示 的速度您可以根据需要调整延时a的值,不要超过变量类型的值域就很行了。哦,您还没有实验板?那如 何可以知道程序运行的结果呢?呵,不用急,这就来说说用KEILuVision2的软件仿真来调试10 口输出输 入程序。
O VCC
图5一1八路跑马灯电路
编译运行上面的程序,然后按外部设备菜单Peripherals - I/O Ports - Portl就翻开Portl的调试窗 口了,如图5—3中的2。这时程序运行了,但我们并不能在Portl调试窗口上看到有会什么效果,这时我 们可以用鼠标左击图5-3中1旁边绿色的方条,点一卜就有一个小红方格在点一卜又没有了,哪一句语句 前有小方格程序运行到那一句时就停止了,就是设置调试断点,同样图5—2中的1也是同样功能,分别是 增加/移除断点、移除所有断点、允许/禁止断点、禁止所有断点,菜单也有一样的功能,另外菜单中还有 Breakpoints可翻开断点设置窗口它的功能更强大,不过我们这里先不用它。我们?quol;Pl = design[b] 这一句设置一个断点这时程序运行到这里就停住了,再留意一下Porll调试窗口,再按图5-2中的2的运 行键,程序又运行到设置断点的地方停住了,这时Peril调试窗口的状态又不同了。也就是说Peril调试 窗「I模拟了 P1 口的电平状态,打勾为高电平,不打勾那么为低电平,窗口中P1为P1寄存器的状态,Pins 为引脚的状态,注意的是如果是读引脚值必须把引脚对应的寄存器置1才能正确读取。图5-2中2旁边的 {}样的按钮分别为单步入,步越,步出和执行到当前行。图中3为显示下一句将要执行的语句。图5—3 中的3是Walches窗口可查看各变量的当前值,数组和字串是显示其头一个地址,如本例中的design数组 是保存在RAM存储区的首地址为D:0x08,可以在图中4 Memory存储器查看窗口中的Address地址中打入 D:0x08就可以查看到design各数据和存放地址了。如果你的uVision2没有显示这些窗口,可以在View 菜单中翻开在图5-2中3后面一栏的查看窗口快捷栏中翻开。
位 RunLED - Vision?
File Edit View Project Debug Peripherals Tools SVCS Window Help■■■?— fMB■■■■■■■■ •
ZIM
窗岫富|二叱|率率4茏%/聃国 漉昌阿||吗向]&删眄处
籥®2P刊小{>彳}3 o族{决凰四少昌百Er7图5 — 2调试用快捷菜单栏
m 尸:\""@1@\其它\我的文章\(:51\(:\复件 p5 l\RunLED.cdo{
for (b=0; b<35; b++)
do{
for (b=0; b<35; b++)
“尽可能使用少字节的类型,在大型的程序中很受用
for(a=0; a<50000; a++); //延时一段时间Pl = design[b];“读已定义的花样数据并写花样数据到Pl 口
2d d
iddress: p:0x08
[=| RunLED. c00
10
40
02
42
00
10
40
02
42
4 0 8 8 2
0 8 0 14
2 0 0 0 1
O 4 1 o R
D:0x08: D:OxOD: D:0xl2: D:0xl7: D-nx1C-上课所提到变量就是一种在程序执行过程中其值能不断变化的量。要在程序中使用变量必须先用标识符作 为变量名,并指出所用的数据类型和存储模式,这样编译系统才能为变量分配相应的存储空间。定义一个 变量的格式如下:
[存储种类]数据类型[存储耀类型]变量名表
在定义格式中除了数据类型和变量名表是必要的,其它都是可选项。存储种类有四种:自动(auto), 外部(extern),静态(static)和寄存器(register),缺省类型为自动(auto)。
而这里的数据类型那么是和我们在第四课中学习到的名种数据类型的定义是一样的。说明了一个变量的 数据类型后,还可选择说明该变量的存储器类型。存储器类型的说明就是指定该变量在C51硬件系统中所 使用的存储区域,并在编译时准确的定位。表6—1中是KEILuVision2所能认别的存储器类型。注意的是 在AT89C51芯片中RAM只有低128位,位于80H到FFH的高128位那么在52芯片中才有用,并和特殊寄存器 地址重拄。特殊寄存器(SFR)的地址表请看附录二AT89c51特殊功能寄存器列表表6—1存储器类型
存储器类型
说明
data
宜接访问内部数据存储器(128字节),访问速度最快
bdata
可位寻址内部数据存储器(16字节),允许位与字节混合访问
idala
间接访问内部数据存储器(256字节),允许访问全部内部地址
pdata
分页访问外部数据存储器(256字节),用MOVX @Ri指令访问
xdata
外部数据存储器(64KB),用MOVX @DPTR指令访问
code
程序存储器(64KB),用MOVC ©A+DPTR指令访问
如果省略存储器类型,系统那么会按编译模式SMA1.L, COMPACT或LARGE所规定的默认存储器类型去指定 变量的存储区域。无论什么存储模式都可以声明变量在任何的8051存储区范围,然而把最常用的命令如循 环计数器和队列索引放在内部数据区可以显著的提高系统性能。还有要指出的就是变量的存储种类与存储 器类型是完全无关的。
SMALL存储模式把所有函数变量和局部数据段放在8051系统的内部数据存储区这使访问数据非常快, 但SMALL存储模式的地址空间受限。在写小型的应用程序时,变量和数据放在data内部数据存储器中是很 好的因为访问速度快,但在较大的应用程序中data区最好只存放小的变量、数据或常用的变量(如循环计 数、数据索引),而大的数据那么放置在别的存储区域。
COMPACT存储模式中所有的函数和程序变量和局部数据段定位在8051系统的外部数据存储区。外部数 据存储区可有最多256字节(一页),在本模式中外部数据存储区的短地址用鳏0/R1。
LARGE存储模式所有函数和过程的变量和局部数据段都定位在8051系统的外部数据区外部数据区最多 可有64KB,这要求用DPTR数据指针访问数据。
之前提到简单提到sfr, sfrl6, sbit定义变量的方法,下面我们再来仔细看看。
sfr和sfrl6可以直接对51单片机的特殊寄存器进行定义,定义方法如下:
sfr特殊功能寄存器名=特殊功能寄存器地址常数;
sfr16特殊功能寄存器名=特殊功能寄存器地址常数;
我们可以这样定义AT89c51的Pl 口
sfr Pl = 0x90: 〃定义 Pl I/O n,其地址 90H
sfr关键定后面是一个要定义的名字,可任意选取,但要符合标识符的命名规那么,名字最好有一定的 含义如P1 口可以用P1为名,这样程序会变的好读好多。等号后面必须是常数,不允许有带运算符的表达 式,而且该常数必须在特殊功能寄存器的地址范围之内(80H-FFH),具体可查看附录中的相关表。sfr 是定义8位的特殊功能寄存器而sfrl6那么是用来定义16位特殊功能寄存器,如8052的T2定时曙,可以定 义为:
sfr16 T2 = OxCC; 〃这里定义 8052 定时器 2,地址为 T2L=CCH, T2H=CDH用sfrl6定义16位特殊功能寄存器时,等号后面是它的低位地址,高位地址一定要位于物理低位地址之上。 注意的是不能用于定时器。和I的定义。
sbil可定义可位寻址对象。如访问特殊功能寄存器中的某位。其实这样应用是经常要用的如要访问P1 口中的第2个引脚PL 1。我们可以照以下的方法去定义: (l)sbit位变量名=位地址
sbit Pl_l = 0x91;这样是把位的绝对•地址赋给位变量。同sfr 一样sbit的位地址必须位于80H-FFH之间。
(2)Sbit位变量名=特殊功能寄存器名,立位置sft Pl = 0x90;
sbit Pl」=Pl - 1; 〃先定义一个特殊功能寄存器名再指定位变量名所在的位置 当可寻址位位于特殊功能寄存器中时可采用这种方法序,那么就要新建一个程序文件。在KEIL中有一些程序的Dem。,在这里我们还是以一个 C程序为例介绍如何新建一个C程序和如何加到您的第一个工程中吧。点击图1—5中1的 新建文件的快捷按钮,在2中出现一个新的文字编辑窗I」,这个操作也可以通过菜单File -New或快捷键Qrl+N来实现。好了,现在可以编写程序了,光标已出现在文本编辑窗口 中,等待我们的输入了。第一程序嘛,写个简单明了的吧。下面是经典的一段程序,呵,如 果你看过别的程序书也许也有类似的程序:
#includc#include void main(void)
(
SCON = 0x50; 〃串口方式1,允许接收
TMOD = 0x20; 〃定时器1定时方式2
TCON = 0x40;〃设定时器1开始计数
THI =0xE8;//11.0592MHz 1200 波特率
TL1 = 0xE8;
TI= 1;
TR1 = 1;〃启动定时器 while(l)World!\n"); 〃显示 Hello
))
World!\n"); 〃显示 Hello
))
l- Use Extended Linker (LX51) instead of BL51
「 Use Extended Assembler (AX51) instead of A51
{printf ("HelloWorld
Select Device for Target 'Target 1"
CPU
Vendor: Atmel
Device: AT89C51
Family: MCS-51
Data baseDescription:
口u口口口 口口口 口口口 口
AT89c51
AT89C51RC
AT89C52
AT89c55 AT89C55WD
AT89F51
AT89F52
AT89LS51
AT89LS52
AT89LV51
AT89LV52
8051 based Full Static CMOS controller with Three-Level
32 1/0 lines, 2 Timers/Counters, 6 Interrupts sources
4 K Flash Memory, 128 Bytes On-chip RAM
福一I取消
(3)sbit位变量名=字节地址.位位置
sbit Pl_l = 0x90 * 1;
这种方法其实和2是一样的,只是把特殊功能寄存器的位址直接用常数表示。
在C51存储器类型中提供有一个bdata的存储器类型,这个是指可位寻址的数据存储器,位于单片机 的可位寻址区中,可以将要求可位录址的数据定义为bdata,如:
unsigned char bdata ib; //在可位录址区定义ucsigned char类型的变量ibint bdata ab[2]: 〃在可位寻址区定义数组ab[2],这些也称为可寻址位对象
sbit ib7=ib*7 〃用关键字sbil定义位变量来独立访问可寻址位对象的其中一位sbit ab12=ab[l]"12;
操作符""”后面的位位置的最大值取决于指定的基址类型,charO-7, intO-15, long0-31o下面我们用上一课的电路来实践一下这一课的知识。同样是做一下简单的跑马灯实验,工程名为RunLED2° 程序如下:
sfr Pl = 0x90; 〃这里没有使用预定义文件,sbit Pl_0 = Pl_0; 〃而是自己定义特殊寄存器
sbit Pl_7 = 0x90 . 7; 〃之前我们使用的预定义文件其实就是这个作用sbit Pl_l = 0x91; 〃这里分别定义Pl端口和P10,PU,P17引脚
void main(void){
unsigned int a;unsigned char b;
do{for (a=0;a<50000;a++)
Pl_0 = 0; //点亮 Pl_0for (a=0;a<50000;a++)
Pl_7 = 0; //点亮Pl_7for (b=0;b<255;b++)
{for (a=0;a<10000;a++)
Pl = b; 〃用b的值来做跑马灯的花样)
Pl = 255; 〃熄灭 Pl 上的 LED for (b=0;b<255;b++) for (a=0;a<10000;a++) 〃P1」闪烁Pl_l = 0;
for (a=0;a<10000;a++)Pl_l = 1;
)}while⑴;
)第七课运算符和表达式(D
上课到这一课相隔了好长一段时间,这些日子里收到不少网友的来信支持和鼓励,要求尽快完成余下的部 分。出门在外的人不得不先为吃饭而努力,似乎这也成为我的借口,以后每晚抽空打一些吧这样大家也就 可以不用隔太久就能看到一些新东西。或许我的笔记并不是很正确,但我尽量
展开阅读全文