- 单片机及接口技术项目教程
- 李建兰主编
- 6012字
- 2025-03-01 11:48:00
2.7 训练项目
2.7.1 点亮LED发光二极管
1.目的
(1)学会Keil软件的使用。
(2)掌握MCS-51单片机通用I/O口的使用方法。
(3)掌握位变量的定义及使用。
(4)掌握LED发光二极管点亮方法。
2.任务
本项目要完成的任务是使用P1口的某一引脚(如P1.0)控制LED发光二极管点亮。
3.任务引导
由前面的学习可知,要想验证P1口的输出电平是不是由你编写的程序输出的电平,可以采用一个非常简单有效的办法,就是在想验证的端口接一个发光二极管。当输出高电平时,发光二极管灭;输出低电平时,发光二极管亮。
4.任务实施
1)硬件电路设计
单片机最小系统控制电路如图2-11所示。

图2-11 单片机最小系统控制电路
注意:图中P0口8个上拉电阻用一个排阻,电路简单实用,排阻外观及引脚图如图2-12所示。在排阻上一般都标有阻值号如102、103等,102表示其阻值大小为1kΩ;103表示其阻值大小为10kΩ。

图2-12 排阻外观及引脚
2)软件设计
编写C51控制源程序如下所示(采用两种方式实现)。
(1)按位方式实现。
/********************************************************************** * @ File:chapter 2_1.c * @ Function:点亮第一个发光二极管 **********************************************************************/ #include<reg51.h> //51系列单片机头文件 sbit LED1=P1^0; //定义LED1 void main() //主函数 { LED1=0; //点亮第一个发光二极管 while(1); //程序暂停 }
(2)按字节方式实现。
/********************************************************************** * @文件:chapter 2_2.c * @Function:点亮第一个发光二极管 **********************************************************************/ #include<reg51.h> //51系列单片机头文件 void main() //主函数 { P1=0xfe; //第一个发光二极管送有效信号,其他无效 while(1); //程序暂停 }
关于程序的几点说明:
(1)“#include <reg51.h>”的作用及内容。
在上述程序中,由于用到了特殊功能寄存器P1口的第一位(P1.0)和P1,使用它们时必须事先加以定义。Keil编译器对单片机特殊功能寄存器的定义都是放在一个名为reg51.h的头文件中的,所以程序中需要先用预处理命令“#include <reg51.h>”将51单片机的特殊功能寄存器定义包含进来,只有这样使用51单片机的特殊功能寄存器才是合法的,否则编译器就会报错。
打开reg51.h头文件可以看到下面的一些内容。
/*-------------------------------------------------------------------------- REG51.H Header file for generic 80MCS-51 and 80C31 microcontroller. Copyright (c) 1988-2002 Keil Electronic Gmbh and Keil Software, Inc. All rights reserved. --------------------------------------------------------------------------*/ #ifndef __REG51_H__ #define __REG51_H__ /* BYTE Register */ sfr P0 = 0x80; sfr P1 = 0x90; sfr P2 = 0xA0; sfr P3 = 0xB0; sfr PSW = 0xD0; sfr ACC = 0xE0; sfr B = 0xF0; sfr SP = 0x81; sfr DPL = 0x82; sfr DPH = 0x83; 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; /* BIT Register */ /* PSW */ sbit CY = 0xD7; sbit AC = 0xD6; sbit F0 = 0xD5; sbit RS1 = 0xD4; sbit RS0 = 0xD3; sbit OV = 0xD2; sbit P = 0xD0; /* 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; /* IP */ sbit PS = 0xBC; sbit PT1 = 0xBB; sbit PX1 = 0xBA; sbit PT0 = 0xB9; sbit PX0 = 0xB8; /* P3 */ sbit RD = 0xB7; sbit WR = 0xB6; sbit T1 = 0xB5; sbit T0 = 0xB4; sbit INT1 = 0xB3; sbit INT0 = 0xB2; sbit TXD = 0xB1; sbit RXD = 0xB0; /* 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; #endif
上述内容都是MCS-51单片机中一些符号(包括特殊功能寄存器和特殊位)的定义,即定义符号名与地址的对应关系。
① 特殊功能寄存器定义。
例如:
sfr P1=0x90; //定义P1 与地址0x90 对应
上述程序定义P1口的地址为0x90(0x90是C语言中十六进制数的写法,相当于汇编语言中的90H)。
从上面的头文件中可以看到一个频繁出现的词:sfr,sfr并不是标准C语言的关键字,而是Keil软件为能直接访问MCS-51单片机中的特殊功能寄存器(SFR)提供的一个新的关键词,其用法为:
sfr变量名=地址值;
② 特殊位定义。
例如:
sbit LED1=P1^0; //符号LED1用来表示P1.0引脚
在C语言里,如果直接写P1.0,C编译器并不能识别,而且P1.0也不是一个合法的C语言变量名,所以需要给它另取一个名字,这里取名为P1.0,可是LED1是不是就是P1.0呢? C编译器并不这么认为,所以必须给它们建立联系,这里使用了Keil C的关键字sbit来定义,sbit的用法有以下三种。
第一种方法:
sbit 位变量名=地址值;
第二种方法:
sbit 位变量名=sfr名称^变量位地址值;
第三种方法:
sbit 位变量名=sfr地址值^变量位地址值;
如定义PSW中的OV可以使用以下三种方法:
sbit OV=0xd2; //0xd2是OV的位地址值 sbit OV=PSW^2; //其中PSW必须先用sfr定义好 sbit OV=0xD0^2; //0xD0就是PSW的地址值
(2)“while(1);”语句的作用。
while()是C语言中的循环控制语句,当程序执行完LED1=0后,它还将向下执行,但后面的空间并没有存放程序代码,这时程序会乱运行,也就是说发生了“跑飞”现象。加上“while(1);”语句,是让程序一直停止在这里不再往下运行,即防止程序“跑飞”。这条语句后面经常用到,读者要学会用它。
3)程序编译、调试、运行与仿真
打开Keil软件,建立工程,输入上述源程序并编译。编译界面如图2-13所示。

