六.按键中断识别
[实验任务]
采用中断技术,每按一下按键,计数器加1,并用LED显示出来。
[硬件电路]
注意:我们只用4位数码管中的两位。
注意:a接P0.0;b接P0.1;c接P0.3……
注意: 2H接P2.0; 1H接P2.1; 中断按键已经接好。
[C语言源程序]
#include<reg52.h>
unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,
0x82,0xf8,0x80,0x90};
unsigned char dispcount=0; //计数
sbit gewei=P2^0; //个位选通定义
sbit shiwei=P2^1; //十位选通定义
void Delay(unsigned int tc) //延时程序
{ while( tc != 0 )
{
unsigned int i;
for(i=0; i<100; i++);
tc--;
}
}
void ExtInt0() interrupt 0 //中断服务程序
{
dispcount++; //每按一次中断按键,计数加一
if (dispcount==100) //计数范围0-99
{dispcount=0;}
}
void LED( ) //LED显示函数
{
if(dispcount>=10) //显示两位数
{
shiwei=0;
P0=table[dispcount/10];
Delay(8);
shiwei=1;
gewei=0;
P0=table[dispcount%10];
Delay(5);
gewei=1;
}
else //显示一位数
{
shiwei=1;
gewei=0;
P0=table[dispcount];
Delay(8);
}
}
void main()
{ TCON=0x01; //中断设置
IE=0x81;
while(1) //循环执行
{
LED(); //只须调用显示函数
}
}
-------------------------------------------------------------------------------------------------------------------------------------
七.定时器 T0 的应用---9.9 秒计时设计
[实验任务]
开始时,显示“00”,第 1 次按下 BY1 后就开始计时。第 2 次按 BY1 后,计时
停止。 第3次按BY1后,计时归零。
[硬件电路]
[C语言源程序]
#include <AT89X51.H>//必须用这个子函数库
unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,
0x82,0xf8,0x80,0x90};
unsigned char sec; //定义计数值,每过1/10 秒,sec 加一
unsigned char keycnt=0;
unsigned int tcnt; //键值判断
sbit gewei=P2^0; //个位选通定义
sbit shiwei=P2^1; //十位选通定义
void Delay(unsigned int tc) //延时程序
{
while( tc != 0 )
{
unsigned int i;
for(i=0; i<100; i++);
tc--;
}
}
void LED() //LED显示函数
{
shiwei=0;
P0=table[sec/10];
Delay(8);
shiwei=1;
gewei=0;
P0=table[sec%10];
Delay(5);
gewei=1;
}
void KEY() //按键扫描程序
{
unsigned char i,j;
if(P3_0==0)
{
for(i=20;i>0;i--) //延时去干扰
for(j=248;j>0;j--);
if(P3_0==0)
{
keycnt++;
switch(keycnt) //按下次数判断
{
case 1: //第一次按下
TH0=0x06; //对TH0 TL0 赋值
TL0=0x06;
TR0=1; //开始定时
break;
case 2: //第二次按下
TR0=0; //定时结束
break;
case 3: //第三次按下
keycnt=0; //重新开始判断键值
sec=0; //计数重新从零开始
break;
}
while(P3_0==0);
} } } //请注意写程序时的格式规范,此处是为了节省纸张
void t0(void) interrupt 1 using 0 //定时中断服务函数
{
tcnt++; //每过250ust tcnt 加一
if(tcnt==400) //计满400 次(1/10 秒)时
{
tcnt=0; //重新再计
sec++;
if(sec==100) //定时10 秒,在从零开始计时
{
sec=0;
}
}
}
void main(void)
{
TMOD=0x02; //定时器工作在方式2
ET0=1;
EA=1;
sec=0;
while(1)
{
KEY();
LED();
}
}
---------------------------------------------------------------------------------------------------------
八.利用定时器产生乐曲
[实验任务]
利用单片机的I/O口演奏乐曲。
[硬件电路图]
[实验原理]
乐曲是按照一定的高低,长短和强弱关系组成的关系,在一首乐曲中,每一个音符与频率有关。所以我们只要把有关频率的占空比数据做成表格,在通过查表,在I/O口输出相关乐曲的方波,便产生了乐曲。
注意:无源蜂鸣器与有源蜂鸣器的区别可以同时学习一下
[C语言源程序]
#include "reg52.h"//这样用双引号也可以。
unsigned char Count;
sbit _Speak =P0^0 ; //讯响器控制脚
unsigned char code SONG[] ={ //祝你平安
0x26,0x20,0x20,0x20,0x20,0x20,0x26,0x10,0x20,0x10,0x20,0x80,0x26,0x20,0x30,0x20,
0x30,0x20,0x39,0x10,0x30,0x10,0x30,0x80,0x26,0x20,0x20,0x20,0x20,0x20,0x1c,0x20,
0x20,0x80,0x2b,0x20,0x26,0x20,0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x80,0x26,0x20,
0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,0x26,0x60,0x40,0x10,0x39,0x10,0x26,0x20,
0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,0x26,0x80,0x26,0x20,0x2b,0x10,0x2b,0x10,
0x2b,0x20,0x30,0x10,0x39,0x10,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x20,
0x20,0x10,0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,0x18,0x20,0x18,0x20,0x26,0x20,
0x20,0x20,0x20,0x40,0x26,0x20,0x2b,0x20,0x30,0x20,0x30,0x20,0x1c,0x20,0x20,0x20,
0x20,0x80,0x1c,0x20,0x1c,0x20,0x1c,0x20,0x30,0x20,0x30,0x60,0x39,0x10,0x30,0x10,
0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x10,0x26,0x10,0x26,0x10,0x2b,0x10,0x2b,0x80,
0x18,0x20,0x18,0x20,0x26,0x20,0x20,0x20,0x20,0x60,0x26,0x10,0x2b,0x20,0x30,0x20,
0x30,0x20,0x1c,0x20,0x20,0x20,0x20,0x80,0x26,0x20,0x30,0x10,0x30,0x10,0x30,0x20,
0x39,0x20,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x10,0x40,0x10,0x20,0x10,
0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,0x00,
//路边的野华不要采
0x30,0x1C,0x10,0x20,0x40,0x1C,0x10,0x18,0x10,0x20,0x10,0x1C,0x10,0x18,0x40,0x1C,
0x20,0x20,0x20,0x1C,0x20,0x18,0x20,0x20,0x80,0xFF,0x20,0x30,0x1C,0x10,0x18,0x20,
0x15,0x20,0x1C,0x20,0x20,0x20,0x26,0x40,0x20,0x20,0x2B,0x20,0x26,0x20,0x20,0x20,
0x30,0x80,0xFF,0x20,0x20,0x1C,0x10,0x18,0x10,0x20,0x20,0x26,0x20,0x2B,0x20,0x30,
0x20,0x2B,0x40,0x20,0x20,0x1C,0x10,0x18,0x10,0x20,0x20,0x26,0x20,0x2B,0x20,0x30,
0x20,0x2B,0x40,0x20,0x30,0x1C,0x10,0x18,0x20,0x15,0x20,0x1C,0x20,0x20,0x20,0x26,
0x40,0x20,0x20,0x2B,0x20,0x26,0x20,0x20,0x20,0x30,0x80,0x20,0x30,0x1C,0x10,0x20,
0x10,0x1C,0x10,0x20,0x20,0x26,0x20,0x2B,0x20,0x30,0x20,0x2B,0x40,0x20,0x15,0x1F,
0x05,0x20,0x10,0x1C,0x10,0x20,0x20,0x26,0x20,0x2B,0x20,0x30,0x20,0x2B,0x40,0x20,
0x30,0x1C,0x10,0x18,0x20,0x15,0x20,0x1C,0x20,0x20,0x20,0x26,0x40,0x20,0x20,0x2B,
0x20,0x26,0x20,0x20,0x20,0x30,0x30,0x20,0x30,0x1C,0x10,0x18,0x40,0x1C,0x20,0x20,
0x20,0x26,0x40,0x13,0x60,0x18,0x20,0x15,0x40,0x13,0x40,0x18,0x80,0x00,};
void Time0_Init()
{ TMOD = 0x01;
IE = 0x82;
TH0 = 0xD8;
TL0 = 0xEF; //12MZ晶振,10ms
}
void Time0_Int() interrupt 1
{ TH0 = 0xD8;
TL0 = 0xEF;
Count++; //长度加1
}
void Delay_xMs(unsigned int x) //1MS 延时子程序
{ unsigned int i,j;
for( i =0;i < x;i++ )
{for( j =0;j<3;j++ );}
}
void Play_Song(unsigned char i) //:歌曲播放子程序i 为播放哪一段曲目
{ unsigned char Temp1,Temp2;
unsigned int Addr;
Count = 0; //中断计数器清0
Addr = i * 217;
while(1)
{ Temp1 = SONG[Addr++];
if ( Temp1 == 0xFF ) //休止符
{TR0 = 0; Delay_xMs(100); }
else if ( Temp1 == 0x00 ) //歌曲结束符
{return;}
else {Temp2 = SONG[Addr++];
TR0 = 1;
while(1)
{ _Speak = ~_Speak;
Delay_xMs(Temp1);
if ( Temp2 == Count )
{Count = 0;
break;
} } } } } //请注意写程序时的格式规范,此处是为了节省空间
void main() //主程序
{Time0_Init(); //定时器0 中断初始化
while(1)
{Play_Song(0);} //播放
}
-----------------------------------------------------------------------------------------------------------------------
九.数摸转换 ADC0804 的应用
[实验任务]
从 ADC0804 的通道 IN+输入 0-5V 之间的模拟量,通过 ADC0804 转换成数字
量在数码管上以十进制形成显示出来。
[硬件电路图]
注意:上图未显示全的是四个共阴极数码管。
[实验原理]
ADC0804 是 8 位全 MOS 中速 A/D 转换器、它是逐次逼近式 A/D 转换器,片内有三态数据输出锁存器,可以和单片机直接接口。单通道输入,转换时间大约为100us。ADC0804转换时序是:当CS=0许可进行A/D转换。WR由低到高时,A/D开始转换,一次转换一共需要66-73个时钟周期。CS与WR同时有效时启动A/D转换,转换结束产生 INTR 信号(低电平有效),可供查询或者中断信号。在 CS和RD的控制下可以读取数据结果。
[C语言源程序]
#include <reg52.h>
code unsigned char seg7code[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,
0x82,0xf8,0x80,0x90
}; //显示段码
sbit int1=P3^3; //定义管脚功能
sbit cs=P3^2;
sbit wr=P3^6;
sbit rd=P3^7;
void Delay(unsigned int tc) //显示延时程序
{while( tc != 0 )
{unsigned int i;
for(i=0; i<100; i++);
tc--;}
}
unsigned char adc0804( void ) //读AD0804 子程序
{ unsigned char addata,i;
rd=1;wr=1;int1=1; //读ADC0804 前准备
P1=0xff; //P1全部置一准备
cs=0;wr=0;wr=1; //启动ADC0804 开始测电压
while(int1==1); //查询等待A/D转换完毕产生的INT(低电平有效)信号
rd=0; //开始读转换后数据
i=i; i=i; //无意义语句,用于延时等待ADC0804 读数完毕
addata=P1;//读出的数据赋与addate
rd=1;cs=1;//读数完毕
return(addata);// 返回最后读出的数据
}
unsigned int datpro(void)//ADC0804 读出的数据处理
{ unsigned char x;
unsigned int dianyah,dianyal; //用于存储读出数据的高字节和低字节
unsigned int dianya=0; //存储最后处理完的结果 注意数据类型
for(x=0;x<10;x++) //将10 次测得的结果存储在dianya 中
{dianya=adc0804()+dianya; }
dianya=dianya/10; //求平均值
dianyah=dianya&0xf0; //屏蔽低四位
dianyah=dianyah>>4; //右移四位 取出高四位
dianyal=dianya&0x0f; //屏蔽高四位 取出低四位
dianya=dianyal*20+dianyah*320; //最后的结果是一个四位数,便于显示
return(dianya);//返回最后处理结果
}
void Led()
{ unsigned int date;
date=datpro(); //调用数据处理最后结果
P2=P2&0xef;
P0=seg7code[date/1000]|0x80; //输出个位数和小数点
Delay(8); P2=P2|0xf0; P2=P2&0xdf;
P0=seg7code[date%1000/100]; //输出小数点后第一位
Delay(8); P2=P2|0xf0; P2=P2&0xbf;
P0=seg7code[date%100/10]; //输出小数点后第二位
Delay(8); P2=P2|0xf0; P2=P2&0x7f;
P0=seg7code[date%10]; //输出小数点后第三位
Delay(8); P2=P2|0xf0;
}
main()
{
while(1)
{
Led( ); //只需调用显示函数
}
}
ADC0809是带有8位A/D转换器、8路多路开关以及微处理机兼容的控制逻辑的CMOS组件。它是逐次逼近式A/D转换器,可以和单片机直接接口。
(1)ADC0809的内部逻辑结构
由上图可知,ADC0809由一个8路模拟开关、一个地址锁存与译码器、一个A/D转换器和一个三态输出锁存器组成。多路开关可选通8个模拟通道,允许8路模拟量分时输入,共用A/D转换器进行转换。三态输出锁器用于锁存A/D转换完的数字量,当OE端为高电平时,才可以从三态输出锁存器取走转换完的数据。
(2)引脚结构
IN0-IN7:8条模拟量输入通道
ADC0809对输入模拟量要求:信号单极性,电压范围是0-5V,若信号太小,必须进行放大;输入的模拟量在转换过程中应该保持不变,如若模拟量变化太快,则需在输入前增加采样保持电路。
地址输入和控制线:4条
ALE为地址锁存允许输入线,高电平有效。当ALE线为高电平时,地址锁存与译码器将A,B,C三条地址线的地址信号进行锁存,经译码后被选中的通道的模拟量进转换器进行转换。A,B和C为地址输入线,用于选通IN0-IN7上的一路模拟量输入。通道选择表如下表所示。
数字量输出及控制线:11条
ST为转换启动信号。当ST上跳沿时,所有内部寄存器清零;下跳沿时,开始进行A/D转换;在转换期间,ST应保持低电平。EOC为转换结束信号。当EOC为高电平时,表明转换结束;否则,表明正在进行A/D转换。OE为输出允许信号,用于控制三条输出锁存器向单片机输出转换得到的数据。OE=1,输出转换得到的数据;OE=0,输出数据线呈高阻状态。D7-D0为数字量输出线。
CLK为时钟输入信号线。因ADC0809的内部没有时钟电路,所需时钟信号必须由外界提供,通常使用频率为500KHZ,
VREF(+),VREF(-)为参考电压输入。
2. ADC0809应用说明
(1). ADC0809内部带有输出锁存器,可以与AT89S51单片机直接相连。
(2). 初始化时,使ST和OE信号全为低电平。
(3). 送要转换的哪一通道的地址到A,B,C端口上。
(4). 在ST端给出一个至少有100ns宽的正脉冲信号。
(5). 是否转换完毕,我们根据EOC信号来判断。
(6). 当EOC变为高电平时,这时给OE为高电平,转换的数据就输出给单片机了。
电路原理图:
adc0809内部结构图:
----------------------------------------------------------------------------------------------------------------------------------------------
十.摸数转换 DAC0832的应用
[实验任务]
用两个按键通过单片机控制 DAC0832 的输出,使 OUT 端可以输出 0—5V 的幅
值,频率为1KHZ的锯齿波和三角波两种波形。通上电源后;按下INT1则输出三
角波,在按下INT0输出锯齿波。
[硬件电路图]
注意:D0对应P0.0;D1对应P0.1;D2对应P0.2……
按键可以直接使用INT0和INT1按键(程序中以扫描方式识别按键)。
硬件电路图中的画框的部分,运放 LM324 的连接部分需要用户自己搭建,注
意LM324使用的是正负12V的双电源供电。我们只使用LM324四个运放中的两个
[实验原理]
ADC0804是8位全MOS 中速D/A转换器,采用R—2RT形电阻解码网络,转换结果为一对差动电流输出,转换时间大约为 1us。使用单电源+5V―+15V 供电。参考电压为-10V-+10V。在此我们直接选择+5V 作为参考电压。DAC0832 有三种工作方式:直通方式,单缓冲方式,双缓冲方式;在此我们选择直通的工作方式将XFER WR 1WR2 CS管脚全部接数字地。管脚8接参考电压,在此我们接的参考电压是+5V。那么经过第一级运放后,输出电压将是-5V-0V,在经过第二级运放反相放大1倍以后将可以输出0V—5V了。我们在控制P1口输出数据有规律的变化将可以产生三角波,锯齿波,梯型波等波形了。
[C语言源程序]
#include <AT89X51.H>
unsigned char keycnt=0;
unsigned char tcnt=0; //键值判断
bit sjz=0; //产生三角波时用到的标志
void delayl() //延时子程序
{ unsigned char i,j;
for(i=20;i>0;i--)
for(j=248;j>0;j--); }
void KEY() //按键扫描程序
{
if(P3_2==0)
{
delayl(); //延时跳过按下时的抖动
if(P3_2==0)
{
keycnt=0; //定时器产生锯齿波标志
TR0=0; //暂时停止波形输出
TH0=0x256-40; //对TH0 TL0 赋值
TL0=0x256-40;
TR0=1; //开始定时,产生锯齿波
while(P3_2==0); //如果一直按着键,则等待松键开
delayl(); //延时跳过松开后的抖动
} }
if(P3_3==0)
{
delayl(); //延时跳过按下时的抖动
if(P3_3==0)
{
keycnt=1; //定时器产生三角波标志
TR0=0; //暂时停止波形输出
TH0=0x256-40; //对TH0 TL0 赋值
TL0=0x256-40;
TR0=1; //开始定时 产生三角波
}
while(P3_2==0); //如果一直按着键,则等待松键开
delayl(); //延时跳过松开后的抖动
}
} //请注意写程序时的格式规范,此处是为了节省纸张
void t0(void) interrupt 1 using 0 //定时中断服务函数
{
if(keycnt==0) //产生锯齿波
{
P0=tcnt;
tcnt=+0x0a; //步进0.2V/一次中断
if(tcnt==0xfb)
{tcnt=0;}
}
if(keycnt==1) //产生三角波
{ if(sjz==0)
{P0=tcnt;
tcnt=+0x0a;//步进0.2V/一次中断
if(tcnt==0xfa)
{sjz=1;}
}
if(sjz==1)
{
P0=tcnt;
tcnt=-0x0a;
if(tcnt==0)
{sjz=0;}
}
}
}
void main(void)
{
TMOD=0x02; //定时器工作在方式2
ET0=1;
EA=1;
while(1)
{ KEY(); }
}