2.3 任务5 开关控制LED循环点亮
工作任务
用P3.0作输入接开关SW,P1口作输出接8个LED,通过开关SW控制LED循环点亮。开关SW合上,LED循环点亮,开关SW打开,LED停止循环点亮。
2.3.1 开关控制LED循环点亮电路设计
开关控制LED循环点亮电路比LED循环点亮控制电路(见图2-1)多一个开关电路部分,其他都一样。开关SW一端接到单片机的P3.0引脚上,另一端接地,当开关SW合上时P3.0引脚就接到了低电平。开关控制LED循环点亮电路设计如图2-16所示。
运行Proteus软件,新建“开关控制LED循环点亮”设计文件。按图2-16所示,放置并编辑AT89C52、CRYSTAL、CAP、CAP-ELEC、RES、LED-RED和SWITCH等元器件。设计完成开关控制LED循环点亮电路后,进行电气规则检测。
2.3.2 开关控制LED循环点亮程序设计
与LED循环点亮控制程序相比,本程序的关键是如何用开关控制LED循环点亮。根据任务要求,用P3.0作输入接开关SW,通过开关SW控制LED循环点亮。开关SW合上,P3.0为低电平,LED循环点亮;开关SW打开,P3.0为高电平,LED停止循环点亮。
图2-16 开关控制LED循环点亮电路
开关控制LED循环点亮程序如下:
#include <reg52.h> //包含reg52.h头文件 sbit SW=P3^0; //符号SW表示 P3.0 引脚 void Delay() //延时函数 { unsigned char i, j; for (i=0;i<255;i++) for (j=0;j<255;j++); } void main() { unsigned char i; unsigned char temp; P1 = 0xff; //十六进制全1,熄灭所有LED while(1) { temp = 0x01; //第一位为1,即初始控制码为0x01 for (i=0;i<8;i++) { if(SW==0) //SW 若合上,P3.0 为低电平,LED 循环点亮 { P1 = ~ temp; //temp 值取反送P1口 Delay(); temp = temp << 1 ; //temp 值左移一位,获得下一个控制码 } } } }
开关控制LED循环点亮程序设计好以后,打开“开关控制LED循环点亮”Proteus电路,加载“开关控制LED循环点亮.hex”文件,进行仿真运行,观察开关控制LED循环点亮是否与设计要求相符。
2.3.3 C51数据类型
C51定义了标准C语言的所有数据类型,同时对标准C语言进行了扩展,更加注意对系统资源的合理利用,如表2-5所示。
表2-5 C51基本数据类型
1. C51基本数据类型
标准C语言中的基本数据类型有char、int、short、long、float和double,而在C51编译器中,int和short相同,float和double相同。
(1)char字符类型
char字符类型的长度是一个字节(8位),通常用于定义处理字符数据的变量或常量。这很适合MCS-51单片机,因为MCS-51单片机每次可处理8位数据。char类型分无符号字符类型unsigned char和有符号字符类型signed char,默认为signed char类型。
unsigned char类型用字节中所有的位来表示数值,数值范围是0~255。常用于处理ASCII字符或小于等于255的整型数。signed char类型具有重要意义的位是最高位的符号标志位,“0”表示正数,“1”表示负数,负数用补码表示,数值范围是-128~+127。正数的补码与原码相同,负数的补码等于它的原码按位取反后加1(符号位不变)。
例如:
unsigned char a; //定义变量a为无符号字符类型unsigned char char b; //定义变量b为有符号字符类型signed char
(2)int整型
int整型的长度为两个字节(16位),用于存放一个两字节数据。MCS-51系列单片机将int型变量的高位字节存放在低地址字节中,低位字节存放在高地址字节中。int整型分有符号整型数signed int和无符号整型数unsigned int,默认为signed int类型。
unsigned int表示的数值范围是0~65535。signed int表示的数值范围是-32768~+32767,字节中最高位表示数据的符号,“0”表示正数,“1”表示负数。
例如:
unsigned int x; //定义变量x为无符号整型数unsigned int int y; //定义变量y为有符号整型数signed int
(3)long长整型
long长整型长度为4个字节(32位),用于存放一个四字节数据。分有符号长整型signed long和无符号长整型unsigned long,默认为signed long类型。
unsigned long表示的数值范围是0~4294967295。signed int表示的数值范围是-2147483648~+2147483647,字节中最高位表示数据的符号,“0”表示正数,“1”表示负数。
(4)float浮点型
float浮点型长度为4个字节(32位)。在十进制中具有7位有效数字,许多复杂的数学表达式都采用浮点变量数据类型组成。
(5)*指针型
指针型本身就是一个变量,在这个变量中存放的是指向另一个数据的地址。指针变量要占据一定的内存单元,在C51中,它的长度一般为1~3个字节。
2. C51扩展的数据类型
为了更加有效地利用单片机的硬件资源,C51对标准C语言进行了扩展,增加了如下几个特殊的数据类型。
(1)bit位变量
可以将与MCS-51硬件特性操作相关的变量定义成位变量。位变量必须定位在MCS-51单片机内部RAM的位寻址空间中。但不能定义位指针,也不能定义位数组。bit位变量的值就是一个二进制位,不是0就是1,类似True和False。
例如:
bit flag; //flag 为bit 位变量,其值是0 或1
(2)sfr特殊功能寄存器
为了能直接访问特殊功能寄存器SFR,C51提供了一种特殊形式的定义方法,这种定义方法与标准C语言不兼容,只适用于对MCS-51系列单片机进行C语言编程。sfr占用一个字节,数值范围为0~255。利用它可以访问51单片机内部的所有特殊功能寄存器。C51定义特殊功能寄存器的一般语法格式如下:
sfr 特殊功能寄存器名=特殊功能寄存器的字节地址;
例如:
sfr P1 = 0x90;
定义了P1为P1端口在内部的寄存器,在后面的语句中我们可以用P1=0xff(对P1端口的所有引脚置高电平)之类的语句来操作特殊功能寄存器。
又如:
sfr SCON=0x98; //串口控制寄存器,地址为0x98 sfr TMOD=0x89; //定时器/计数器方式控制寄存器,地址为0x89
注意
“sfr”是定义语句的关键字,其后必须跟一个MSC-51单片机真实存在的特殊功能寄存器名,“=”后面必须是一个整型常数,不允许是带有运算符的表达式,代表特殊功能寄存器的字节地址,这个常数值必须在SFR地址范围内,位于0x80~0xFF。
(3)sfr16 16位特殊功能寄存器
sfr16占用两个字节,数值范围为0~65535。sfr16和sfr一样用于操作特殊功能寄存器,所不同的是它操作占用两个字节的寄存器,如52子系列的定时器/计数器2。
在许多新的MCS-51系列单片机中,有时会使用两个连续地址的特殊功能寄存器来指定一个16位的值。为了有效地访问这类SFR,可使用关键字“sfr16”来定义,16位SFR定义语句的语法格式与8位SFR相同,只是“=”后面的地址必须用16位SFR的低字节地址作为“sfr16”的定义地址。
例如:
sfr16 T2 = 0xCC //定时器/计数器2:T2低8位地址为0xCC,T2高8位地址为0xCD
注意
这种定义适用于所有新的16位SFR,但不能用于定时器/计数器0和1。
(4)sbit可寻址位
C51的扩充功能支持对特殊位的定义。与SFR定义一样,关键字“sbit”用于定义某些特殊位,利用它可以访问芯片内部的RAM中的可寻址位或特殊功能寄存器中的可寻址位。如先前我们定义:
sfr P1 = 0x90;
因P1端口的寄存器是可位寻址的,所以我们可以定义:
sbit P1_1 = P1^1; //P1_1为P1中的P1.1 引脚
这样在以后的程序语句中,我们就可以用P1_1来对P1.1引脚进行读写操作了。C51关键字sbit的用法有3种格式:
第1种格式:
sbit bit-name = sfr-name^int constant;
其中,“bit-name”是一个寻址位符号名,该位符号名必须是MCS-51单片机中规定的位名称,“sfr-name”必须是已定义过的SFR的名字,“^”后的整常数是寻址位在特殊功能寄存器“sfr-name”中的位号,必须是0~7范围中的数。
例如:
sfr PSW=0xD0; //定义PSW 寄存器地址为0xD0 sbit OV=PSW^2; //定义OV位为PSW.2,地址为0xD2 sbit CY=PSW^7; //定义CY位为PSW.7,地址为0xD7
第2种格式:
sbit bit-name = int constant^int constant;
其中,“=”后的int constant为寻址位所在的特殊功能寄存器的字节地址,“^”符号后的int constant为寻址位在特殊功能寄存器中的位号。
例如:
sbit OV=0xD0^2; //定义OV位地址是0xD0 字节中的第2 位 sbit CY=0xD0^7; //定义CY位地址是0xD0 字节中的第7 位
第3种格式:
sbit bit-name = int constant;
其中,“=”后的int constant为寻址位的绝对位地址。
例如:
sbit OV=0xD2; //定义OV位地址为0xD2 sbit CY=0xD7; //定义CY位地址为0xD7
MCS-51系列单片机的特殊功能寄存器的数量与类型不尽相同,因此建议将所有特殊的“sfr”定义放入一个头文件中,该文件应包括MCS-51单片机系列机型中的SFR定义。
说明:在C51存储器类型中提供了一个bdata的存储器类型,是指可位寻址的数据存储器,位于单片机的可位寻址区中,可以将要求可位寻址的数据定义为bdata,如:
unsigned char bdata xb; //在可位寻址区定义unsigned char 类型的变量xb int bdata yb[2]; //在可位寻址区定义数组yb[2],这些也称为可寻址位对象 sbit xb7=xb^7 //用关键字 sbit定义位变量来独立访问可寻址位对象的其中一位 sbit yb12=yb[1]^12;
操作符“^”后面的位位置的最大值取决于指定的数据类型,char为0~7,int为0~15,long为0~31。
2.3.4 C语言常量与变量
常量在程序运行过程中不能改变,而变量在程序运行过程中可以不断变化。变量可以使用C51编译器支持的所有数据类型,而常量可以使用的数据类型只有整型、浮点型、字符型、字符串型和位变量。
1. 常量
常量可用在不必改变值的场合,如固定的数据表、字库等。
(1)整型常量可以表示为十进制,如123,0、-89等。十六进制则以0x开头,如0x34、-0x3B等。长整型是在数字后面加字母L,如104L、034L、0xF340等。
(2)浮点型常量采用十进制和指数表示形式。十进制由数字和小数点组成,如0.888、3345.345、0.0等,整数或小数部分为0,可以省略,但必须有小数点。指数表示形式为[±]数字[.数字]e[±]数字,[]中的内容为可选项,根据具体情况可有可无,但其余部分必须有,如125e3、7e9、-3.0e-3。
(3)字符型常量是指单引号内的字符,如'a'、'd'等,而对于无法显示的控制字符,可以在该字符前面加一个反斜杠“\”组成专用转义字符。常用转义字符参见表2-6。
(4)字符串型常量由双引号内的字符组成,如"test"、"OK"等。当引号内没有字符时,为空字符串。在使用特殊字符时同样要使用转义字符。字符串常量是作为字符类型数组来处理的,在存储字符串时,系统会在字符串尾部加上“\o”转义字符作为该字符串的结束符。字符串常量"A"和字符常量'A'是不同的,前者在存储时会多占用一个字节的空间(用于存储“\o”)。
表2-6 常用转义字符表
(5)位标量的值是一个二进制。
常量的定义方式有几种,下面来加以说明。
#difine False 0x0; //用预定义语句定义常量 #difine True 0x1; //这里定义False为0,True为1
程序中用到False和True,在编译时,会将False替换为0,True替换为1。
unsigned int code a=100; //这一句用code把a定义在程序存储器中并赋值 const unsigned int c=100; //用const定义c为无符号int常量并赋值
a和c的值都保存在程序存储器中,而程序存储器在运行中是不允许被修改的,如果之后用了类似a=110、a++这样的赋值语句,编译时将会出错。
2. 变量
变量是一种在程序执行过程中其值能不断变化的量。要在程序中使用变量必须先用标识符作为变量名,并指出所用的数据类型和存储模式,这样编译系统才能为变量分配相应的存储空间。定义一个变量的格式如下:
[存储种类]数据类型[存储器类型]变量名表
在定义格式中,除了数据类型和变量名表是必要的,其他都是可选项。
(1)存储种类。存储种类有4种:自动(auto)、外部(extern)、静态(static)和寄存器(register),缺省类型为自动(auto)。
(2)数据类型。和前面的各种数据类型的定义是一样的。说明了一个变量的数据类型后,还可选择说明该变量的存储器类型。
(3)存储器类型。存储器类型是指定该变量在C51硬件系统中所使用的存储区域,并在编译时准确地定位。C51的存储器类型如表2-7所示。
如果省略存储器类型,系统会按存储模式SMALL、COMPACT或LARGE所规定的默认存储器类型去指定变量的存储区域。无论是什么存储模式,都可以声明变量位于任何的8051存储区范围,还有变量的存储种类与存储器类型是完全无关的。
表2-7 存储器类型
(4)存储模式。在SMALL存储模式中,所有函数变量和局部数据段都被放在8051系统的内部数据存储区,访问数据非常快,但SMALL存储模式的地址空间受限。在小型的应用程序中,变量和数据放在data内部数据存储器中是很好的,因为访问速度快。但在较大的应用程序中,data区最好只存放小的变量、数据或常用的变量(如循环计数、数据索引),而大的数据则放置在别的存储区域。
在COMPACT存储模式中,所有的函数和程序的变量和局部数据段均定位在8051系统的外部数据存储区。外部数据存储区最多可有256字节(一页)。在本模式中,外部数据存储区的短地址用@R0/R1表示。
在LARGE存储模式中,所有函数和过程的变量和局部数据段都定位在8051系统的外部数据存储区,外部数据存储区最多可有64KB,这要求用DPTR数据指针访问数据。
【技能训练2-2】汽车转向灯控制设计
任务5是通过开关来控制LED循环点亮的,我们如何使用开关来完成汽车转向灯控制设计呢?
1. 汽车转向灯功能分析
汽车转向灯有前左右转向灯和后左右转向灯,它们的功能如下。
(1)左右转向灯的作用是在汽车需要左转或者右转时,提醒前面和后面的车辆、行人等,车辆需要转弯,请注意。
(2)左右转向灯同时闪烁时,示意车辆有危险情况,请注意避让。
2. 汽车转向灯控制电路设计
根据汽车转向灯功能分析,开关控制汽车转向灯电路主要由单片机最小系统、开关和LED等电路组成,如图2-17所示。
根据图2-17所示,在任务5的基础上添加单刀三掷开关(SW-ROT-3)、黄色发光二极管(LED-YELLOW)和排阻(RESPACK-7)。汽车转向灯控制电路设计方法如下。
(1)开关控制电路
转向灯开关和双闪开关的右边引脚接地、左边引脚经上拉电阻接电源;
转向灯开关左边有3个引脚,上面引脚(左转开关)接P0.5引脚,中间引脚悬空,下面引脚(右转开关)接P0.7引脚;
双闪开关的左边引脚接P0.4引脚。
(2)汽车转向灯电路
汽车的前后左转向灯LED的阴极接P2.5,汽车的前后右转向灯LED的阴极接P2.4,转向灯LED的阳极经限流电阻接电源。
图2-17 汽车转向灯控制电路
3. 汽车转向灯控制实现分析
如图2-17所示,转向灯开关是分别控制前左右转向灯和后左右转向灯的,双闪开关是同时控制前左右转向灯和后左右转向灯的。汽车转向灯控制实现过程如下。
(1)当转向灯开关打到左转位置时,左侧前后转向灯LED开始闪烁。
(2)当转向灯开关打到右转位置时,右侧前后转向灯LED开始闪烁。
(3)当双闪开关闭合时,不论转向灯开关处于什么位置,左右侧的前后转向灯都同时开始闪烁,直到双闪开关断开停止闪烁。
(4)当转向灯开关打到悬空位置时,转向灯LED停止闪烁。
4. 汽车转向灯程序设计
从以上分析可以看出,汽车转向灯控制C语言程序如下:
#include <reg52.h> //包含reg52.h头文件 sbit SW=P0^4; //定义SW,是双闪开关 sbit SWL=P0^5; //定义SWL ,是左转开关 sbit SWR=P0^7; //定义SWR ,是右转开关 sbit LEDR=P2^4; //定义LEDR,控制右侧前后转向灯LED 亮和灭 sbit LEDL=P2^5; //定义LEDL,控制左侧前后转向灯LED 亮和灭 void Delay() //延时函数 { unsigned char i, j; for (i=0;i<255;i++) for (j=0;j<255;j++); } void main() { while(1) { while(SW==0) //双闪开关闭合,左右侧的前后转向灯同时开始闪烁 { LEDR=0; //右侧前后转向灯LED 点亮 LEDL=0; //左侧前后转向灯LED 点亮 Delay(); //延时,左右侧的前后转向灯都保持点亮一段时间 LEDR=1; //右侧前后转向灯LED 熄灭 LEDL=1; //左侧前后转向灯LED 熄灭 Delay(); //延时,左右侧的前后转向灯都保持熄灭一段时间 } while(SWL==0) //左转开关闭合,左侧的前后转向灯同时开始闪烁 { LEDL=0; //左侧前后转向灯LED 点亮 Delay(); //延时,左侧的前后转向灯都保持点亮一段时间 LEDL=1; //左侧前后转向灯LED 熄灭 Delay(); //延时,左侧的前后转向灯都保持熄灭一段时间 if(SW==0) break; //若双闪开关闭合,进入双闪状态 } while(SWR==0) //右转开关闭合,右侧的前后转向灯同时开始闪烁 { LEDR=0; //右侧前后转向灯LED 点亮 Delay(); //延时,右侧的前后转向灯都保持点亮一段时间 LEDR=1; //右侧前后转向灯LED 熄灭 Delay(); //延时,右侧的前后转向灯都保持熄灭一段时间 if(SW==0) break; //若双闪开关闭合,进入双闪状态 } LEDR=1; //右侧前后转向灯LED 熄灭 LEDL=1; //左侧前后转向灯LED 熄灭 } }