图2-13 点亮LED1编译界面
编译后可进行程序调试与运行。
调试、运行程序的同时,可调出键盘、LED显示实验仿真板,其仿真结果如图2-14所示。

图2-14 点亮LED1仿真结果
2.7.2 单灯闪烁
1.目的
(1)进一步学会使用Keil软件调试程序。
(2)熟悉并掌握延时子函数的编写方法及用途。
(3)掌握信号灯实现闪烁的方法。
2.任务
本项目要完成的任务是使LED1发光二极管以1Hz的频率不间断闪烁。
3.任务引导
由于CPU执行的速度非常快,要让人的眼睛感觉到LED有亮—灭—亮—灭……的变化,必须降低显示的速度,所以在程序中要加入一段延时子函数。频率需要1Hz,则周期为1s,需要编写一段延时500ms的子函数。
延时子函数的编写方法:用软件实现延时的基本方法是让程序绕圈子做无用功,即让CPU去执行一些与输出无关的命令,以达到拖延时间的目的,如以下子函数均可以实现延时。
/*****************xms级延时子函数1*********************/ void delayms(unsigned int xms) { unsigned int i,j; for(i=xms;i>0;i--) for(j=110;j>0;j--); //执行时间约为1ms } /*********************xms级延时子函数2*****************/ void delayms(unsigned int ms) { unsigned char data x; unsigned int data y; for(y = ms;y > 0;y--) { for(x = 125;x>0;x--) { _nop_(); //延时1μs函数 } } } /*********************xμs级延时子函数*********************/ void Delayxμs(unsigned int n) { while (n--) { _nop_(); //延时1μs函数 _nop_() } }
其中,延时子函数中出现的“_nop_()”为空函数,能延时1个机器周期的时间。一个机器周期为12个振荡周期,如果单片机晶振为12MHz,则一个机器周期的时间为1μs。延时还有很多方法,如使用定时器/计数器与中断等,关于这些方法将在后续项目中介绍。

图2-15 LED灯闪烁流程图
4.任务实施
1)硬件电路设计
单片机控制电路与图2-10所示电路相同。
2)软件设计
根据LED灯闪烁工作原理,画出其程序流程图如图2-15所示。
由LED灯闪烁流程图,可以很方便地编写出C51控制源程序,其源程序如下所示(采用两种方式实现)。
(1)按位方式实现。
/********************************************************************** * @File:chapter 2_3.c * @ Function:单灯闪烁 **********************************************************************/ #include<reg51.h> //51系列单片机头文件 #include<stdio.h> //标准I/O库函数头文件 #define uint unsigned int //宏定义 sbit LED1=P1^0; //定义LED1 void delayms(uint xms) //延时xms子函数 { uint i,j; for(i=xms;i>0;i--) for(j=125;j>0;j--); } void main() //主函数 { while(1) //大循环 { LED1=0; //点亮第一个发光二极管 delayms(500); //调延时子函数,延时500ms LED1=1; //关闭第一个发光二极管 delayms(500); //调延时子函数,延时500ms } }
(2)按字节方式实现,同时增加串口窗口显示。
/********************************************************************** * @File:chapter 2_4.c * @ Function:单灯闪烁 **********************************************************************/ #include<reg51.h> //51系列单片机头文件 #include<stdio.h> //标准I/O库函数头文件 #define uint unsigned int //宏定义 void delayms(uint xms) //延时xms子函数 { uint i,j; for(i=xms;i>0;i--) for(j=125;j>0;j--); } void main() //主函数 { SCON=0x52; //串口初始化 TMOD=0x20; TH1=0xf3; TR1=1; printf("Program Running!\n"); //输出两行信息 printf(" LED1灯闪烁 \n"); printf("\n"); //显示换行 while(1) //大循环 { P1=0xfe; //第一个发光二极管送有效信号,其他无效 delayms(500); //延时500ms P1=0xff; //关闭第一个发光二极管 delayms(500); //延时500ms } }
读者可能已经发现,在上述程序段中多了以下几条C语句:
SCON=0x52; //串口初始化 TMOD=0x20; TH1=0xf3; TR1=1; printf("Program Running!\n"); //输出三行信息 printf(" LED1灯闪烁 \n"); printf("\n ");
其中前4条是串口初始化,后3条是printf() 函数。Keil软件里专门提供了一个串口显示窗口(UART #1),用于显示一些与程序相关的信息。
MCS-51单片机的一般I/O函数库中定义的I/O函数都是通过串行接口实现的,串行口的波特率由定时器/计数器1溢出率决定。在使用I/O函数之前,应先对MCS-51单片机的串行接口和定时器/计数器1进行初始化。串口工作于方式1,定时器/计数器1工作于方式2 (8位自动重载方式),设系统时钟为12MHz,波特率为2400bps,则初始化程序即为上面的4条语句。有关串口知识将在后面串行通信接口技术项目中再进行介绍。
3)程序编译、调试、运行与仿真
打开Keil软件,建立工程,输入上述源程序并编译。
调试、运行程序,调出键盘、LED显示实验仿真板与串口窗口,其仿真结果如图2-16所示。

