一、利用中断发出1Khz的方波信号,驱动蜂鸣器鸣叫。
1、定时器/计数器的结构
AT89S51定时器/计数器结构见图,定时器/计数器TO由特殊功能寄存器THO、TLO构成,T1由特殊功能寄存器TH1、TL1构成。
图一
T0、T1都有定时器和计数器两种工作模式,两种模式实质都是对脉冲信号进行计数,只不过计数信号来源不同。
计数器模式是对加在T0(P3.4)和T1(P3.5)两个引脚上的外部脉冲进行计数。
定时器模式是对系统时钟信号经12分频后的内部脉冲信号(机器周期)计数。由于系统时钟频率是定值,可根据计数值计算出定时时间。两个定时器/ 计数器属于增1计数器,即每计一个脉冲,计数器增1。
T0、T1具有4种工作方式(方式0、1、2和3)。
图中特殊功能寄存器TMOD用于选择定时器 / 计数器 T0、T1的工作模式和工作方式。特殊功能寄存器TCON用于控制T0、T1的启动和停止计数,同时包含了TO、T1状态。
计数器起始计数从初值开始。单片机复位时计数器初值为0,也可给计数器装入1个新的初值。
2、利用T1控制发出1KHz的音频信号
要求:利用T1的中断控制P1.7引脚输出频率为1kHz方波音频信号驱动蜂鸣器发声。系统时钟为12MHz。方波音频信号周期1ms,因此T1的定时中断时间为0.5ms,进入中断服务程序后,对P1.7求反。电路见图2。
先计算T1初值,系统时钟为12MHz,则机器周期为1μs。1kHz音频信号周期为1ms,要定时计数的脉冲数为a。则T1初值:
TH1=(65536-a) /256
TL1=(65536 -a) %256
图2 控制蜂鸣器发出1KHz的音频信号
代码如下:
#include<reg51.h> //包含头文件
sbit sound=P1^7; //将sound位定义为P1.7脚
#define f1(a) (65536-a)/256 //定义装入定时器高8位时间常数
#define f2(a) (65536-a)%256 //定义装入定时器低8位时间常数
unsigned int i=500;
unsigned int j=0;
void main(void)
{
EA=1; //开总中断.
ET1=1; //允许定时器T1中断 .
TMOD=0x10; //TMOD=0001 000B,使用T1的方式1定时 TH1=f1(i); //给T1高8位赋初值.
TL1=f2(i); //给T1低8位赋初值.
TR1=1; //启动T1
while(1)
{ //循环等待
i=460;
while(j<2000);
j=0;
i=360;
while(j<2000);
j=0;
}
}
void T1(void) interrupt 3 using 0 //定时器T1中断函数
{
TR1= 0; //关闭T1
sound=~sound; //P1.7输出求反
TH1=f1(i); //T1的高8位重新赋初值.
TL1=f2(i); //T1的低8位重新赋初值.
j++;
TR1=1; //启动定时器T1
}
仿真效果如下:
二、LED数码管秒表的制作。
要求:用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。
LED数码管秒表的制作
要求:用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0·再次按一下计时功能键,则重复上述计时过程。
本秒表应用定时器模式,计时范围0.1~9.9s·此外还涉及如何编写控制LED数码管显示的程序。
仿真图:
代码如下:
#include<reg51.h>
unsigned char code discode1[]= {0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
//数码管显示0~9的段码表, 带小数点
unsigned char code discode2[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//数码管显示0~9的段码表,不带小数点
unsigned char timer=0; //timer记录中断次数
unsigned char second; //second储存秒
unsigned char key=0; //key记录按键次数
main()
{
TMOD=0x01; //定时器T0方式1定时
ET0=1; //允许定时器T0中断
EA=1; //总中断允许
second=0; //设初始值
P0=discode1[second/10]; //显示秒位0
P2=discode2[second%10]; //显示0.1s位0
while(1) //循环
{
if((P3&0x80)==0x00) //当按键被按下时
{
key++; //按键次数加1
switch(key) //根据按键次数分三种情况
{
case 1: //第一次按下为启动秒表计时
TH0=0xee; //向TH0写入初值的高8位
TL0=0x00; //向TL0写入初值的低8位,定时5ms
TR0=1; //启动定时器T0
break;
case 2: //按下两次暂定秒表
TR0=0; //关闭定时器T0
break;
case 3: //按下3次秒表清0
key=0; //按键次数清
second=0; //秒表清0
P0=discode1[second/10]; //显示秒位0 P2=discode2[second%10]; //显示0.1s位0
break;
}
while((P3&0x80)==0x00); //如果按键时间过长在此循环
}
}
}
void int_T0() interrupt 1 using 0 //定时器T0中断函数
{
TR0=0; //停止计时,执行以下操作(会带来计时误差)
TH0=0xee; //向TH0写入初值的高8位
TL0=0x00; //向TL0写入初值的低8位,定时5ms
timer++; //记录中断次数
if (timer==20) //中断20次,共计时20*5ms=100ms=0.1s
{
timer=0; //中断次数清0
second++; //加0.1s
P0=discode1[second/10]; //根据计时,即时显示秒位 P2=discode2[second%10]; //根据计时,即时显示0.1s位 }
if(second==99) //当计时到9.9s时
{
TR0=0; //停止计时
second=0; //秒数清0
key=2; //按键数置2,当再次按下按键时, //key++,即key=3,秒表清0复原
}
else //计时不到9.9s时
{
TR0=1; //启动定时器继续计时
}
}
}
三、使用定时器实现一个LCD显示时钟。
LCD时钟的设计
最小计时单位是秒,如何获得1s的定时?可将T0定时时间定为50ms,采用中断方式进行溢出次数累计,满20次,则秒计数变量second加1;若秒计满60,则分计数变量minute加1,同时将秒计数变量second清0;若分钟计满60,则小时计数变量hour加1;若小时计数变量满24,则将小时计数变量hour清0。
仿真原理图:
参考代码:
#include<reg51.h>
#include<lcd1602.h>
#define uchar unsigned char
#define uint unsigned int
uchar int_time; //定义中断次数计数变量
uchar second; //秒计数变量
uchar minute; //分钟计数变量
uchar hour; //小时计数变量
uchar code date[]=" H.I.T. CHINA "; //LCD第1行显示的内容
uchar code time[]=" TIME 23:59:55 "; //LCD第2行显示的内容
uchar second=55,minute=59,hour=23;
void clock_init()
{
uchar i,j;
for(i=0;i<16;i++)
{
write_data(date[i]);
}
write_com(0x80+0x40);
TMOD=0x01; //设置定时器T0为方式1定时
EA=1; // 总中断开
ET0=1; // 允许T0中断
TH0=(65536-46483)/256; //给T0装初值
TL0=(65536-46483)%256;
TR0=1;
int_time=0; //中断次数、秒、分、时单元清0
second=55;
minute=59;
hour=23;
while(1)
{
clock_write(second ,minute, hour);
}
}
void T0_interserve(void) interrupt 1 using 1 //T0中断服务子程序
{
int_time++; //中断次数加1
if(int_time==20) //若中断次数计满20次
{
int_time=0; //中断次数变量清0
second++; //秒计数变量加 1
}
if(second==60) //若计满60s
{
second=0; //秒计数变量清0
minute ++; //分计数变量加 1
}
if(minute==60) //若计满60分
{
minute=0; //分计数变量清0
hour ++; //小时计数变量加1
}
if(hour==24)
{
hour=0; //小时计数计满24,将小时计数变量清0
}
TH0=(65536-46083)/256; //定时器T0重新赋值
TL0=(65536-46083)%256;
}
四、双单片坤串行通信
1、串行口通信介绍
单片机坤的串行和并行通信是指单片机与外部设备或其他单片机之间进行数据传输的两种基本方式。
1.串行通信:
串行通信是一种逐位传输数据的通信方式,数据位按照一定的顺序依次发送和接收。在单片机中,通过配置串行通信的参数(如波特率、数据位、校验位等),可以实现与其他设备或单片机之间的可靠数据传输。
2.并行通信:
并行通信是指同时传输多个数据位的通信方式,每个数据位使用单独的线路传输。
与串行通信相比,并行通信具有更高的传输速度,但是需要更多的线路和硬件资源。
在单片机中,通常使用并行通信来连接外部设备,如存储器、显示器或其他外围设备。
差异:
传输速度:串行通信适用于低速传输,而并行通信适用于高速传输。
线路数量和硬件资源:串行通信需要较少的线路和硬件资源,而并行通信需要更多的线路和硬件资源。
2、双单片坤通信
仿真原理图:
参考代码:
//甲机发送
#include <REGX52.H>
sbit T_P=PSW^0;
unsigned char code Tab[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//流水灯程序
void Send(unsigned char dat)
{
TB8=T_P;
SBUF=dat;
while(TI==0);
TI=0;
}
void Delay(unsigned char xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
unsigned char i;
TMOD=0x20;
SCON=0xc0;
PCON&=0x7f;
TH1=0xfd;
TL1=0xfd;
TR1=1;
while(1)
{
for(i=0;i<8;i++)
{
Send(Tab[i]);
Delay(200);//约200ms发送数据
}
}
}
//乙机接收
#include <REGX52.H>
sbit R_P=PSW^0;
unsigned char Receive()//接收一字节数据
{
unsigned char dat;
while(RI==0);//检测RI,RI=0,未接收完
RI=0; //接收数据完成RI手动清0
ACC=SBUF; //将接收缓冲器的数据存于ACC
if(RB8=R_P) //只有偶检验成功才能往下执行,接收数据
{
dat=ACC; //将ACC数据存于dat
return dat; //将接收的数据返回
}
}
void main()
{
TMOD=0x20; //设置定时器为方式2,8位自动重载
SCON=0xd0; //串口为方式3,允许接收REN=1
PCON&=0x7f; //波特率不加倍
TH1=0xfd; //波特率9600
TL1=0xfd;
TR1=1;
//REN=1;
while(1)
{
P2=Receive(); //将接收的数据送至P2口显示
}
}
五、单片坤与PC通信
要求:将单片机串口与笔记本电脑串口模块相连,单片机每隔2秒发送“Hello C51”,笔记本电脑用串口助手软件接收。 如果串口助手发送字符“0" 给单片机,则单片机停止发送; 如果单片机收到“1”,则继续每隔2秒发送“Hello C51”。
参考代码:
#include <REGX52.H>
#include "stdio.h"
unsigned char ch;
unsigned char Flag=1;
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA=1;
ES=1;
}
void UartSend()
{
TI=1;
puts("Hello C51");
while(!TI);
TI=0;
Delay(2000);
}
void main()
{
UartInit();
while(1)
{
if(Flag==1)UartSend();
}
}
//串口中断函数模板
void UART_Routine() interrupt 4 //串口中断
{
if(RI==1)
{
RI=0;
ch=SBUF;
if(ch=='1')Flag=1;
if(ch=='0')Flag=0;
}
}
总结: 经过此次实验增加了对51单片机的进一步了解及有了更多的实践经验,是一次很好的锻炼。但由于本人认知浅薄,上述内容有诸多不详错漏之处,望各位海涵并指出批正。