【51单片机】定时器应用与时钟显示技巧
定时器与串口通信
一、任务目标
请分别在Proteus和普中51单片机上完成下列程序:
1、利用中断发出1Khz的方波信号,驱动蜂鸣器鸣叫。
2、用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。
3、使用定时器实现一个LCD显示时钟。
二、实现过程
2.1 中断方波信号
通过改变定时器的计数值来改变蜂鸣器的频率,从而产生不同的声音。
系统时钟为12MHz,则机器周期为1µs。方波音频信号周期1ms。
1kHz方波音频信号,因此T1的定时中断时间为0.5 ms。进入中断后,P1.0端口和P1.7端口的状态也在每次中断时取反。
先计算T1初值,系统时钟为12MHz,则机器周期为1µs。1kHz音频信号周期为1ms,要定时计数的脉冲数为a。则T1初值:
TH1=(65 536 −a) /256;
TL1=(65 536 −a) %256
2.1.1 Proteus仿真图
2.1.2 KEIL代码
#include <reg52.h>
sbit BUZZ=P1^7; // 假设蜂鸣器接在P1.7
sbit P1_0=P1^0; // 假设示波器接在P1.0
#define f1(a) (65536 - a) / 256; //用于计算定时器初值的高8位
#define f2(a) (65536 - a) % 256; //用于计算定时器初值的低8位
unsigned int i=500;
//定义一个无符号整数变量i,初始值为500,用于定时器的计数值
unsigned int j=0;
//定义一个无符号整数变量j,用于计数中断次数
void main(void) {
TMOD|=0x10; //设置定时器T1为模式2
TH1=f1(i); //用于设置定时器初值的高8位
TL1=f2(i); //用于设置定时器初值的低8位
ET1=1; //允许定时器T1中断
EA=1; //开启全局中断
TR1=1; //启动定时器T1
while(1)
{
i=460; //设置定时器的计数值
while(j<2000); //等待中断发生2000次
j=0; //重置计数器j
i=360;
while(j<2000);
j=0;
}
}
void int_T1() interrupt 3 using 0 //定义定时器T1的中断服务程序
{
TR1=0; //在中断服务程序开始时,先关闭定时器T1
BUZZ=~BUZZ; //取反蜂鸣器的输出状态
P1_0=~P1_0; //取反P1.0端口的输出状态
TH1=f1(i); //更新定时器T1的高8位初值
TL1=f2(i); //更新定时器T1的低8位初值
j++;
TR1=1; //重新启动定时器T1
}
2.2 显示计时时间
2.2.1 Proteus仿真图
2.2.2 代码实现
需要编写代码来初始化定时器、扫描按键状态、更新计时时间,以及将时间显示在数码管上。
初始化定时器
TMOD=0x01; //定时器T0方式1定时
ET0=1; //允许定时器T0中断
EA=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; //启动定时器继续计时
}
}
}
完整代码
#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记录按键次数
void main(void) //主函数
{
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; //启动定时器继续计时
}
}
}
2.3 LCD显示时钟
2.3.1 Proteus仿真图
2.3.2 代码实现
#include<reg52.h>
#include<lcd1602.h>
#include<stdio.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;
}
三、实验总结
以上则是我的实验过程,如有错误请各位大佬多多指教
作者:双料毒狼_s