图2-16 LED1灯闪烁仿真结果
2.7.3 流水灯控制
1.目的
(1)进一步学会使用Keil软件调试程序。
(2)进一步掌握MCS-51单片机通用I/O口的使用方法。
(3)掌握单片机控制八彩灯显示多种花样的编程方法。
(4)学会硬件电路板焊接与电路板测试方法。
2.任务
本项目要完成的任务是设计并制作一个彩灯控制器,让八个彩灯以一定速度按多种显示花样动作。
3.任务引导
控制八个彩灯以不同速度显示是通过单片机的I/O接口输出不同的脉冲序列来实现的,通过改变延时函数参数来控制。而控制彩灯花样的方法很多,可采用一维数组、二维数组或循环移位等方式实现。
4.任务实施
1)硬件电路设计
单片机控制八彩灯最小系统电路如图2-17所示。

图2-17 单片机控制八彩灯最小系统电路
2)软件设计
编写C51控制源程序如下所示(采用三种方式实现)。
(1)用一维数组实现。
/********************************************************************** * @ File:chapter 2_5.c * @Function:一维数组流水灯控制 **********************************************************************/ #include<reg51.h> //51系列单片机头文件 #include<stdio.h> //标准I/O库函数头文件 #define uint unsigned int //宏定义 #define uchar unsigned char uchar num; uchar code table[]={0x81,0xc3,0xe7,0xff,0x18,0x3c,0x7e,0xff}; //显示花样 void delayms(uint xms) //延时xms子函数 { uint i,j; for(i=xms;i>0;i--) for(j=125;j>0;j--); } void main() { SCON=0x52; //串口初始化 TMOD=0x20; TH1=0xf3; TR1=1; printf("Program Running!\n"); //输出两行信息 printf(" 一维数组流水灯控制 "); printf("\n "); while(1) //大循环取花样代码 { for(num=0;num<8;num++) { delayms(500); //延时500ms P1=table[num]; } } }
(2)用二维数组实现。
/********************************************************************** * @ File:chapter 2_6.c * @Function:二维数组流水灯控制 **********************************************************************/ #include<reg51.h> //51系列单片机头文件 #include<stdio.h> //标准I/O库函数头文件 #define uint unsigned int //宏定义 #define uchar unsigned char uchar num; uchar code table[6][9]={{0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0xff}, {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff}, {0x7e,0x3c,0x18,0x00,0x7e,0x3c,0x18,0x00,0xff}, {0x0f,0xf0,0x0f,0xf0,0x0f,0xf0,0x0f,0xf0,0xff}, {0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xff}, {0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff}}; //显示花样 void delayms(uint xms) { uint i,j; for(i=xms;i>0;i--) for(j=125;j>0;j--); } void main() { uchar x,y; //定义变量 SCON=0x52; //串口初始化 TMOD=0x20; TH1=0xf3; TR1=1; printf("Program Running!\n"); //输出两行信息 printf(" 二维数组流水灯控制 "); while(1) //大循环取花样代码 { for(x=0;x<6;x++) { for(y=0;y<9;y++) { P1=table[x][y]; delayms(1000); //延时1000ms } } } }
(3)采用循环移位方式实现。
/********************************************************************** * @ File:chapter 2_7.c * @Function:循环移位流水灯控制 **********************************************************************/ #include<reg52.h> //52系列单片机头文件 #include <inTR1ns.h> #include<stdio.h> //标准I/O库函数头文件 #define uint unsigned int //宏定义 #define uchar unsigned char void delayms(uint xms) { uint i,j; for(i=xms;i>0;i--) //i=xms,即延时xms for(j=125;j>0;j--); } void main() { uchar a; //定义变量 SCON=0x52; //串口初始化 TMOD=0x20; TH1=0xf3; TR1=1; printf("Program Running!\n"); //输出两行信息 printf(" 流水灯控制 "); a=0xfe; //赋初值11111125B while(1) //大循环 { P1=a; delayms(500); //延时500ms a=_crol_(a,1); //调循环左移函数 } }
3)程序编译、调试、运行与仿真
打开Keil软件,建立工程,输入上述源程序,编译后生成HEX文件。
调试、运行程序,调出键盘、LED显示实验仿真板与串口窗口,其仿真结果如图2-18所示。
5.电路板制作与测试
1)元器件清单
根据设计好的电路原理图2-17,列出元器件清单如表2-4所示。
表2-4 流水灯控制电路元器件清单


