《 单 片 机 原 理 及 应用》
实 验 指 导 书 (C语言)
XX大学物电学院 微机教研室 XX 2011 前 言 由于单片机具有高可靠性、超小型、低价格、容易产品化等特点,在仪器仪表智能化、实时工业控制、实时数据采集、智能终端、通信设备、导航系统、家用电器等控制应用领域,具有十分广泛的用途。由于目前在国内单片机应用中,MCS-51系列单片机仍然是一种主流单片机,所以本实验指导书为学习MCS-51单片机的学生,配合《单片机原理及应用》课程的教学,结合本学院自制单片机教学实验板编写了这本实验指导书。
《单片机原理应用及》是一门实践性很强的课程,提高教学质量的一个重要环节是上机实习和训练,无论是学习汇编语言程序设计,还是学习接口电路和外设与计算机的连接,或者软硬兼施地研制单片机应用系统,不通过加强动手是不能获得预期效果的。本实验指导书提供多个实验的指导性材料,有些实验还有一些有一定难度的选做项目,可以根据课时的安排和教学要求进行取舍。为了达到某些实验的目的,书中提供的参考程序与实际应用中的程序会有些差别,所以不一定是最优的。
由于时间紧迫,需要赶课程进度与实验时间的同步,加上编者学识有限,如有不妥之处,欢迎读者批评指正。
实 验 须 知 1. 实验前必须阅读教科书的有关部分和本实验指导书,了解实验目的、内容、步骤,做好实验前的准备工作,编写好实验中要求自编或修改的程序;
完成实验前要求完成的准备工作后方可以上机实验,否则不得上机操作。
2. 各种电源的电压和极性不能接错,严禁带电接线和接插元器件。通电前须经过指导教师检查认可后方能通电。
3. 不准随意拨弄各种与实验无关的旋钮和开关,凡与本次实验无关的任何设备都禁止动用和摸弄,注意安全。
4. 严禁用手触摸实验系统印制电路板和元器件的引脚,防止静电击穿芯片。
5. 实验中若损坏仪器或元器件,应及时向指导教师报告。
6. 在实验室内保持安静和卫生,不得随意走动和喧哗,集中精力完成实验。
7. 实验完成后,关掉电源,及时整理实验台桌面,保持环境整洁。
8. 按规定认真完成实验报告,在规定的时间内缴上实验报告。
9. 凡实验或实验报告未能按规定完成的学员,不能参加本课程的考试或考查。
单片机实验报告格式 实验报告标题 一、实验目的 二、实验内容 三、实验设计及调试:
(1)实验分析及内容。
(2)实验电路:画出与实验内容有关的简单实验电路。
(3)实验设计及调试步骤:根据内容写出实验程序。调试程序,观察结果。
(4)实验调试过程中所遇到的问题、解决问题的思路和解决的方法。
四、实验后的经验教训总结。
五、对实验课的建议 目 录 实验一 实验板使用,KEIL C51软件使用 6 实验二 单片机控制LED灯点亮 12 实验三 模拟开关灯 16 实验四 单片机控制数码管实验 21 实验五 中断系统应用实验 25 实验六 LED 数码管的动态驱动 29 实验七 定时器/计数器使用 32 实验八 数字电子钟 36 实验九 矩阵键盘识别实验 37 实验十 自动演奏乐曲 41 实验十一 综合实验 交通信号灯控制器的设计 45 实验十二 教学板自检程序设计 46 实验十三 综合实验:数据采集—火灾报警装置的软硬件设计 47 附录:实验教学板电路原理图 48 实验一 实验板使用,Keil C51软件使用 一、实验目的 1. 熟悉单片机实验板、Keil C51软件使用 二、实验说明 本实验介绍实验板的组成、Keil C51软件使用以及烧录软件的使用。通过该实验学生可以了解单片机编程、调试方法。
三、实验内容及步骤 1.启动PC机,安装好Keil C51软件以及烧录软件,用串口线连接计算机与实验板(USB线提供电源)。
2.打开Keil uVision2仿真软件,首先建立本实验的项目文件,接着建立源程序,编译无误后,全速运行程序。
3.可把源程序编译成可执行文件,用烧录器烧录到89芯片中。
四、Keilc 软件使用说明 1、打开程序:
双击程序 2、新建工程:Project → New Project→保存工程→选择CPU(一般选ATMEL公司的AT89S51)
-17- 3、设置工程:右击→Options for ‘Target 1’ →→选择Create HEX (输出HEX文件)。
-18- 4、新建源文件:选File→New→输入、编辑源程序→保存(汇编源程序:文件名.asm;C源程序:文件名.c)
5、为工程添加源文件:在Project出口File页上单击文件组,选择Add Files to Group‘Source Group 1’选项,选择你的源文件和文件类型,然后点击ADD,再点击close。
-19- 6、编译:点击 ,或右击Target 1 →Build Target F7,就可对源程序进行编译。当程序有语法错误时,会在输出窗口(Output Windows)中显示错误信息和警告信息,修改编译成功后会生成 HEX文件。
7、下载、调试:
编译成功后,用在线烧录程序将.HEX文件下载到单片机内部ROM中运行,在运行过程中若发现错误要重新修改程序,并编译后再下载运行。
五、烧录软件的使用 1、打开在线烧录程序 2、选择MCU类型(STC89C51RC)
1、 点击, 打开工程目录下的hex文件 2、 选择端口(一般是COM1) 3、 点击Download烧录程序 4、 打开实验板上的电源(如果烧录的时候出现问题,点击stop) 注意:
5、6 两步骤的顺序不能颠倒!即在点击Download之前要先关掉实验板上的电源。
实验二 单片机控制LED灯点亮 一、实验目的 1.进一步熟悉编程和程序调试 2.学习P1口的使用方法 3.学习延时子程序的编写和使用 二、实验说明 (1)输出控制。
如图1所示 ,当P1 . 0端口输出高电平,即P1.0=1时 ,根据发光二极管的单向导电性 可知,这时发光二极管L1熄灭;
当 P1 .0端口输出低电平,即 P1 .0=0时,发光二极管L 1亮;
我们可以使用SETB P1.0指令使 P 1. 0端口输出高电平 , 使用CLR P1.0指令使 P1 .0 端口输出低电平 。
(2)延时子程序的设计方法 作为单片机的指令的执行时间是很短的,数量达微秒级,因此,如果我们要求的闪烁时间间隔为0.2秒,相对于微秒来说,相差太大,所以我们在执行某一指令时,插入延时程序,来达到我们的要求,但这样的延时程序是如何设计呢?下面具体介绍其原理:
石英晶体为12MHz,因此,1个机器周期为1微秒 机器周期 微秒 MOV R6,#20 2个机器周期 2 D1: MOV R7,#248 2个机器周期 2×20 DJNZ R7,$ 2个机器周期 2×248×20 DJNZ R6,D1 2个机器周期 2×20=40 10002 因此,上面的延时程序时间为10.002ms。
由以上可知,当R6=10、R7=248时,延时5ms,R6=20、R7=248时,延时10ms,以此为基本的计时单位。如要求0.2秒=200ms,10ms×R5=200ms,则R5=20,汇编延时子程序如下:
DELAY: MOV R5,#20 D1: MOV R6,#20 D2: MOV R7,#248 DJNZ R7,$ DJNZ R6,D2 DJNZ R5,D1 RET C语言延时子程序如下:
void delay02s(void) //延时0.2秒子程序 { unsigned char i,j,k; for(i=20;i>0;i--) for(j=20;j>0;j--) for(k=248;k>0;k--); } 三、实验步骤及参考例子 实验步骤说明:
本实验需要用到单片机最小应用系统。
用P1口做输出口,程序功能使发光二极管点亮。
1.用串行数据通信线连接计算机与实验板,用USB给实验板提供电源 3.打开Keil uVision2仿真软件,首先建立本实验的项目文件,输入源程序(参考程序1),进行编译,直到编译无误。生成hex文件。
5.通过STC-ISP下载软件,将hex文件下载到实验板内,观察发光二极管显示情况。
参考例子:
(1) 点亮板子上的第一个灯D0 (2) 让第一个灯闪烁 四、参考程序 (一)适用于mini80E实验板 1)
#include<reg52.h> void main() { P1=0xfe; } 2)
#include<reg52.h> #define uint unsigned int #define uchar unsigned char delay(); void main() { while(1) { P1=0xfe; delay(); P1=0xff; delay(); } } void delay() { uint x,y; for(x=100;x>0;x--) for(y=600;y>0;y--) ; } (二)适用于自制最小系统实验板 (1)
#include <reg52.h> void main (void) { P2=0x00; P2=0x10; P0=0xfe; } (2)
#include <reg52.h> void delay(void); void main (void) { P2=0x00; while(1) {P2=0x10; P0=0xfe; delay(); //P2=0x00; P0=0xff; delay(); } } void delay(void) { int x,y; for(x=600;x>0;x--) for(y=100;y>0;y--); } 六、实验内容 请在keil环境下编写以下程序:
1)点亮最后一个LED(或者任意一个LED,或者任意几个LED) 2)让点亮的LED闪烁 3)点亮板子上的D0、D2、D4、D6灯,与D1、D3、D5、D7灯交替闪烁 4)设计出流水灯程序,从D7—D0或从D0-D7 实验三 模拟开关灯 1.实验目的 1.进一步熟悉编程和程序调试 2.学习独立按键的使用方法 2.实验说明 如果系统只需几个按键,可直接采用I/O线构成单个按键电路,各个按键之间相互独立,一根线上的按键状态不会影响其他输入线上的工作状态,又称独立式键盘接口电路。
检测是否有键闭合,如有键闭合,则去除键抖动,判断键号并转入相应的按键处理。编写单片机的键盘检测程序时,一般在检测按下时加入去抖延时,检测松手时就不用加了。
3、参考例子 1)通过四个按键来控制LED灯的显示情况:
S1:D0点亮 S2:D1点亮 S3:D2点亮 S4:D3点亮 4、参考程序 (一)适用于自制实验板 #include <REG51.H> #define uint unsigned int #define uchar unsigned char uchar flag; uchar key_down; sbit LED2 = P0^0; sbit LED3 = P0^1; sbit LED4 = P0^2; sbit LED5 = P0^3; /****************************/ void delay(uint k) { uint data i,j; for(i=0;i<k;i++) {for(j=0;j<121;j++) {;}} } /**********************************/ uchar scan_key(void) { uchar temp; temp=P3; return temp; } /***********************************/ void main(void) { key_down=0; P3=0xf7; P2=0x10; P0=0xff; while(1) { P3=0xf7; if((P3!=0xf7)&&(key_down==0)) { delay(20); if((P3!=0xf7)&&(key_down==0)) { flag=scan_key(); key_down=1; switch(flag) { case 0xe7:LED2 = ~LED2;break; case 0xd7:LED3 = ~LED3;break; case 0xb7:LED4 = ~LED4;break; case 0x77:LED5 = ~LED5;break; default: break; } } } if(P3==0xf7) { key_down=0; } } } (二)适用与mini80E板 #include <REG51.H> #define uint unsigned int #define uchar unsigned char uchar flag; /****************************/ void delay(uint k) { uint data i,j; for(i=0;i<k;i++) {for(j=0;j<121;j++) {;}} } /**********************************/ uchar scan_key(void) { uchar temp; temp=P3; return temp; } /***********************************/ void main(void) { while(1) { P3=0xff; if(P3!=0xff) { delay(20); if(P3!=0xff) flag=scan_key(); } else flag=0; switch(flag) { case 0xfe:P1=0xfe;break; case 0xfd:P1=0xfd;break; case 0xfb:P1=0xfb;break; case 0xf7:P1=0xf7;break; default:P1=0xff;break; } } } 或者 include <REG51.H> #define uint unsigned int #define uchar unsigned char uchar flag; uchar key_down; sbit LED2 = P1^0; sbit LED3 = P1^1; sbit LED4 = P1^2; sbit LED5 = P1^3; /****************************/ void delay(uint k) { uint data i,j; for(i=0;i<k;i++) {for(j=0;j<121;j++) {;}} } /**********************************/ uchar scan_key(void) { uchar temp; temp=P3; return temp; } /***********************************/ void main(void) { key_down=0; P3=0xff; P1=0xff; while(1) { P3=0xff; if((P3!=0xff)&&(key_down==0)) { delay(20); if((P3!=0xff)&&(key_down==0)) { flag=scan_key(); key_down=1; switch(flag) { case 0xfe:LED2 = ~LED2;break; case 0xfd:LED3 = ~LED3;break; case 0xfb:LED4 = ~LED4;break; case 0xf7:LED5 = ~LED5;break; default: break; } } } if(P3==0xff) { key_down=0; } } } 5、实验内容 1)8个按键分别对应8盏led灯的亮灭(针对自制教学板)
2)通过四个按键来控制LED灯的显示情况:
S1:D0-D3点亮 S2:D2点亮 S3:D0-D7点亮 S4:D0-D5点亮 实验四 单片机控制数码管实验 一、实验目的 1. 掌握数码管是如何显示出字符 2. 进一步掌握延时子程序的使用 二、实验原理 1.数码管两种接法 2. 共阴极数码管编码 § 0x3f , 0x06 , 0x5b , 0x4f , 0x66 , 0x6d , § 0 1 2 3 4 5 § 0x7d , 0x07 , 0x7f , 0x6f , 0x77 , 0x7c , § 6 7 8 9 A B § 0x39 , 0x5e , 0x79 , 0x71 , 0x00 § C D E F 无显示 请思考共阳极数码管的编码。
三、参考例子 1)让第一个数码管显示一个8字 A) 对于MINI80E实验板(共阴极), 数码管的选通是通过P2口控制三八译码器得到的,要让第一个数码管显示8字,那么别的数码管的位选就要关闭,即只打开第一个数码管的位选。控制位选的P2口要输出的数据位0xf7(二进制为1111 0111)。位选确定后,在确定段选,要显示的是8,那么只有dp段为0,其余段为1,所以P0口要输出0x7f(二进制0111 1111)。
B) 对于自制实验板(共阳极),数码管的选通是通过P2口控制的。要让第一个数码管显示8字,那么别的数码管的位选就要关闭,即只打开第一个数码管的位选。控制位选的P2口要输出的数据位0x08(二进制为0000 1000)。位选确定后,在确定段选,要显示的是8,那么只有dp段为1,其余段为0,所以P0口要输出0x80(二进制1000 0000)。
2)在四个数码管上显示1,2,3,4 位选:接P2口 左边四个数码管 DS3 DS2 DS1 DS0 对应的P2口:
0xf3 0xf2 0xf1 0xf0 右边四个数码管 DS3 DS2 DS1 DS0 对应的P2口:
0xf7 0xf6 0xf5 0xf4 段选:接P0口 四、参考程序 (一)mini80e实验板 1)
#include<reg52.h> void main(void) { while(1) { P2=0xf7; P0=0x7f; } } 2)
#include<reg52.h> #define uint unsigned int #define uchar unsigned char void delay(); void main(void) { while(1) { P2=0xf7; P0=0x66; delay(); P2=0xf6; P0=0x4f; delay(); P2=0xf5; P0=0x5b; delay(); P2=0xf4; P0=0x06; delay(); } } void delay() //延时程序1 { uint x,y; for(x=2;x>0;x--) for(y=112;y>0;y--) ; } (二)自制教学实验板(共阳极数码管)
(1)
#include<reg52.h> void main(void) { while(1) { P2=0x08; P0=0x80; } } 2)
#include<reg52.h> #define uint unsigned int #define uchar unsigned char void delay(); void main(void) { while(1) { P2=0x08; P0=0x99; delay(); P2=0x04; P0=0xb0; delay(); P2=0x02; P0=0xa4; delay(); P2=0x01; P0=0xf9; delay(); } } void delay() //延时程序1 { uint x,y; for(x=2;x>0;x--) for(y=112;y>0;y--) ; } 五、原理图 六、实验内容 1)用一位数码管循环显示0-9;
2)用数码管显示字母A-F;
3) 交替点亮4个数码管。(例如:开始时在数码显示器的最右边一位上显示1个“0”字,以后每隔0.5秒将“0”字左移1位,直到最左边一位后则停止显示。)
4)结合实验3,实现按键与数字的一一对应。
实验五 中断系统应用实验 一、实验目的 1.掌握外部中断技术的基本使用方法 2.掌握中断处理程序的编写方法 二、实验说明 1.外部中断的初始化设置共有三项内容:中断总允许即EA=1,外部中断允许即EXi=1(i=0或1),中断触发方式设置。中断触发方式设置一般有两种方式:电平触发方式和脉冲(边沿)触发方式,本实验选用后者,其前一次为高电平后一次为低电平时为有效中断请求。因此高电平状态和低电平状态至少维持一个周期,中断请求信号由引脚INT0(P3.2)和INT1(P3.3)引入,本实验由INT0(P3.2)引入。
2.中断控制原理:
中断控制是提供给用户使用的中断控制手段。实际上就是控制一些寄存器,51系列用于此目的的控制寄存器有四个:TCON 、IE 、SCON 及IP。
3.中断响应的过程:
首先中断采样然后中断查询最后中断响应。采样是中断处理的第一步,对于本实验的脉冲方式的中断请求,若在两个相邻周期采样先高电平后低电平则中断请求有效,IE0或IE1置“1”;
否则继续为“0”。所谓查询就是由CPU测试TCON和SCON中各标志位的状态以确定有没有中断请求发生以及是那一个中断请求。中断响应就是对中断请求的接受,是在中断查询之后进行的,当查询到有效的中断请求后就响应一次中断。
4. 8051的中断系统 8051的中断系统包括5个中断源,并提供两个优先级,允许用户对中断源进行独立控制和中断优先级设置.8051支持的5个中断源分别为外部中断0、定时器0溢出中断、外部中断1、定时器1溢出中断和串口中断。
对应的中断号为0、1、2、3、4;
寄存器有4个工作组可以切换,为0-3;C51中,中断服务程序是以中断函数的方式来时实现的。
5.中断函数格式如下:
void 函数名() interrupt 中断号 using 工作组 { 中断服务程序内容;
} 三、参考例子 1) 右边的三个数码管从“000”开始进行加法计数。按动按键时计数暂停,再按继续计数。
四、参考程序 (一)适用于自制教学实验板 #include <reg52.h> #define uchar unsigned char #define uint unsigned int sbit P37=P3^7; uchar code table[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; uchar code wei[4] = {0x08,0x04,0x02,0x01}; /***************************************************************/ uint cnt; bit flag; /**********************************************/ void init(void) { bit flag=0; //设置标志 EA=1;//开中断 EX0=1; //外部中断0开中断 IT0=1; //外部中断0的触发方式 } /**************************************************/ void delay(uint k) { uint data i,j; for(i=0;i<k;i++) { for(j=0;j<121;j++){;} } } /**************************************************/ void main(void) { uchar i; P37=0; init(); while(1) { if(flag)cnt++; if(cnt>999)cnt=0; for(i=0;i<100;i++) { P0=table[cnt/100]; P2=wei[2]; delay(1); P0=table[(cnt%100)/10]; P2=wei[1]; delay(1); P0=table[cnt%10]; P2=wei[0]; delay(1); } } } /************************************************/ void extern_int0(void) interrupt 0 using 0 { flag=!flag; } (二)适用于MINI80E实验板 #include <reg52.h> #define uchar unsigned char #define uint unsigned int sbit P37=P3^7; uchar code table[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar code wei[4] = {0xf7,0xf6,0xf5,0xf4}; /***************************************************************/ uint cnt; bit flag; /**********************************************/ void init(void) { bit flag=0; //设置标志 EA=1;//开中断 EX0=1; //外部中断0开中断 IT0=1; //外部中断0的触发方式 } /**************************************************/ void delay(uint k) { uint data i,j; for(i=0;i<k;i++) { for(j=0;j<121;j++){;} } } /**************************************************/ void main(void) { uchar i; P37=0; init(); while(1) { if(flag)cnt++; if(cnt>999)cnt=0; for(i=0;i<100;i++) { P0=table[cnt/100]; P2=wei[2]; delay(1); P0=table[(cnt%100)/10]; P2=wei[1]; delay(1); P0=table[cnt%10]; P2=wei[0]; delay(1); } } } /************************************************/ void extern_int0(void) interrupt 0 using 0 { flag=!flag; } 五、实验内容 1、使用外部中断1实现上述功能(注意C51中的不同中断号)
2、数码管从“0000”开始计数,高两位和低两位独立计数。采用两个按键,一个对应前两位的暂停和继续,另一个对应后两位的暂停和继续。
实验六 LED 数码管的动态驱动 一、实验目的 1、 学习 LED 数码管的动态驱动编程。
2、 学习使用定时/计数器。
二、实验内容 编写程序,使实验板上的4个LED数码管稳定显示4个不同的数字,并使这四位数从0000开始,每秒钟加一。
三、实验说明 在前面实验中,我们已经能够让某一个 LED 数码管显示需要的数字,比如选让第一个 LED显示“1”,隔一较短的时间(如 5 毫秒)后关闭第一个 LED,让第二个 LED 显示“2”,如此周而复始,让 4 个 LED 依次显 1、2、3、4,我们就能看到 4 个 LED 上稳定地显示 4 个不同的数字。当然,每个瞬间只有一个 LED 被点亮,大家亮的时间相同,均为 5 毫秒,4 个 LED数码管点亮一遍需要 20 毫秒,一秒钟各亮 50 次,所以看上去不会有闪烁感,但亮度只是实验四中 LED 亮度的四分之一。
要实现每隔 5 毫秒变换一个 LED,最好的方法是使用定时器中断。
四、参考程序 (一)自制实验教学板C51参考程序如下:
#include <REG51.H> #define U8 unsigned char U8 tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; U8 scn[] = {0x01,0x02,0x04,0x08};//先点亮左边一个数码管 U8 buf[4] = {0,0,0,0};// 显示缓冲区,开机先显示“0000“ U8 cnt = 0; // 扫描计数 void main(void) { EA = 1; // 允许中断 TMOD = 0x01; // 设定时器 0 为模式 1(16 位) ET0 = 1; // 定时器 0 中断允许 TH0 = 0xee; // 晶振 11.0592MHz,5mS TL0 = 0; TR0 = 1; // 开始计数 while(1); // 死循环,等待中断 } void timeint(void) interrupt 1 // 定时器 0 中断服务程序 { char i; TH0 = 0xee; // 设置定时器时间常数 TL0 = 0; i = cnt & 0x03; // 求应点亮的 LED 号(从左到右依次为 0,1,2,3) P0 = tab[buf[i]]; // 笔划代码送 P0 口 P2 = scn[i]; // 控制扫描码送 P2 口 if(cnt==200) { cnt=0; // 到 1 秒钟,显示的数字加一 for(i=3;i>=0;i--) { buf[i]++; if(buf[i]==10) buf[i]=0;// 加到 10 向前进位 else break; } } cnt++; } (二)mini80e实验板C51参考程序如下:
#include <REG51.H> #define U8 unsigned char U8 tab[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; U8 scn[] = {0xf4,0xf5,0xf6,0xf7};//先点亮左边一个数码管 U8 buf[4] = {0,0,0,0};// 显示缓冲区,开机先显示“0000“ U8 cnt = 0; // 扫描计数 void main(void) { EA = 1; // 允许中断 TMOD = 0x01; // 设定时器 0 为模式 1(16 位) ET0 = 1; // 定时器 0 中断允许 TH0 = 0xee; // 晶振 11.0592MHz,5mS TL0 = 0; TR0 = 1; // 开始计数 while(1); // 死循环,等待中断 } void timeint(void) interrupt 1 // 定时器 0 中断服务程序 { char i; TH0 = 0xee; // 设置定时器时间常数 TL0 = 0; i = cnt & 0x03; // 求应点亮的 LED 号(从左到右依次为 0,1,2,3) P0 = tab[buf[i]]; // 笔划代码送 P0 口 P2 = scn[i]; // 控制扫描码送 P2 口 if(cnt==200) { cnt=0; // 到 1 秒钟,显示的数字加一 for(i=3;i>=0;i--) { buf[i]++; if(buf[i]==10) buf[i]=0;// 加到 10 向前进位 else break; } } cnt++; } 五、实验内容 1、每个按键控制一个数码管,按任意一个键,可在相应的数码管上实现数字加1。
2、如何让 4 个 LED 数码管从 0000 开始,每秒钟自动加 1,前两位代表分,后两位代表秒? 实验七 定时器/计数器使用 一、实验目的 1.学习89C51内部定时计数器的使用和编程方法 2.进一步掌握中断处理程序的编写方法 二、实验说明 1、51单片机有。两个16位内部定时器/计数器(T/C,Timer/ Counter)。若是计数内部晶振驱动时钟,则是定时器;
若是计数8051的输入引脚的脉冲信号,则它是计数器。定时器实际上也是工作在计数方式下,只不过对固定频率的脉冲计数。由于脉冲周期固定由计数值可以计算出时间,有定时功能。
定时器有关的寄存器有工作方式寄存器TMOD和控制寄存器TCON。TMOD用于设置定时器/计数器的工作方式0-3,并确定用于定时还是用于计数。TCON主要功能是为定时器在溢出时设定标志位,并控制定时器的运行或停止等。
2、TMOD 1) M1M0工作方式控制位 2) C/T 定时器方式或计数器方式选择位 若C/T=1时, 为计数器方式; C/T = 0时, 为定时器方式。
3)GATE 定时器/计数器运行门控标志位 当GATE=1时, T/C的启动受双重控制,即要求INT0 (或INT1)引脚为高电平且TR0(或TR1 )置 1 时, 相应的T/C才被选通工作。若GATE=0, T/C的启动仅受TR0 (或 TR1)控制,即置 1, T/C就被选通, 而不管 INT0 (或 INT1)的电平是高还是低。
3、TCON TF0、TF1分别是定时器/计数器T0、 T1 的溢出中断标志位, 加法计数器计满溢出时置1, 申请中断, 在中断响应后自动复 0。TF产生的中断申请是否被接受, 还需要由中断是否开放来决定。TR1、TR0 分别是定时器 /计数器T1、 T0 的运行控制位, 通过软件置 1 后, 定时器 /计数器才开始工作, 在系统复位时被清 0。
4、初始化 1)初始化步骤 在使用51系列单片机的T/C前,应对它进行编程初始化,主要是对TCON和TMOD编程,还需要计算和装载T/C的计数初值。一般完成以下几个步骤:
(1)确定T/C的工作方式——编程TMOD寄存 (2)计算T/C中的计数初值,并装载到TH和TL;
(3)T/C在中断方式工作时,必须开CPU中断和源中断——编程IE寄存器;
(4)启动定时器/计数器——编程TCON中TR1或TR0位。
2)计数初值的计算 (1)定时器的计数初值:
在定时器方式下,T/C是对机器周期脉冲计数的,fOSC=6MHZ,一个机器周期为12/fOSC=2us,则 方式0 13位定时器最大定时间隔=213*2us=16.384ms 方式1 16位定时器最大定时间隔=216*2us=131.072ms 方式2 8位定时器最大定时间隔=28*2us=512us 若T/C工作在定时器方式1时,要求定时1ms,求计数值。如设计数初值为x,则有:
(216-x)*2us=1000us 推出x=216-500 因此,TH,TL可置65 536-500 (2)计数器的计数初值 在计数器方式下:
方式0 13位计数器的满计数值=213=8192 方式1 16位计数器的满计数值=216=65 536 方式2 8位计数器的满计数值=28=256 若T/C工作在计数器方式2时,则要求计数10个脉冲的计数初值,如设计数初值为x。则有 28-x=10 即 x= 28-10 因此,TH=TL=256-10 三、参考例子 1)设单片机晶振频率为 12 MHz,利用定时器在P1.0脚输出周期为 2 ms的方波。
分析:选用定时器 /计数器T0 作定时器, 输出为P1.0 引脚, 2 ms 的方波可由间隔 1 ms的高低电平相间而成, 因而只要每隔 1 ms对 P1.0 取反一次即可得到这个方波。
机器周期=12÷12MHz= 1 μs 1 ms内T0 需要计数N次: N= 1 ms÷1 μs = 1000 由于计数器向上计数,为得到1000个计数之后的定时器溢出,必须给定时器置初值为:65 536-1000. 2) 在P1.7端接有一个发光二极管,要求利用T/C控制,使LED亮1s,灭1s,周而复始。
分析:要求定时1s,T/C的三种工作方式都不能满足。(为什么?)对于较长时间的定时,应采用符合定时的方法。
l 使T/C0工作在定时器方式1,定时100ms,定时时间到后P1.0反相,即P1.0端输出周期200ms的方波脉冲。另设T/C1工作在计数器方式2,对T1输出的脉冲计数,当计满5次,定时1s时间到,将P1.7端方向,改变灯的状态。
l 采用6MHZ晶振,方式1的最大定时才能达到100多ms。对于100ms,机器周期为12/fOSC=2us,需要计数的次数=100*1000/2=50 000,即初值为65 536-50 000. 方式2满5次溢出中断,初值为256-5. 四、参考程序 (一)适用于自制实验教学板 1)设置周期为2ms的方波 查询方式:
#include<reg52.h> sbit P1_0=P1^0; void main() { TMOD=0x01; //T/CO工作在定时器方式1 16位定时器 TR0=1; //启动T/C0 for(;;) //主程序循环 { TH0=(65536-1000)/256; //预置计数初值 TL0=(65536-1000)%256; do { P1_0=!P1_0; //P1.0取反 TF0=0; //软件清TF0 }while(!TF0); //查询等待TF0置位 } } 中断方式:
#include<reg52.h> sbit P1_0=P1^0; void timer0(void) interrupt 1 using 1 //T/C中断服务程序入口 { TF0=0; //清楚中断标志位 P1_0=!P1_0; //P1.0取反 TH0=(65536-1000)/256; //计数初值重装载 TL0=(65536-1000)%256; } void main() { TMOD=0x01; //T/CO工作在定时器方式1 P1_0=0; TH0=(65536-1000)/256; //预置计数初值 TL0=(65536-1000)%256; EA=1; //CPU开中断 TF0=0; ET0=1; //T/C0开中断 TR0=1; //启动T/C0开始定时 do {}while(1); } 2) 灯的闪烁,时间间隔为1s(P1.0与P3.5相连,作为定时器1的输入)
#include<reg52.h> sbit P1_0=P1^0; sbit P0_7=P0^7; timer0() interrupt 1 using 1 //T/C0中断服务程序入口 { TF0=0; //清楚中断T0标志位 P1_0=!P1_0; //P1.0取反 TH0=(65536-1000)/256; //计数初值重装载 TL0=(65536-1000)%256; } timer1() interrupt 3 using 2 //T/C1中断服务程序入口 { TF1=0; //清楚中断T1标志位 P0_7=!P0_7; //1s到,灯改变状态 } void main() { P2=0x10; //打开LED电源开关 P0_7=1; //置灯初始灭 P1_0=1; //保证第一次方向便开始计数 TMOD=0x61; //T/CO工作在定时器方式1,T/C1工作在计数器方式2 TH0=(65536-10000)/256; //预置计数初值 10毫秒 TL0=(65536-10000)%256; TH1=256-100; TL1=256-100; IP=0X08; //置优先级存储器 EA=1; //CPU开中断 TF0=0; //清除中断T0标志位 TF1=0; //清除中断T1标志位 ET0=1; //T/C0开中断 ET1=1; //T/C1开中断 TR0=1; //启动T/C0开始定时 TR1=1; //启动T/C1 do{} while(1); } (二)适用于MINI80E实验板 (2)P0.7与P3.5相连,作为定时器1的输入 #include<reg52.h> sbit P1_0=P1^0; sbit P0_7=P0^7; timer0() interrupt 1 using 1 //T/C0中断服务程序入口 { TF0=0; //清除中断T0标志位 P0_7=!P0_7; //P1.0取反 TH0=(65536-1000)/256; //计数初值重装载 TL0=(65536-1000)%256; } timer1() interrupt 3 using 2 //T/C1中断服务程序入口 { TF1=0; //清除中断T1标志位 P1_0=!P1_0; //1s到,灯改变状态 } void main() { P2=0x10; //打开LED电源开关 P0_7=1; //置灯初始灭 P1_0=1; //保证第一次方向便开始计数 TMOD=0x61; //T/CO工作在定时器方式1,T/C1工作在计数器方式2 TH0=(65536-10000)/256; //预置计数初值 10毫秒 TL0=(65536-10000)%256; TH1=256-100; TL1=256-100; IP=0X08; //置优先级存储器 EA=1; //CPU开中断 TF0=0; //清除中断T0标志位 TF1=0; //清除中断T1标志位 ET0=1; //T/C0开中断 ET1=1; //T/C1开中断 TR0=1; //启动T/C0开始定时 TR1=1; //启动T/C1 do{} while(1); } 五、电路图 六、实验内容 1)利用定时/计数器T1产生定时时钟,由P1口(或P0口)控制8个发光二极管,使8个指示灯依次一个一个闪动,闪动频率为10次/秒(8个灯依次亮一遍为一个周期),循环。
实验八 数字电子钟 一、实验目的 进一步熟悉定时/计数器的使用。
二、实验内容 在实验板上编写程序,实现电子钟功能。时间显示格式为 HH.MM,中间的小数点每秒钟闪烁一次。用两个按键控制分和小时的调整,每按一次分或者小时加1。
三、实验说明 前面实验钟 LED 数码管的笔划代码均不含小数点(h 位为 1,小数点不亮),为了让第 2 个 LED 的小数点在每一秒的前半秒亮,后半秒灭,应在定时器的中断子程序中,判断当前时间为前半秒,而且动态扫描到第 2 个 LED 时,将其笔划代码取出,和 0xDF 相与后再写入 P0 口。
实验九 矩阵键盘识别实验 一、实验目的 1.掌握矩阵键盘的检测原理 二、实验说明 1、矩阵式键盘中,行、列线分别连接到按键开关的两端,行线通过上拉电阻接到+5V上。当无键按下时,行线处于高电平状态;
当有键按下时,行、列线将导通,此时,行线电平将由与此行线相连的列线电平决定。这一点是识别矩阵按键是否被按下的关键。然而,矩阵键盘中的行线、列线和多个键相连,各按键按下与否均影响该键所在行线和列线的电平,各按键间将相互影响,因此,必须将行线、列线信号配合起来作适当处理,才能确定闭合键的位置。
扫描方式是利用CPU完成其他工作的空余调用键盘扫描子程序来响应键盘输入的要求。在执行键功能程序时,CPU不再响应键输入要求,直到CPU重新扫描键盘为止。键盘扫描程序一般应包括以下内容:
(1)判别有无键按下。
(2)键盘扫描取得闭合键的行、列值。
(3)用计算法或查表法得到键值。
(4)判断闭合键是否释放,如没释放则继续等待。
(5)将闭合键键号保存,同时转去执行该闭合键的功能。
三、参考例子 1)按矩阵键盘上面的key0---key15,在第一个数码管上分别显示0-9,A-F。
四、参考程序 1)矩阵键盘(适用于MINI80E板)
#include<REG51.H> #define uchar unsigned char #define uint unsigned int uchar code DIS_SEG7[16]={0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; uchar code DIS_BIT[8]={0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0}; uchar code SKEY[16]={13,14,15,0,9,10,11,12,5,6,7,8,1,2,3,4}; uchar code act[4]={0xf7,0xfb,0xfd,0xfe}; //======================== void delay(uint k) { uint data i,j; for(i=0;i<k;i++){ for(j=0;j<121;j++) {;}} } //=========================== char scan_key(void) { uchar i,j,in,ini,inj; bit find=0; for(i=0;i<4;i++) { P3=act[i]; delay(10); in=P3; in=in>>4; in=in|0xf0; for(j=0;j<4;j++) { if(act[j]==in) {find=1; inj=j;ini=i; } } } if(find==0)return -1; return (ini*4+inj); } //============================ void main(void) { char c; uchar key_value; while(1) { c=scan_key(); if(c!=-1)key_value=SKEY[c]; P0=DIS_SEG7[key_value]; P2=DIS_BIT[0]; delay(2); } } (二)适用于自制教学实验板 #include<REG51.H> #define uchar unsigned char #define uint unsigned int uchar code DIS_SEG7[8]={0xc0,0xf9,0xa4,0xb0,0x99, 0x92,0x82,0xf8}; uchar code DIS_BIT[4]={0x08,0x04,0x02,0x01}; uchar code SKEY[8]={4,5,6,7,0,1,2,3}; uchar code act[4]={0xf7,0xfb,0xfd,0xfe}; //======================== void delay(uint k) { uint data i,j; for(i=0;i<k;i++){ for(j=0;j<121;j++) {;}} } //=========================== char scan_key(void) { uchar i,j,in,ini,inj; bit find=0; for(i=0;i<2;i++) { P3=act[i]; delay(10); in=P3; in=in>>4; in=in|0xf0; for(j=0;j<4;j++) { if(act[j]==in) {find=1; inj=j;ini=i; } } } if(find==0)return -1; return (ini*4+inj); } //============================ void main(void) { char c; uchar key_value; while(1) { c=scan_key(); if(c!=-1)key_value=SKEY[c]; P0=DIS_SEG7[key_value]; P2=DIS_BIT[0]; delay(2); } } 五、电路图 1)矩阵键盘 实验十 自动演奏乐曲 一、实验目的 (1)进一步熟悉 89C51 定时器/计数器的功能及应用;
(2)掌握其初始化与中断服务程序的编程方法;
(3)掌握用定时器/计数器产生不同频率方波的编程方法。
二、实验内容 用单片机的定时器/计数器 0 作定时器使用,工作于模式 1,中断产生方波发声,根据 简谱中各个音阶的频率,计算对应的定时时间常数,定时器中断后按此常数赋初值,从而发 出对应的音调。将歌曲的音调和节拍各编成一个表(数组),用音调作为定时器的初值,用节 拍控制发音时间, 就可以实现自动演奏乐曲。
三、实验说明 产生音阶定时初值的计算:
根据下表的音阶频率,计算对应的音阶周期 T,用 T/2 计算定时周期数(晶振11.0592MHz),填入下表中。定时器的计数初值为 65536-定时周期数,由程序计算产生。定时周期数增加 1 倍,音阶降低八度,定时周期数降低 1 倍,音阶升高八度。
音阶表的编码规则为,音阶 1-7 用 11-17 表示,高八度音阶 1-7 用 21-27 表示,低八度 音阶 1.-7.用 1-7 表示。
节拍表的编码规则为,1 拍为 16,约 570mS,1/2 拍为 8,1/4 为 4,依此类推。
下面是《康定情歌》的简谱及编码,上一行数字是音阶编码,下一行数字是节拍编码。
自动演奏《康定情歌》乐曲的 C51 程序为:
(一) 适用于mini80E板 #include <REG51.h> #define U8 unsigned char sbit SPK=P1^2; sbit P3_2=P3^2; code unsigned int cyc[]={1800,1600,1440,1351,1200,1079,960};//音阶 1-7 的半周期数 code U8 tone[]={13,15,16,16,15,16,13,12,12,13,15,16,16,15,16,13,13, 13,15,16, 16,15,16,13,12,12,15,13,12,13,12,11,12,6,6,12,15,13,12,6,6,15,13,12,13,12,11, 12,6,5,6,0xff};// 乐曲《康定情歌》的简谱表 code U8 time[]={8,8,8,4,4,8,8,12,4,8,8,8,4,4,8,16,8,8,8,8,4,4,8,8,12,4,8,8,4,4, 4,4,8,24,8,24,8,24,8,16,8,8,8,4,4,4,4,8,16,8,32};// 节拍表 U8 H0,L0,cnt; void cntint0(void) interrupt 1 // 定时器 0 中断用于产生音阶方波 { TH0=H0; TL0=L0; SPK=~SPK; // P1.2 是音乐信号输出脚,P1.2 反相,产生方波 } void cntint1(void) interrupt 3 // 定时器 1 中断用于产生节拍延时 { cnt++; // 计数初值为 0,所以不用赋值 } void main(void) { U8 i,a,t; unsigned int b; next:TMOD=0x11; EA=1; ET0=1; ET1=1; cnt=0; TR1=1; i=0; while(1) { t=tone[i]; // 读音调 if(t==0xff) break; // 0xff 是结束符 if(t!=0) // 0 是休止符 { b=cyc[t%10-1]; // 根据基本音阶,求出半周期数 if(t<10) b=b*2; // 若是低八度音阶,半周期数加倍 if(t>20) b=b/2; // 若是高八度音阶,半周期数减半 H0=(65536-b)/256; // 根据半周期数,计算 T0 初值的高字节和低字节 L0=(65536-b)%256; TR0=1; // 启动定时器 0 发音 } cnt=0; a=time[i]; // 读节拍 while(a>cnt); TR0=0; i++; for(b=0;b<1000;b++); // 稍加延时,增强节奏感 } SPK=1; // 关闭喇叭 while(P3_2==1); // 等待按 INT 键 goto next; // 重放一遍 } (二) 适用于自制实验教学板 #include <REG51.h> #define U8 unsigned char sbit P2_5=P2^5; sbit P3_2=P3^2; code unsigned int cyc[]={1800,1600,1440,1351,1200,1079,960};//音阶 1-7 的半周期数 code U8 tone[]={13,15,16,16,15,16,13,12,12,13,15,16,16,15,16,13,13, 13,15,16, 16,15,16,13,12,12,15,13,12,13,12,11,12,6,6,12,15,13,12,6,6,15,13,12,13,12,11, 12,6,5,6,0xff};// 乐曲《康定情歌》的简谱表 code U8 time[]={8,8,8,4,4,8,8,12,4,8,8,8,4,4,8,16,8,8,8,8,4,4,8,8,12,4,8,8,4,4, 4,4,8,24,8,24,8,24,8,16,8,8,8,4,4,4,4,8,16,8,32};// 节拍表 U8 H0,L0,cnt; void cntint0(void) interrupt 1 // 定时器 0 中断用于产生音阶方波 { TH0=H0; TL0=L0; P2_5=~P2_5; // P2.5 是音乐信号输出脚,P2.5 反相,产生方波 } void cntint1(void) interrupt 3 // 定时器 1 中断用于产生节拍延时 { cnt++; // 计数初值为 0,所以不用赋值 } void main(void) { U8 i,a,t; unsigned int b; next:TMOD=0x11; EA=1; ET0=1; ET1=1; cnt=0; TR1=1; i=0; while(1) { t=tone[i]; // 读音调 if(t==0xff) break; // 0xff 是结束符 if(t!=0) // 0 是休止符 { b=cyc[t%10-1]; // 根据基本音阶,求出半周期数 if(t<10) b=b*2; // 若是低八度音阶,半周期数加倍 if(t>20) b=b/2; // 若是高八度音阶,半周期数减半 H0=(65536-b)/256; // 根据半周期数,计算 T0 初值的高字节和低字节 L0=(65536-b)%256; TR0=1; // 启动定时器 0 发音 } cnt=0; a=time[i]; // 读节拍 while(a>cnt); TR0=0; i++; for(b=0;b<1000;b++); // 稍加延时,增强节奏感 } P2_5=0; // 关闭喇叭 //while(P3==0x7b); // 等待按 INT 键 //goto next; // 重放一遍 } 五、思考题 1)将程序改成演奏别的乐曲。
2)将按键设计成16(或8个)音调,随意弹奏想要表达的歌曲。
实验十一 综合实验 交通信号灯控制器的设计 一、实验目的 学习定时器的综合运用。
二、实验内容 参照实验一和实验二的电路以及实验三的程序,编写交通信号灯控制器程序,将实验板 上左边两个数码管和发光二极管分别作为南北方向的红绿灯和时间显示,右边两个数码管和 发光二极管分别作为东西方向的红绿灯和时间显示,如下图所示。要求,南北方向绿灯亮 25 秒,红灯亮 30 秒,东西方向绿灯亮 30 秒,红灯亮 25 秒。
三、实验说明 时间显示参照前面实验程序,采用动态扫描方式。
四、思考题 1、用键盘设置红绿灯亮的时间。
2、设定的时间保存在 flash 存储器中,开机后自动按设定时间运行。
实验十二 教学板自检程序设计 一、实验目的 1.通过实验掌握EEPROM使用 2. 掌握串行A/D的应用 3. 掌握单片机串行通信收发接口的使用 4.学习系统应用程序的设计和调试方法。
二、实验设备 PC机一台 、 实验教学板一块。
三、实验内容及原理 程序运行前先将TXD、RXD短接。
程序完成以下功能:
1.自检程序先将所有的数码管点亮,显示0~9,每秒变换一次。
2. 编写走马灯程序,使LED逐位显示一次 3.自检程序将EEPROM所有单元置0,并读出检查是否为0,再置EEPROM所有单元为FFH,并读出检查是否为全1。若不成功则显示器显示“— — — 1” 。
4.启动串行口进行自发自收,检查发、收的数据是否相同。不成功则显示“— — — 2” 。
5. 启动A/D,并在数码管上显示A/D转换值。
6. 等待按键,按任一键则蜂鸣器发声0.5秒一次。
四、实验要求 1.按实验题目要求设计好硬件电路,画出电路原理图。
2. 要求画出主程序、子程序、中断服务程序流程图,设计出相应程序并给程序加上较详细的注释。
3. 在Keil C51编译环境下完成程序编译。
4. 通过下载线和下载软件STC-ISP V39将编译后的程序下载到单片机。
5. 运行程序,用实验结果验证程序的正确性。若不能达到要求,分析原因、查找错误,修改源程序,再次汇编、连接,下载、运行,直至达到题目的要求。
一、 思考题 1、 若要8路A/D,在原有的电路上应作如何修改? 2、这块实验板的串行通信的速度最高能够做到多少波特率?请给出计算方法 和初始设置的程序。
实验十三 综合实验:数据采集—火灾报警装置的软硬件设计 一、实验目的 1.掌握串行A/D的应用 2. 掌握单总线器件18B20的应用 3. 掌握温度的检测方法 4.学习系统应用程序的设计和调试方法。
二、实验设备 PC机一台 、 实验教学板一块。18B20芯片一块,390欧电阻、1K电位器各一个 三、实验内容及原理 完成一个火灾数据采集系统的硬件电路的设计。编写和调试运行程序。系统主要技术指标如下:
1.系统监测1路可燃气体的浓度 注:传感器在浓度从00~100%LEL(Low Expiosion Limit,爆炸极限)输出4~20mA的信号(可用电位器替代)
2.系统监测1路温度的变化值 3.设若干位显示,设置按键进行浓度、温度显示转换。
4.系统可设置气体浓度安全值,温度变化的安全值。
5.一旦超出安全范围则进入自动报警。
四、实验要求 1.按实验题目要求设计好硬件电路,画出电路原理图。
2. 说明电路原理,设计指标,系统操作方法。
3. 要求画出主程序、子程序、中断服务程序流程图,设计出相应程序并给程序加上较详细的注释。
4. 在Keil C51编译环境下完成程序编译。
5. 通过下载线和下载软件STC-ISP V39将编译后的程序下载到单片机。
6. 运行程序,用实验结果验证程序的正确性。若不能达到要求,分析原因、查找错误,修改源程序,再次汇编、连接,重新下载、运行,直至达到题目的要求。
附录:实验教学板电路原理图 仅供参考
推荐访问:c语言上机实验指导书 c语言实验指导书16学时 c语言实验指导书答案 VB实验指导书 C实验指导书 基于汇编与C语言的单片机原理及应用-电子 基于汇编与c语言的单片机原理及应用 C++实验指导书 PLC实验指导书 电工实验指导书