目录
一、定时器/计数器的结构
AT89S51定时器/计数器结构见图 7-1,定时器/计数器T0由特殊功能寄存器TH0、TL0构成,T1由特殊功能寄存器TH1、TL1构成。
图1 定时器/计数器结构框图
T0、T1都有定时器和计数器两种工作模式,两种模式实质都是对脉冲信号进行计数,只不过计数信号来源不同。计数器模式是对加在T0(P3.4)和T1(P3.5)两个引脚上的外部脉冲进行计数(见图7-1);定时器模式是对系统时钟信号经12分频后的内部脉冲信号(机器周期)计数。由于系统时钟频率是定值,可根据计数值计算出定时时间。两个定时器/计数器属于增1计数器,即每计一个脉冲,计数器增1。T0、T1具有4种工作方式(方式0、1、2和3)。图 7-1特殊功能寄存器TMOD用于选择定时器/计数器T0、T1的工作模式和工作方式。特殊功能寄存器TCON用于控制T0、T1的启动和停止计数,同时包含了T0、T1状态。计数器起始计数从初值开始。单片机复位时计数器初值为0,也可给计数器装入1个新的初值。
二、利用中断发出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
}
Proteus仿真:
三、LED数码管秒表的制作。
用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。
电路图:
代码:
#include<reg51.h>
unsigned char code discode1[]= {0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
unsigned char code discode2[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char timer=0;
unsigned char second;
unsigned char key=0;
main()
{
TMOD=0x01;
ET0=1;
EA=1;
second=0;
P0=discode1[second/10];
P2=discode2[second%10];
while(1)
{
if((P3&0x80)==0x00)
{
key++;
switch(key)
{
case 1:
TH0=0xee;
TL0=0x00;
TR0=1;
break;
case 2:
TR0=0;
break;
case 3:
key=0;
second=0;
P0=discode1[second/10];
break;
}
while((P3&0x80)==0x00);
}
}
void int_T0() interrupt 1 using 0
{
TR0=0;
TH0=0xee;
TL0=0x00;
timer++;
if (timer==20)
{
timer=0;
second++;
P0=discode1[second/10];
P2=discode2[second%10];
}
if(second==99)
{
TR0=0;
second=0;
key=2;
}
else
{
TR0=1;
}
}
四、使用定时器实现一个LCD显示时钟。
电路设计:
代码:
#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);
for(j=0;j<16;j++)
{
write_data(time[j]);
}
}
void clock_write( uint s, uint m, uint h)
{
write_sfm(0x47,h);
write_sfm(0x4a,m);
write_sfm(0x4d,s);
}
void main()
{
init1602(); //LCD初始化
clock_init(); //时钟初始化
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;
}
Proteus仿真:
五、两个单片机的串口通信
(甲乙两个单片机串口通信)在实物实验时,如果不能找到两个普中单片机,用笔记本电脑的串口助手程序代替其中一个单片机,实现课件上描述的主要功能。
电路设计:
甲代码:
#include <reg51.h>
sbit P=PSW^0; //P位为PSW寄存器的第0位,即奇偶校验位
unsigned char Tab[8]= {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf,
0xbf, 0x7f}; //控制流水灯显示数据数组,为全局变量
void main(void) //主函数
{
unsigned char i;
TMOD=0x20; //设置定时器T1为方式2
SCON=0xc0; //设置串口为方式3
PCON=0x00; //SMOD=0
TH1=0xfd; //给T1赋初值,波特率设置为9600
TL1=0xfd;
TR1=1; //启动定时器T1
while(1)
{
for(i=0;i<8;i++)
{
Send(Tab[i]);
delay( ); //大约200ms发送一次数据
}
}
}
void Send(unsigned char dat) // 发送1字节数据的函数
{
TB8=P; // 将偶校验位作为第9位数据发送
SBUF=dat;
while(TI==0); //检测TI,TI=0,未发送完
; // 空操作
TI=0; // 1字节发送完,TI清0
}
void delay (void) // 延时约200ms的函数
{
unsigned char m,n;
for(m=0;m<250;m++)
for(n=0;n<250;n++);
}
乙代码:
#include <reg51.h>
sbit P=PSW^0; // P位为PSW 寄存器的第0位,即奇偶校验位
void main(void) //主函数
{
TMOD=0x20; //设置定时器T1为方式2
SCON=0xd0; //设置串口为方式3,允许接收REN=1
PCON=0x00; // SMOD=0
TH1=0xfd; //给定时器T1赋初值,波特率为9600
TL1=0xfd;
TR1=1; //接通定时器T1
REN=1; //允许接收
while(1)
{
P1= Receive( ); //将接收到的数据送P1口显示
}
}
unsigned char Receive(void) //接收1字节数据的函数
{
unsigned char dat;
while(RI==0); //检测RI,RI=0,未接收完,则循环等待
;
RI=0; //已接收一帧数据,将RI清0
ACC=SBUF; //将接收缓冲器的数据存于ACC
if(RB8==P) //只有偶校验成功才能往下执行,接收数据
{
dat=ACC; //将接收缓冲器的数据存于dat
return dat; //将接收的数据返回
}
}
Proteus仿真:
六、单片机与笔记本电脑串口通信
将单片机串口与笔记本电脑串口模块相连,单片机每隔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)
{
PCON &= 0x7F;
SCON = 0x50;
TMOD &= 0x0F;
TMOD |= 0x20;
TL1 = 0xFD;
TH1 = 0xFD;
ET1 = 0;
TR1 = 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;
}
}