图2-18 流水灯控制仿真结果
2)焊接电路板、下载HEX文件并排查故障
故障检查要领如下。
首先焊接电路板,待电路板焊接好后,打开STC-ISP下载软件将HEX文件下载至单片机,然后给电路板上电。
若上电后发现51单片机的电路板无法正常动作时,请按照下述步骤进行检查。
(1)先确定电路已确实按电路图焊接好,尤其是LED的方向是否连接正确。
(2)用万用表的DCV挡测量STC89C51RC单片机的第40脚与第20脚间的电压,应指示4.5~5.5V;否则请检查电源。
(3)以逻辑笔测量STC89C51RC单片机的第18脚或30脚,测试笔的黄灯(PULSE)应发亮,否则振荡电路有故障。其原因有三:一为晶振故障;二为30pF电容器短路;三为单片机已经损坏。
(4)用万用表的DCV挡测量STC89C51RC单片机的第9脚与第20脚间的电压,应指示0V。以逻辑笔测量STC89C51RC单片机的第9脚,应亮绿灯,否则为10μF电容器有故障,请检查。
(5)用万用表的DCV挡测量STC89C51RC单片机的第31脚与第20脚间的电压,应指示4.5~5.5V,否则为接线错误或接触不良,请检查。
注意:若第31脚悬空,当受噪声干扰时,电路板会出现动作有时正常有时不正常的现象,故第31脚不能悬空,应接到电源的正极。
3)电路板上电显示
软硬件都检查无误,电路板上电即可观察到八彩灯按不同的花样显示。
利用单片机控制LED彩灯能做出各种各样的霓虹灯花样来,如图2-19所示是单片机控制霓虹灯实物的实际显示效果图。

