单片机原理及应用实验报告
引言
单片机是一种控制芯片,一个微型的计算机,而加上晶振,存储器,地址锁存器,逻辑门,七段译码器(显示器),按钮(类似键盘),扩展芯片,接口等那是单片机系统。80C51单片机有40个引脚大致可分为4类:电源、时钟、控制和I/O引脚。1. 电源:⑴ VCC-芯片电源,接+5V;⑵ VSS-接地端;⒉ 时钟:XTAL1、XTAL2-晶体振荡电路反相输入端和输出端。⒊ 控制线:控制线共有4根,⑴ ALE/PROG:地址锁存允许/片内EPROM编程脉冲 ① ALE功能:用来锁存P0口送出的低8位地址 ② PROG功能:片内有EPROM的芯片,在EPROM编程期间,此引脚输入编程脉冲。⑵ PSEN:外ROM读选通信号。⑶ RST/VPD:复位/备用电源。① RST(Reset)功能:复位信号输入端。② VPD功能:在Vcc掉电情况下,接备用电源。 ⑷ EA/Vpp:内外ROM选择/片内EPROM编程电源。① EA功能:内外ROM选择端。② Vpp功能:片内有EPROM的芯片,在EPROM编程期间,施加编程电源Vpp。⒋ I/O线80C51共有4个8位并行I/O端口:P0、P1、P2、P3口,共32个引脚。P3口还具有第二功能,用于特殊信号输入输出和控制信号(属控制总线)。80C51的存储器组织结构可以分为三个不同的存储空间,分别是:⑴ 64KB程序存储器(ROM),包括片内ROM和片外ROM;⑵ 64KB外部数据存储器(外RAM);⑶ 256B内部数据存储器(内RAM)(包括特殊功能寄存器)。
目录
一、 KEIL 和PROTEUS 联调实验 1
二、 中断与定时实验 5
三、 输入输出实验 13
四、 ADC/DAC 21
五、通信实验 35
实验总结与感想 54
一、 KEIL 和PROTEUS 联调实验
1.1 实验要求
1. 采用延时方法,实现2kHz的方波输出,观察LED的现象。
2. 采用软件延时方法实现频率2Hz方波输出,实现LED的闪烁显示。
3. 采用LED的闪烁显示的方法,实现检测内存单元20H中高电平“1”的个数,闪烁1次表示1,闪烁2次表示2….,结束关显示2秒。循环进行。
4. 采用LED1的闪烁显示的表示“1”,LED2的闪烁表示“0”的方法,实现检测内存单元20H中的数据大小,从高位开始,结束关显示2秒。循环进行。
5. P2口驱动8个LED,设计流水灯程序,实现每0.5s移位显示。
1.2程序实现
1)
图1.1
在图1.1中,经过一定的时钟周期(晶振为12MHZ),根据一个机器周期为1us来计算延迟时间,达到延时时间信号取反,在Proteus中仿真得到如下波形:
2)
图1.2
在图1.2中,经过一定的时钟周期(晶振为12MHZ),根据一个机器周期为1us来计算延迟时间,达到延时时间信号取反,在Proteus中仿真得到如下波形:
3)
每个单元存储的数据为8位,将数据0FEH存入A后进行移位操作,通过一定的时间间隔,实现8个LED依次循环点亮,从而实现流水灯的设计;仿真结果如下:
1.3实验总结
51单片机是基础的开发工具,这节课我们主要实现了两个软件的联调以及汇编语言的编写,汇编是基础的机器语言一开始的编程不是很顺利一遍一遍的试,尤其的延迟程序虽然有单字节指令、多字节指令,根据机器周期一个一个进行计算确定延迟时间,总是有那么一点误差。软件的联调也在实验过程中实现了,查找自己代码上的错误以及指导计算机是如何运行我们所编写的程序,让我的编程能力得到了一定提升,也更加了解熟悉计算机,要继续努力不断深入下去,加油。
二、 中断与定时实验
2.1 实验要求
1. 第1次按键LED亮,第2次按键灯灭,用中断方式实现;
2. P2.0输出2kHz的方波,用中断方式实现;
3. P2.0输出1kHz占空比25%的方波,用中断方式实现;
4. 按键中断交替实现,P2.0输出2kHz与500Hz的方波,用中断方式实现;
5. 在1-2s时间内按键1次P2.0发出1kHz方波,在1-2s时间内按键连2次,P2.0发出2kHz的方波。在1-2s内连击按键3次,P2.0发出500Hz 的占空比25%的方波。
2.2 程序实现
1)
图2.1
在图2.1中,设定一个外部中断0,实时监听P2.0口,一旦检测到按键按下,参数A取反,对应输出相应的高低电平。一开始灯不亮,当按键按下时灯亮,再按一下灯又熄灭。。。。。。如此反复。在Proteus上仿真得到如下情况:
图2.2
2)
图2.3
在图2.3中,根据一定的时钟周期(晶振为12MHZ),利用定时器1来进行定时,定时器1每间隔0.25ms对P2_0取反一次,如此循环得到频率为2kHz的方波,在Proteus上仿真得到如下情况:
图2.4
3)
图2.5
在图2.5中,要获得一个1kHz、占空比为25%的方波,只要在0.25ms时电平置低,之后再经过0.75ms,电平再拉高,由此实现1kHz、占空比25%方波的输出,在Proteus上仿真得到如下情况:
4)
图2.7
在图2.7中,设定一个外部中断0,实时监听INT0口,一旦检测到按键按下,参数A取反。对于参数A的不同取值,通过定时器1分别输出2kHz与500Hz。在Proteus上仿真得到如下情况:
图2.8
5)
图2.9
在图2.9中,通过中断函数ext0(),在一定时间内按键按下几次,参数A取相应的值。再通过定时器1,通过判断参数A的取值,P2.0口输出相应的方波。当按键次数A=1时实现P2.0口输出1kHz方波,当按键次数A=2时实现P2.0口输出2kHz方波,当按键次数A=3时实现P2.0口输出500Hz占空比25%的方波。编译无误后进行Proteus仿真:
图2.10 P2.0口输出1kHz方波
图2.11 P2.0口输出2kHz方波
图2.12 P2.0口输出500Hz占空比25%的方波
在图2.10、2.11、2.12中,通过按键次数的增加,实现实验所需要求的功能,顺利完成实验。
2.3实验总结
在做实验之前,我复习了中断的概念,包括外部中断、定时器中断,了解到51单片机内部资源丰富,能实现现阶段很多想要实现的功能。感觉学了这次实验的内容,我可以动手做很多跟时间有关的小作品,在实验课上,我对代码进行优化,因为一开始对定时器所给的初值与我们所需要方波的延迟周期有一定的误差,需要不断调试来产生的方波更加优美。
三、 输入输出实验
3.1实验要求
1) 设计电路图
由P2口控制显示器位码,P1口控制显示器段码,设计6位的LED显示器,采用动态显示。也可以采用静态显示。
用P1或P3口设计一键盘,可以是16个按键的行列按键,也可以是8个独立按键。
2) 程序设计
1. 实现显示123456
2. 时间显示,显示格式 XXXX.XX (s)
3. 实现秒表功能,按一键启动秒表,再按一次该按键停止计时,显示格式 MM.SS.XX.
4. 实现倒计时功能,按键设定计时的时间,启动按键计时,中间可以按键暂停计时,时间到后闪烁显示屏,显示格式 MM.SS.XX.
5. 实现时钟功能,可按键修改初始时间,显示格式 HH.MM.SS
要求采用按功能键实现功能2~5的切换。
3.2程序实现
1) 实现显示123456。
图3.1
在图3.1中,采用动态扫描的方式,通过函数DigDisplay()实现数码管位选与段选数据对应输出,经过扫描和人眼视觉残留实现6个数码管显示123456。在Proteus中仿真得到如下显示:
2) 按功能键实现功能2~5的切换,综合程序。
#include<reg51.h>
#define GPIO_DIG P1 //段选
#define GPIO_PLACE P2 //位选
#define uchar unsigned char
#define uint unsigned int
sbit key=P3^0;
sbit t3=P3^5;
sbit t2=P3^6;
sbit t1=P3^7;
uint disnum1=0,disnum2=0,disnum3=0,K,T=0,power=0;
unsigned char code DIG_PLACE[6] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf}; //位码
unsigned char code table[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//段码0~9
void display(uint liu,uint wu,uint si,uint san,uint er,uint yi);
void timing();
void delay(uint a)
{
unsigned int i,j;
for(j=a;j>0;j--)
for(i=0;i<500;i++);
}
void main()
{
TMOD = 0X01;
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
EA = 1;
ET0 = 1;
TR0 = 1;
IT0=1;
EX0=1;
IT1=1;
EX1=1;
while(1)
{
timing();
display(disnum3/10,disnum3%10,disnum2/10,disnum2%10,disnum1/10,disnum1%10);
}
}
void time1() interrupt 1
{
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
if(power==0) //功能2
{
disnum1++;
if(disnum1==100)
{
disnum1=0;
disnum2++;
if(disnum2==60)
{
disnum2=0;
disnum3++;
if(disnum3==60)
{
disnum3=0;
}
}
}
}
if(power==1) //功能3
{
if(K!=0)
{
disnum1++;
if(disnum1==100)
{
disnum1=0;
disnum2++;
if(disnum2==60)
{
disnum2=0;
disnum3++;
if(disnum3==60)
{
disnum3=0;
}
}
}
}
}
if(power==2) //功能4
{
if(K!=0)
{
if(disnum1==0&&(disnum3!=0||disnum2!=0))
{
disnum1=100;
if(disnum2==0&&disnum3!=0)
{
disnum2=60;
disnum3--;
}
disnum2--;
}
else
if(disnum1!=0||disnum3!=0||disnum2!=0)
disnum1--;
}
}
if(power==3) //功能5
{
T++;
if(T==100)
{
T=0;
if(K!=0)
{
disnum1++;
if(disnum1==60)
{
disnum1=0;
disnum2++;
if(disnum2==60)
{
disnum2=0;
disnum3++;
if(disnum3==60)
{
disnum3=0;
}
}
}
}
}
}
}
void int0(void) interrupt 0 //启动/暂停键
{
K = ~K;
}
void int1(void) interrupt 2 //功能选择键
{
power++;
if(power==4)
{
power=0;
}
disnum1=0;disnum2=0;disnum3=0;K=0;T=0;
}
void display(uint liu,uint wu,uint si,uint san,uint er,uint yi)
{
GPIO_PLACE=~DIG_PLACE[0];
GPIO_DIG=table[liu];
delay(1);
GPIO_PLACE=~DIG_PLACE[1];
GPIO_DIG=table[wu]+0X80;
delay(1);
GPIO_PLACE=~DIG_PLACE[2];
GPIO_DIG=table[si];
delay(1);
GPIO_PLACE=~DIG_PLACE[3];
GPIO_DIG=table[san]+0X80;
delay(1);
GPIO_PLACE=~DIG_PLACE[4];
GPIO_DIG=table[er];
delay(1);
GPIO_PLACE=~DIG_PLACE[5];
GPIO_DIG=table[yi];
delay(1);
}
void timing()
{
if(t3==0)
{
delay(40);
if(t3==0)
{
disnum3++;
if(disnum3==60)
disnum3=0;
}
}
if(t2==0)
{
delay(40);
if(t2==0)
{
disnum2++;
if(disnum2==60)
disnum2=0;
}
}
if(t1==0)
{
delay(40);
if(t1==0)
{
disnum1++;
if(disnum1==100)
disnum3=0;
}
}
}
根据Proteus中所设计的的电路图,功能实现为:
将8个按键从上到下依次命名为key1~key8.key4为功能切换键,每按下一次切换一个功能;key3为启动/暂停键,用于秒表的启动/暂停控制;key6、key7、key8为时间调整键,每按一次加一。
功能2:通过定时器计时,每隔10ms,disnum1加一;当disnum1加到100时,disnum1置零,disnum2加一;当disnum2加到60时,disnum2置零,disnum3加一。运用动态扫描的方式在数码管上依次显示disnum3、disnum2、disnum1。
功能3:与功能2类似,增加了K取值零与非零的判断。通过判断K的取值实现秒表的启动/暂停功能。
功能4:首先设定MM.SS.XX的时间,通过启动/暂停键控制启动。设定刚好与秒表功能相反。通过定时器计时,每隔10ms,disnum1减一;当disnum1减到0时,disnum1置100,disnum2减一;当disnum2减到0时,disnum2置60,disnum3减一。运用动态扫描的方式在数码管上依次显示disnum3、disnum2、disnum1。
功能5:与秒表的功能实现方式相同,只是在时间上做了调整。
3.3实验总结
此次实验在上次中断实验基础上进行,实现时钟、秒表、倒计时等功能的实现,在实验的过程中遇到了一个麻烦就是实现倒计时时,每位之间借位时,出现了显示错误,通过在课堂上的测试与调整,成功克服了这一问题。此次实验学到了很多新知识,过程挺开心的,同时实验也取得了成功。
四、 ADC/DAC
4.1实验要求
1. 编制ADC转换程序,将转换的数字直接显示在显示器上;
2. 编制DAC转换三角波程序,输出频率在10-200Hz;
3. 可按键设定ADC转换程序,量程最大值限制在50-99之间,前面两位为提示符;
4. 编制电位器调节量程程序,实现2路调节电位器最小到-最大,显示25-99,前面两位为提示符;
5. 编制程序用电位器控制三角波的输出幅度及频率。一路控制幅度在25-99,另一路频率在25-99Hz。
4.2程序实现
1)
#include "reg52.h"
#include<intrins.h>
typedef unsigned int uint;
typedef unsigned char uchar;
sbit LSA=P2^0;
sbit LSB=P2^1;
sbit LSC=P2^2;
sbit CLK=P1^4;
sbit ST=P1^2;
sbit EOC=P1^1;
sbit OE=P1^3;
sbit ALE=P1^0;
sbit ADDA=P1^5;
uchar code table[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
uchar ADdisplayflag=0;
uint adcount=0;
uint dpcount=0;
uint dd=0;
uchar zhuanhuanflag=0;
void CLKInit(void)
{
ET0 = 1;
TR0 = 1;
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
}
void ScanInit(void)
{
ET1 = 1;
TR1 = 1;
TH1 = 0x06;
TL1 = 0x06;
}
void delay(uint i)
{
while(i--);
}
void ADdisplay()
{
switch(ADdisplayflag)
{
case 0:
{
P0 = 0x00;
LSA=1;LSB=1;LSC=0;
}break;
case 1:
{
P0 = 0x00;
P0 = table[dd/100];
LSA=0;LSB=1;LSC=0;
}break;
case 2:
{
P0 = 0x00;
P0 = table[dd%100/10];
LSA=1;LSB=0;LSC=0;
}break;
case 3:
{
P0 = 0x00;
P0 = table[dd%10];
LSA=0;LSB=0;LSC=0;
}break;
default : break;
}
ADdisplayflag++;
if(ADdisplayflag == 4)
{
ADdisplayflag = 0;
}
}
void main()
{
ADDA=0;
EA = 1;
TMOD = 0x21;
ScanInit();
CLKInit();
while(1);
}
void CLK50us(void) interrupt 1
{
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
CLK=!CLK;
}
void ScanLed(void) interrupt 3
{
dpcount++;
if(dpcount==4)
{
dpcount=0;
ADdisplay();
}
adcount++;
if(adcount==40)
{
adcount = 0;
ALE=1;
ST=1;
ALE=0;
ST=0;
_nop_();
_nop_();
zhuanhuanflag=1;
}
if(zhuanhuanflag==1)
{
if(EOC==1)
{
OE=1;
dd=P3;
OE=0;
zhuanhuanflag=0;
}
}
}
在程序中,题目要求需要将电压信号转换成数字并用数码管显示出来,用八位进行采集,采集到的信号转换成数字后的范围为0~255,之后需要用数码管显示,这里还是采用动态扫描显示,显示方式与上一次实验一致,仿真结果如下:
2)
#include <reg52.h>
void delay(int i)
{
while(i--);
}
void main()
{
P0 = 0;
while(1)
{
while(1)
{
if(P0!=0xFF)
{
P0++;
delay(1);
}
else
break;
}
while(1)
{
if(P0!=0x00)
{
P0--;
delay(1);
}
else
break;
}
}
}
P0从0依次加到255,再从255依次减到0,如此循环反复实现了三角波的输出,其中三角波的频率由延时函数delay()来控制,用protuse进行仿真得到如下的仿真波形:
3)
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include<intrins.h>
typedef unsigned int uint; //对数据类型进行声明定义
typedef unsigned char uchar;
sbit LSA=P2^0;
sbit LSB=P2^1;
sbit LSC=P2^2;
sbit CLK=P1^4;
sbit ST=P1^2;
sbit EOC=P1^1;
sbit OE=P1^3;
sbit ALE=P1^0;
sbit k1 = P2^3;
sbit k2 = P2^4;
sbit k3 = P2^5;
sbit k4 = P2^6;
sbit k5 = P2^7;
uchar code table[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
uint adcount=0,dpcount=0,dd=0,ddcopy=0,dchange=0,dchangecopy=0,shanshuoflag=0;
uchar ADdisplayflag=0,zhuanhuanflag=0,k4flag=0,moduleFlag=0,lc=0,rc=0,ledchoose=0,max=0,min=0,weishu=0;
void CLKInit(void)
{
ET0 = 1;
TR0 = 1;
TH0 = 0xF0;
TL0 = 0xF0;
}
void ScanInit(void)
{
ET1 = 1;
TR1 = 1;
TH1 = 0x06;
TL1 = 0x06;
}
void delay(uint i)
{
while(i--);
}
void ADdisplaya(uchar ledmie)
{
switch(ADdisplayflag)
{
case 0:
{
P0 = 0x00;
if(ledmie==1)
{
P0 = 0x00;
}
else P0 = table[10];
LSA=1;LSB=1;LSC=0;
}break;
case 1:
{
P0 = 0x00;
if(ledmie==2)
{
P0 = 0x00;
}
else P0 = table[13];
LSA=0;LSB=1;LSC=0;
}break;
case 2:
{
P0 = 0x00;
if(ledmie==3)
{
P0 = 0x00;
}
else P0 = table[dchange/10];
LSA=1;LSB=0;LSC=0;
}break;
case 3:
{
P0 = 0x00;
if(ledmie==4)
{
P0 = 0x00;
}
else P0 = table[dchange%10];
LSA=0;LSB=0;LSC=0;
}break;
default : break;
}
ADdisplayflag++;
if(ADdisplayflag == 4)
{
ADdisplayflag = 0;
}
}
void ADdisplayb(uchar ledmie)
{
switch(ADdisplayflag)
{
case 0:
{
P0 = 0x00;
if(ledmie==1)
{
P0 = 0x00;
}
else P0 = table[10];
LSA=1;LSB=1;LSC=0;
}break;
case 1:
{
P0 = 0x00;
if(ledmie==2)
{
P0 = 0x00;
}
else P0 = table[13];
LSA=0;LSB=1;LSC=0;
}break;
case 2:
{
P0 = 0x00;
if(ledmie==3)
{
P0 = 0x00;
}
else P0 = table[dchangecopy/10];
LSA=1;LSB=0;LSC=0;
}break;
case 3:
{
P0 = 0x00;
if(ledmie==4)
{
P0 = 0x00;
}
else P0 = table[dchangecopy%10];
LSA=0;LSB=0;LSC=0;
}break;
default : break;
}
ADdisplayflag++;
if(ADdisplayflag == 4)
{
ADdisplayflag = 0;
}
}
void adc0809()
{
ALE=1;
ST=1;
ALE=0;
ST=0;
_nop_();
_nop_();
while(EOC==0);
OE=1;
dd=P3;
OE=0;
}
void keypress(void)
{
if(k4==0)
{
delay(1000);
if(k4==0)
{
k4flag=~k4flag;
k4flag&=0x01;
if(k4flag==0)
{
moduleFlag = 0;
weishu=3;
}
else if(k4flag==1)
{
moduleFlag = 1;
if(dchange<50||dchange>99)
{
dchangecopy=50;
}
else dchangecopy=dchange;
}
while(!k4);
}
}
if(moduleFlag == 1)
{
if(k1==0)
{
delay(1000);
if(k1==0)
{
if(ledchoose%2)
{
weishu=3;
}
else
{
weishu=4;
}
ledchoose++;
}
while(!k1);
}
if(k2==0)
{
delay(1000);
if(k2==0)
{
if(ledchoose%2)
{
if(dchangecopy>=50&&dchangecopy<99)
{
dchangecopy=dchangecopy+1;
}
}else
{
if(dchangecopy>=50&&dchangecopy<90)
{
dchangecopy=dchangecopy+10;
}
}
}
while(!k2);
}
if(k3==0)
{
delay(1000);
if(k3==0)
{
if(ledchoose%2)
{
if(dchangecopy>50&&dchangecopy<=99)
{
dchangecopy=dchangecopy-1;
}
}else
{
if(dchangecopy>=60&&dchangecopy<=99)
{
dchangecopy=dchangecopy-10;
}
}
}
while(!k3);
}
if(k5==0)
{
delay(1000);
if(k5==0)
{
max=dchangecopy;
moduleFlag=0;
k4flag=0;
}
while(!k5);
}
}
}
void main()
{
EA = 1;
TMOD = 0x22;
ScanInit();
CLKInit();
max=99;
min=0;
weishu=3;
while(1)
{
keypress();
}
}
void CLK500us(void) interrupt 1
{
CLK=~CLK;
}
void ScanLed(void) interrupt 3
{
dpcount++;
adcount++;
shanshuoflag++;
if(moduleFlag==0)
{
if(dpcount==4)
{
dpcount=0;
ADdisplaya(0);
}
}else if(moduleFlag==1)
{
if(dpcount>4||shanshuoflag>3000)
{
dpcount=0;
shanshuoflag=0;
}
if(dpcount==4)
{
dpcount = 0;
if(shanshuoflag<1000)
ADdisplayb(0);
else if(shanshuoflag>=1000&&shanshuoflag<=3000)
{
ADdisplayb(weishu);
if(shanshuoflag==3000)
shanshuoflag=0;
}
}
}
if(adcount==40)
{
adcount = 0;
ALE=1;
ST=1;
ALE=0;
ST=0;
_nop_();
_nop_();
zhuanhuanflag=1;
}
if(zhuanhuanflag==1)
{
if(EOC==1)
{
OE=1;
dd=P3;
OE=0;
dchange=min+((max-min)*(dd+1))/256;
zhuanhuanflag=0;
}
}
}//全局时钟变量计时
采集到的信号转换成数字后的范围为0~255,现如今要将其用(0~50)~(0~99)来表示,故在此需要将所采集的数据进行转换,这里只需要用到简单的数学公式就可以完成。转换好之后需要用数码管显示,这里还是采用动态显示。仿真结果如下:
4) 在程序3)的基础上,增加模拟电压输入端的数据选择即可,按键只运用一个数据选择切换键。仿真结果如下:
4.3实验总结
对于本次实验,个人挺难的,课下花费了好长时间才勉强完成。数码管显示数据已经在之前实验中做过了,数据的采集与转换是本次实验的难点,经过多次测试才克服这一难题。在本次实验中收获颇多。
五、通信实验
5.1实验要求
1) 设计电路图
要求采用2个单片机,左边单片机控制2路模拟电压的采集和1个18B20温度传感器的采集,右边的单片机控制4位LED数码管的显示,6位按键控制,DAC输出,2个单片机通过串口通信。
2) 实现功能
1、 按键F1,实现数字时钟功能。可以设定起始时间,显示格式 时:分 (HH.MM)
2、 按键F2,实现显示温度值,显示格式 t XX.X
3、 按键F3, 实现显示第1路ADC 显示格式 F XXX, 实现显示第2路ADC 显示格式 L xxx
4、 按键F4, 实现时钟与温度交替显示 ,交替间隔10秒。切换时闪烁2秒。
5、 由2路ADC控制DAC输出三角波
5.2程序实现
1) 数字时钟功能
#include<reg51.h>
#define GPIO_DIG P1 //段选
#define GPIO_PLACE P2 //位选
#define uchar unsigned char
#define uint unsigned int
sbit key=P3^0;
uint disnum1=0,disnum2=0,disnum3=0,K;
unsigned char code DIG_PLACE[6] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
unsigned char code table[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void display(uint si,uint san,uint er,uint yi);
void delay(uint a)
{
unsigned int i,j;
for(j=a;j>0;j--)
for(i=0;i<500;i++);
}
void main()
{
TMOD = 0X01;
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
EA = 1;
ET0 = 1;
TR0 = 1;
IT0=1;
EX0=1;
while(1)
{
display(disnum3/10,disnum3%10,disnum2/10,disnum2%10);
}
}
void time1() interrupt 1
{
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
if(K!=0){
disnum1++;
if(disnum1==100)
{
disnum1=0;
disnum2++;
if(disnum2==60)
{
disnum2=0;
disnum3++;
if(disnum3==60)
{
disnum3=0;
}
}
}
}
}
void int0(void) interrupt 0
{
K = ~K;
}
void display(uint si,uint san,uint er,uint yi)
{
GPIO_PLACE=~DIG_PLACE[2];
GPIO_DIG=table[si];
delay(1);
GPIO_PLACE=~DIG_PLACE[3];
GPIO_DIG=table[san]+0X80;
delay(1);
GPIO_PLACE=~DIG_PLACE[4];
GPIO_DIG=table[er];
delay(1);
GPIO_PLACE=~DIG_PLACE[5];
GPIO_DIG=table[yi];
delay(1);
}
通过定时器计时,每隔10ms,disnum1加一;当disnum1加到100时,disnum1置零,disnum2加一;当disnum2加到60时,disnum2置零,disnum3加一。运用动态扫描的方式在数码管上依次显示disnum3、disnum2。仿真结果如下:
2) 温度采集显示
a) 温度采集与数据发送程序
#include<reg51.h>
#include<intrins.h>
#include <absacc.h>
#include <math.h>
typedef unsigned int uint;
typedef unsigned char uchar;
sbit CLK=P1^4;
sbit ST=P1^2;
sbit EOC=P1^1;
sbit OE=P1^3;
sbit ALE=P1^0;
sbit DQ=P1^0;
unsigned char TL;
unsigned char TH;
unsigned char TN;
unsigned int TD;
unsigned char time_DS18B20;
uint adcount=0;
uint dpcount=0;
uint dd=0;
uchar zhuanhuanflag=0;
void UsartConfiguration();
unsigned char ReadOneChar();
void WriteOneChar(unsigned char dat);
void ReadyReadTemp(void);
bit Init_DS18B20(void);
void CLKInit(void)
{
ET0 = 1;
TR0 = 1;
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
}
void CLK50us(void) interrupt 1
{
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
CLK=!CLK;
}
void ScanLed(void) interrupt 2
{
adcount++;
if(adcount==40)
{
adcount = 0;
ALE=1;
ST=1;
ALE=0;
ST=0;
_nop_();
_nop_();
zhuanhuanflag=1;
}
if(zhuanhuanflag==1)
{
if(EOC==1)
{
OE=1;
dd=P2;
OE=0;
zhuanhuanflag=0;
}
}
}
void UsartConfiguration()
{
SCON=0X40; //设置为工作方式1
TMOD=0X20; //设置计数器工作方式2
PCON=0X00; //波特率不加倍
TL1=0XE8; //计数器初始值设置,注意波特率是2400的
TH1=0XE8;
ET1=1;
TR1=1; //打开计数器
EA=1; //打开总中断
ES=1; //打开接收中断
}
void ReadyReadTemp(void)
{
Init_DS18B20();
WriteOneChar(0xCC);
WriteOneChar(0x44);
for(time_DS18B20=0;time_DS18B20<100;time_DS18B20++);
Init_DS18B20();
WriteOneChar(0xCC);
WriteOneChar(0xBE);
}
bit Init_DS18B20(void)
{
bit flag_DS18B20;
DQ = 1;
for(time_DS18B20=0;time_DS18B20<2;time_DS18B20++);
DQ = 0;
for(time_DS18B20=0;time_DS18B20<200;time_DS18B20++);
DQ = 1;
for(time_DS18B20=0;time_DS18B20<10;time_DS18B20++);
flag_DS18B20=DQ;
for(time_DS18B20=0;time_DS18B20<200;time_DS18B20++);
return (flag_DS18B20);
}
unsigned char ReadOneChar( )
{
unsigned char i=0;
unsigned char dat;
for (i=0;i<8;i++)
{
DQ =1;
_nop_();
DQ = 0;
dat>>=1;
_nop_();
DQ = 1;
for(time_DS18B20=0;time_DS18B20<3;time_DS18B20++);
if(DQ==1)
dat|=0x80;
else
dat|=0x00;
for(time_DS18B20=0;time_DS18B20<8;time_DS18B20++)
;
}
return(dat);
}
void WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=0; i<8; i++)
{
DQ =1;
_nop_();
DQ=0;
DQ=dat&0x01;
for(time_DS18B20=0;time_DS18B20<10;time_DS18B20++);
DQ=1;
for(time_DS18B20=0;time_DS18B20<1;time_DS18B20++);
dat>>=1;
}
for(time_DS18B20=0;time_DS18B20<4;time_DS18B20++);
}
void main(void)
{
UsartConfiguration();
CLKInit();
while(1)
{
ReadyReadTemp();
TL=ReadOneChar();
TH=ReadOneChar();
if(TH>=8)
{
TH=~TH;
TL=~TL;
TL=TL+1;
if(TL==0)
TH+=1;
TN=TH*16+TL/16;
TD=(TL%16)*63;
dd=TN*10+TD;
}
else
{
TN=TH*16+TL/16;
TD=(TL%16)*62;
dd=TN*10+TD;
}
TI=0;
SBUF=dd;
while(!TI);
}
}
b) 数据接收与数码管显示程序
#include "reg52.h"
#include<intrins.h>
typedef unsigned int uint;
typedef unsigned char uchar;
sbit LSA=P2^0;
sbit LSB=P2^1;
sbit LSC=P2^2;
uint dd=0;
uchar code table[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
uchar ADdisplayflag=0;
uint CLK=0;
void ADdisplay();
void CLKInit(void);
void UsartConfiguration();
void main()
{
UsartConfiguration();
CLKInit();
while(1)
{
RI=0;
dd=SBUF;
while(!RI)
ADdisplay();
}
}
void CLKInit(void)
{
ET0 = 1;
TR0 = 1;
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
}
void ADdisplay()
{
switch(ADdisplayflag)
{
case 0:
{
P0 = 0x00;
LSA=1;LSB=1;LSC=0;
}break;
case 1:
{
P0 = 0x00;
P0 = table[dd/100];
LSA=0;LSB=1;LSC=0;
}break;
case 2:
{
P0 = 0x00;
P0 = table[dd%100/10]+0x80;
LSA=1;LSB=0;LSC=0;
}break;
case 3:
{
P0 = 0x00;
P0 = table[dd%10];
LSA=0;LSB=0;LSC=0;
}break;
default : break;
}
ADdisplayflag++;
if(ADdisplayflag == 4)
{
ADdisplayflag = 0;
}
}
void CLK50us(void) interrupt 1
{
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
CLK=!CLK;
}
void UsartConfiguration()
{
SCON=0X50; //设置为工作方式1
TMOD=0X20; //设置计数器工作方式2
PCON=0X00; //波特率不加倍
TL1=0XE8; //计数器初始值设置,注意波特率是2400的
TH1=0XE8;
TR1=1; //打开计数器
EA=1; //打开总中断
ES=1; //打开接收中断
}
左边的单片机通过P1.0口对温度传感器DS18B20进行温度数据采集,然后通过串口通信原理将采集到的数据发送到右端单片机;右端单片机接收到数据后,将数据进行一定的数学处理,然后通过数码管动态扫描的原理将数据显示在数码管上。仿真结果如下:
5.3实验总结
本次实验较为困难,在课前已经花了很长时间去理解串口通信,等到了实验课上的时候,依然比较模糊。之后经过和同学的讨论,把串口通信弄明白了。但是又卡在了温度传感器DS18B20上,经过上网查找资料,最终才勉强做出温度的采集显示。
实验总结与感想
首先,我对本次实验课的成功倍感珍惜。在实验中,我很深刻的了解到了理论和实践的不同之处,在书本中学到、理解的知识内容并不会很容易的运用在实际当中。有很多没有遇到过的实际问题会出现,这时就需要我们去深入探究,从而将问题一点点的解决,最后问题才能迎刃而解。在这次实验中80C51单片机的编程有了更深入的了解,对单片机制作小作品方面的知识也更加的丰富。
同时也花了较多的时间对以前所学的东西进行了温故,在以前的学习中没有具体掌握的知识,又进行了学习,巩固。如硬件设计是很重要的,硬件设计的方式方法很多,并不是一尘不变的,它反映了你的逻辑思维和创新能力,所以我觉得它才是一个设计的灵魂所在。
在实验过程中,我碰到了很多问题,在同学、老师的帮助下,才能及时顺利地解决,让我感受到集体、友谊的力量。
通过这学期的实验不但让我了解了更多电子方面的知识,同时也提高了自己的编程的能力。可以说这学期的实验不仅体现掌握的知识,而且是学习更多知识的机会,提高自己各方面的知识。可以在今后的学习中,充分发挥自己所学的知识。本次实验中,我学会什么叫坚持不懈、努力奋斗,在今后的学习、工作中一定要将在这次设计学到的品质坚持下去。同时,对给过我帮助的老师和同学再次表示由衷的感谢!
|