图2-19 单片机控制霓虹灯实物的实际显示效果图
2.7.4 蜂鸣器控制
1.目的
(1)掌握蜂鸣器发声原理。
(2)进一步掌握MCS-51单片机通用I/O口的使用方法。
(3)掌握单片机控制蜂鸣器发声的编程方法。
2.任务
本项目要完成的任务如下。
任务1:用单片机作为主控制器控制信号灯和蜂鸣器,信号灯闪烁5次后,蜂鸣器响5声。
任务2:用单片机某一位输出1kHz和500Hz的音频信号,驱动蜂鸣器作为报警信号,要求1kHz信号响100ms,500Hz信号响200ms,两者交替进行。
3.任务引导

图2-20 蜂鸣器实物图
蜂鸣器(SPEAKER)是一种电声转换器件,其实物图如图2-20所示。使用时只要让蜂鸣器通过大小变化的电流(脉动电流),就能使蜂鸣器发出声音。因此,若程序不断地输出1—0—1—0…就可令蜂鸣器发出声响。
由于MCS-51系列的单片机端口输出电流不够大,蜂鸣器必须外加驱动电路才能使用,一般使用三极管或反相器驱动,常用的驱动电路如图2-21所示。
本项目电路板利用MCS-51单片机端口输出脉冲方波,经74LS06反相驱动(注意:74LS06使用时要外接上拉电阻)后使蜂鸣器发声,声音的频率高低由延时函数时间长短控制。

图2-21 蜂鸣器驱动电路
4.任务实施
1)硬件电路设计
单片机控制蜂鸣器报警电路如图2-22所示。

图2-22 单片机蜂鸣器报警控制电路
2)软件设计
程序流程图如图2-23所示。

图2-23 蜂鸣器报警控制程序流程图
编写C51控制源程序如下所示。
(1)以下是蜂鸣器报警控制实现任务1的C51源程序。
/**************************************************************************** * @ File:chapter 2_8.c * @Function:蜂鸣器报警 ****************************************************************************/ #include<reg52.h> #include<stdio.h> //标准I/O库函数头文件 #define uchar unsigned char //宏定义 #define uint unsigned int uchar num1,num2; sbit speaker=P2^7; //蜂鸣器定义 sbit LED1=P1^0; //指示灯定义 void delayms(uint xms) //延时子函数 { uint i,j; for(i=xms;i>0;i--) for(j=125;j>0;j--); } void main() //主函数 { SCON=0x52; //串口初始化 TMOD=0x20; TH1=0xf3; TR1=1; printf(" Program Running!\n"); //输出四行信息 printf(" 蜂鸣器报警 \n "); printf(" 指示灯闪烁5次 \n "); printf(" 蜂鸣器响5声 \n "); while(1) //大循环 { num1=1,num2=1; while(num1<=5) //指示灯闪烁5次 { LED1=0; delayms (500); LED1=1; delayms(500); num1++; } while(num2<=5) //蜂鸣器响5声 { speaker=0; delayms(300); speaker=1; delayms(200); num2++; } } }
(2)以下是蜂鸣器报警控制实现任务2的C51源程序。
/********************************************************************** * @ File:chapter 2_9.c * @Function:声光报警 **********************************************************************/ #include <reg52.h> #include <inTR1ns.h> #define uchar unsigned char sbit speaker=P2^7; //蜂鸣器定义 uchar count; void delay500(void) //延时500μs子函数 { uchar i; for(i=500;i>0;i--) { _nop_(); } } void main(void) //主函数 { while(1) { for(count=200;count>0;count--) //1kHz信号响100ms { speaker=~speaker; delay500(); } for(count=200;count>0;count--) //500Hz信号响200ms { speaker=~speaker; delay500(); delay500(); } } }
3)程序编译、调试、运行与仿真
打开Keil软件,建立工程,输入上述两段源程序并编译。
调试、运行程序,调出键盘、LED显示实验仿真板与串口窗口,观察程序运行情况,如图2-24所示。

图2-24 声光报警控制显示
5.项目制作步骤
(1)运用一种绘图工具绘制单片机控制声光报警电路图,并列出元件清单。
(2)采用C语言编写控制源程序。
(3)采用Keil软件上机调试源程序,并生成HEX文件。
(4)焊接电路板。
(5)下载HEX文件至焊接好的电路板,进行软硬件联调。
调用STC-ISP下载软件,将生成的HEX文件烧录到已做好的电路板上,如果电路板正确无误,上电后就能听到蜂鸣器不间断地发出声音。
2.7.5 继电器控制
1.目的
(1)掌握继电器通断工作原理。
(2)进一步掌握MCS-51单片机通用I/O口的使用方法。
(3)掌握单片机控制继电器动作的编程方法。
2.任务
本项目要完成的任务是利用单片机某一个端口作为控制输出口,接继电器电路,使继电器重复吸合与断开。
3.任务引导
在现代自动控制设备中,都存在一个电子电路的互相连接问题,一方面要使电子电路的控制信号能控制电气电路的执行元件(电动机、电磁铁、LED信号灯等),另一方面又要为电子线路和电气电路提供良好的电气隔离,以保护电子电路和人身的安全。继电器便能完成这一任务。
由继电器工作原理可知,要使执行元件动作,应先将继电器线圈两端通电,常开触点闭合,常闭触点打开,这样与之相连的执行元件将跟着动作。
4.任务实施
1)硬件电路设计
单片机控制继电器电路如图2-25所示。

图2-25 单片机控制继电器电路图
原理图说明:单片机P2口的P2.7作为控制端与8050三极管的基极相连,三极管集电极与继电器线圈一端相连。当控制端为高电平时,继电器线圈通电,常开触点吸合,LED2灯被点亮,常闭触点打开,LED1灯灭;当控制端为低电平时,继电器线圈断电,LED2灯灭,常闭触点恢复常态,LED1灯亮。如果给控制端不断送1—0—1—0—1…信号,则两盏LED灯将交替循环闪烁。
2)软件设计
继电器控制程序流程图如图2-26所示。

图2-26 继电器控制程序流程图
编写C51控制源程序如下所示。
/********************************************************************** * @ File:chapter 2_10.c * @Function:继电器控制 **********************************************************************/ #include<reg52.h> #define uchar unsigned char //宏定义 #define uint unsigned int sbit output=P2^7; //继电器线圈定义 void delayms(uint xms) //延时xms子函数 { uint i,j; for(i=xms;i>0;i--) for(j=125;j>0;j--); } void main() { while(1) { output=1; //继电器线圈通电 delayms (500); output=0; //继电器线圈断电 delayms (500); } }
3)程序编译、调试、运行与仿真
打开Keil软件,建立工程,输入上述源程序并编译生成HEX文件。
调用Proteus仿真软件,观察仿真电路运行情况,其仿真结果如图2-27所示。

图2-27 继电器控制电路仿真结果