基于51单片机的秒表系统设计(源代码+Proteus仿真图)

  • Post author:
  • Post category:其他

目录

总体设计

晶体震荡电路

复位电路

按键控制电路

LCD1602显示电路

蜂鸣器电路

Proteus仿真图调试结果

源代码:

Proteus仿真提取链接


总体设计

本设计是采用AT89C51单片机为中心,利用其定时器/计数器定时和记数的原理,结合显示电路、电源电路、LCD1602液晶以及键盘电路来设计计时器。将软、硬件有机的结合起来,使得系统能够实现四位LCD显示,最大显示时间为09:59:99,有开始/暂停、复位、记录和查看功能,并设有每秒提醒功能。

此次的设计是采用定时器进行计时并且在LCD1602上显示时间,先要基本了解硬件内在结构,确定用P0并行端口进行LCD1602控制输入,使用P2.0、P2.1、P2.2控制LCD的RE、RW、EN端口。P1.0控制蜂鸣器、P1.1控制开始/暂停、P1.2、P1.3控制查看上一条、下一条、P1.4控制记录、P1.5控制复位、P1.6控制每秒提醒。

晶体震荡电路

利用12分频的晶振的一个机器周期为一微妙,通过循环延时产生0.1秒的延时,通过XTAL1和XTAL2外接晶体振荡器构成内部振荡方式。由于单片机内部有一个高增益反相放大器,当外接晶振后,就构成了自激振荡器并产生振荡时钟脉冲。51单片机内部的振荡电路是一个高增益反相放大器,引线XTAL1和XTAL2引脚分别为振荡器的反相放大器输入端,振荡器的反相放大输出端,再接上晶振形成一个完整电路,产生震荡。

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAaGFvaGFvX-S4jeeng-WktA==,size_16,color_FFFFFF,t_70,g_se,x_16

复位电路

采用上电加按键复位电路,也就是手动复位,上电后,由于电容充电,使RET持续一段高电平时间,当单片机运行时,按下复位键也能使RET持续一段时间的高电平,从而实现上电加开关复位的操作上电加复位电路。

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAaGFvaGFvX-S4jeeng-WktA==,size_16,color_FFFFFF,t_70,g_se,x_16

按键控制电路

需要实现秒表的开始/暂停、记录、查看上一条、查看下一条、复位、每秒提醒的开关。用6个按键实现。P1.1控制开始/暂停、P1.2、P1.3控制查看上一条、下一条、P1.4控制记录、P1.5控制复位、P1.6控制每秒提醒。

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAaGFvaGFvX-S4jeeng-WktA==,size_16,color_FFFFFF,t_70,g_se,x_16

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAaGFvaGFvX-S4jeeng-WktA==,size_17,color_FFFFFF,t_70,g_se,x_16

LCD1602显示电路

所谓的1602是指显示的时候,有两行内容每行有16个字符。

基本操作时许

  1. 读状态:输入:RS=L,RW=H,E=H
                –输出:D0-D7=状态字
  2. 写指令:输入:RS=L,RW=L,D0-D7=指令码
                –输出:无
  3. 读数据:输入:RS=H,RW=H,E=H
                –输入:D0-D7=数据
  4. 写数据:输入:RS=H,RW=L,D0-D7=数据,E=高脉冲
                –输入:无

 watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAaGFvaGFvX-S4jeeng-WktA==,size_17,color_FFFFFF,t_70,g_se,x_16

蜂鸣器电路

蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。由于题目内容有计时时间每秒都得有提醒功能,因此我们还得用蜂鸣器。如图所示:

 watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAaGFvaGFvX-S4jeeng-WktA==,size_12,color_FFFFFF,t_70,g_se,x_16

Proteus仿真图调试结果

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAaGFvaGFvX-S4jeeng-WktA==,size_20,color_FFFFFF,t_70,g_se,x_16

 

源代码:

#include<reg52.h>
#include<intrins.h>
//1602定义
sbit RS = P2^0;   //定义端口 
sbit RW = P2^1;
sbit EN = P2^2;

#define RS_CLR RS=0 //状态量定义,方便读取代码
#define RS_SET RS=1

#define RW_CLR RW=0 
#define RW_SET RW=1 

#define EN_CLR EN=0
#define EN_SET EN=1

#define DataPort P0	//1602数据接口
//按键定义
sbit key1=P1^3;			//查看上一天记录
sbit key2=P1^2;			//查看下一条记录
sbit key3=P1^1;			//开始/暂停
sbit key4=P1^4;			//记录
sbit key5=P1^5;			//复位
sbit key6=P1^6;
//蜂鸣器定义
sbit Beep=P1^0;			//1时不响,0时响
//全局变量定义
unsigned int 	RecordTableH[11];		//保存分和秒
unsigned char RecordTableL[11];		//保存毫秒
unsigned int	CountTimeH;					//记录分、秒时间
unsigned char	CountTimeL;					//记录毫秒时间
unsigned char DataRecordFlag = 0;	//数据记录标志位,表示记录的数据量(记录了多少个数据)
unsigned char DataFlag = 0;				//保存数据标志,用于记录保存数据的个数
unsigned int buzzer=0;
code unsigned char NumTable[]="0123456789";
bit Counting = 0;				//计时标志0暂停/停止,1计时状态
//函数声明
void LCD1602_WriteCom(unsigned char com); 			//1602部分
void LCD1602_WriteData(unsigned char Data); 
void LCD1602_Clear(void);
void LCD1602_PutString(unsigned char x,unsigned char y,unsigned char *s);
void LCD1602_PutChar(unsigned char x,unsigned char y,unsigned char Data);
void LCD1602_Init(void);
void LCD1602_Delay(unsigned int t);

void delay_ms(unsigned int t);
void IniTC0(void) ;						//初始化定时计数器0
void KeyWork(void);						//按键扫描以及按键功能动作
void DisplayCountTime(void);	//显示计时的时间

void main()
{
	IniTC0();
	LCD1602_Init();
	Beep = 1;
	
	LCD1602_PutString(4,0,"Well Come");			
	LCD1602_PutString(2,1,"  Stopwatch   ");
	delay_ms(1000);
	LCD1602_Clear();
	
	while(1)
	{
		DisplayCountTime();
		KeyWork();
	}
}
/*------------------------------------------------
              写入命令函数
------------------------------------------------*/
void LCD1602_WriteCom(unsigned char com) 
{
	LCD1602_Delay(5);
	RS_CLR; 
	RW_CLR; 
	EN_SET; 
	DataPort= com; 
	_nop_(); 
	EN_CLR;
}
/*------------------------------------------------
              写入数据函数
------------------------------------------------*/
void LCD1602_WriteData(unsigned char Data) 
{ 
	LCD1602_Delay(5);
	RS_SET; 
	RW_CLR; 
	EN_SET; 
	DataPort= Data; 
	_nop_();
	EN_CLR;
}
/*------------------------------------------------
                清屏函数
------------------------------------------------*/
void LCD1602_Clear(void) 
{ 
 LCD1602_WriteCom(0x01); 
 LCD1602_Delay(5);
}
/*------------------------------------------------
              写入字符串函数
------------------------------------------------*/
void LCD1602_PutString(unsigned char x,unsigned char y,unsigned char *s) 
{     
	if (y == 0) 
	{     
		LCD1602_WriteCom(0x80 + x);     //表示第一行
	}
	else 
	{      
		LCD1602_WriteCom(0xC0 + x);      //表示第二行
	}        
	while (*s) 
	{     
		LCD1602_WriteData( *s);     
		s ++;     
	}
}
/*------------------------------------------------
              写入字符函数
------------------------------------------------*/
void LCD1602_PutChar(unsigned char x,unsigned char y,unsigned char Data) 
{     
	if (y == 0) 
	{     
		LCD1602_WriteCom(0x80 + x);     
	}else {     
		LCD1602_WriteCom(0xC0 + x);     
	}        
	LCD1602_WriteData( Data);  
}
/*------------------------------------------------
              初始化函数
------------------------------------------------*/
void LCD1602_Init(void) 
{
	LCD1602_WriteCom(0x38);    /*显示模式设置*/ 
	LCD1602_Delay(5); 
	LCD1602_WriteCom(0x38); 
	LCD1602_Delay(5); 
	LCD1602_WriteCom(0x38); 
	LCD1602_Delay(5); 
	LCD1602_WriteCom(0x38);  
	LCD1602_WriteCom(0x08);    /*显示关闭*/ 
	LCD1602_WriteCom(0x01);    /*显示清屏*/ 
	LCD1602_WriteCom(0x06);    /*显示光标移动设置*/ 
	LCD1602_Delay(5); 
	LCD1602_WriteCom(0x0C);    /*显示开及光标设置*/
	LCD1602_Clear();
}
/*------------------------------------------------
							1602延时函数
------------------------------------------------*/
void LCD1602_Delay(unsigned int t)
{
	unsigned char c;
	while(t--)
	{
		for(c = 50; c > 0; c--);
	}
}
void delay_ms(unsigned int t)
{
	unsigned char c;
	while(t--)
	{
		for(c = 50; c > 0; c--);
	}
}
void IniTC0(void)        
{
	TMOD=0x01;		//设置定时计数器的工作模式
	TR0 = 0;			//关闭定时计数器
	TF0 = 0;			//清空定时计数器标志位
	
	TH0=0xFC; 				//定时1ms,当计时时间不准在这修改
	TL0=0x18;
	
	ET0 = 1;			//开启定时计数器0中断
	EA=1; 				//开总中断
}
void KeyWork(void)					//判断按键按下就动作,提高实时性
{
	if(key1 == 0)									//显示上一条记录,只有在不计时时有效
	{
		if(!Counting && DataRecordFlag != 0)//只有不在计时,并且有数据时才能察看(DataRecordFlag表示记录的数据条数)
		{
			DataFlag++;
			if(DataFlag >= DataRecordFlag)
			{
				DataFlag = DataRecordFlag;
			}
			if(DataFlag >= 10)
			{		
				LCD1602_PutString(0,1,"10:             ");//显示条数并且把后面的显示清空
			}else {
				LCD1602_PutChar(0, 1, NumTable[DataFlag]);
				LCD1602_PutString(1,1,":              ");
			}
			
			LCD1602_PutChar(4, 1, NumTable[(RecordTableH[DataFlag]/6000)%6]);		//显示记录的时间
			LCD1602_PutChar(5, 1, NumTable[(RecordTableH[DataFlag]/600)%10]);
			LCD1602_PutChar(6, 1, ':');
			LCD1602_PutChar(7, 1, NumTable[(RecordTableH[DataFlag]/100)%6]);
			LCD1602_PutChar(8, 1, NumTable[(RecordTableH[DataFlag]/10)%10]);
			LCD1602_PutChar(9, 1, ':');
			LCD1602_PutChar(10, 1, NumTable[((RecordTableL[DataFlag]%10)%10)%10]);
			LCD1602_PutChar(11, 1, NumTable[RecordTableL[DataFlag]/10]);
		}
		delay_ms(5);						//避开抖动防止多次触发,期间继续显示计数时间
		while(!key1) 
			DisplayCountTime();					//等待按键释放,避开抖动防止多次触发,期间继续显示计数时间
	}
	if(key2 == 0)									//显示下一条记录,只有在不计时时有效
	{
		if(!Counting && DataRecordFlag != 0)//只有不在计时,并且有数据时才能察看(DataRecordFlag表示记录的数据条数)
		{
			DataFlag--;
			if(DataFlag == 0)
			{
				DataFlag = 1;
			}
			LCD1602_PutChar(0, 1, NumTable[DataFlag]);//显示条数并且把后面的显示清空
			LCD1602_PutString(1,1,":              ");
			
			LCD1602_PutChar(4, 1, NumTable[(RecordTableH[DataFlag]/6000)%6]);		//显示记录的时间
			LCD1602_PutChar(5, 1, NumTable[(RecordTableH[DataFlag]/600)%10]);
			LCD1602_PutChar(6, 1, ':');
			LCD1602_PutChar(7, 1, NumTable[(RecordTableH[DataFlag]/100)%6]);
			LCD1602_PutChar(8, 1, NumTable[(RecordTableH[DataFlag]/10)%10]);
			LCD1602_PutChar(9, 1, ':');
			LCD1602_PutChar(10, 1, NumTable[((RecordTableL[DataFlag]%10)%10)%10]);
			LCD1602_PutChar(11, 1, NumTable[RecordTableL[DataFlag]/10]);
		}
		delay_ms(5);						//避开抖动防止多次触发,期间继续显示计数时间
		while(!key2) 
			DisplayCountTime();					//等待按键释放,避开抖动防止多次触发,期间继续显示计数时间
	}
	if(key3 == 0)						//开始/停止计时
	{
		Counting = !Counting;		//切换计时状态并进行相应地动作
		if(Counting)
		{
			TR0 = 1;			//开启定时器计数,开始计时
		}else {
			TR0 = 0;			//关闭定时几计数,停止计时
		}
		delay_ms(5);						//避开抖动防止多次触发,期间继续显示计数时间
		while(!key3) 
			DisplayCountTime();					//等待按键释放,避开抖动防止多次触发,期间继续显示计数时间
	}
	if(key4 == 0)									//记录时间
	{
		if(Counting)			//在计时时才有用
		{
			DataRecordFlag++;
			DataFlag = DataRecordFlag;
			if(DataRecordFlag > 10)		//判断是否为第十条,第十条数据时进行记录,超过第十条时无动作需要复位
			{
				DataRecordFlag = 10;
				DataFlag = 10;
			}else {
				if(DataRecordFlag == 10)
				{
					RecordTableH[DataFlag] = CountTimeH;
					RecordTableL[DataFlag] = CountTimeL;
					LCD1602_PutString(0,1,"10:             ");
				}else {
					LCD1602_PutChar(0, 1, NumTable[DataRecordFlag]);
					LCD1602_PutString(1,1,":              ");
					RecordTableH[DataFlag] = CountTimeH;
					RecordTableL[DataFlag] = CountTimeL;
				}
			}
			
			LCD1602_PutChar(4, 1, NumTable[(RecordTableH[DataRecordFlag]/6000)%6]);		//显示记录的时间
			LCD1602_PutChar(5, 1, NumTable[(RecordTableH[DataRecordFlag]/600)%10]);
			LCD1602_PutChar(6, 1, ':');
			LCD1602_PutChar(7, 1, NumTable[(RecordTableH[DataRecordFlag]/100)%6]);
			LCD1602_PutChar(8, 1, NumTable[(RecordTableH[DataRecordFlag]/10)%10]);
			LCD1602_PutChar(9, 1, ':');
			LCD1602_PutChar(10, 1, NumTable[((RecordTableL[DataRecordFlag]%10)%10)%10]);
			LCD1602_PutChar(11, 1, NumTable[RecordTableL[DataRecordFlag]/10]);
		}
		delay_ms(5);						//避开抖动防止多次触发,期间继续显示计数时间
		while(!key4) 
		DisplayCountTime();					//等待按键释放,避开抖动防止多次触发,期间继续显示计数时间
	}
	if(key5 == 0)
	{
		if(!Counting)		//只有不在计时,并且有数据时才能复位(DataRecordFlag表示记录的数据条数)
		{
			CountTimeH=0;
			CountTimeL=0;
			DataFlag=0;
			DataRecordFlag=0;
			LCD1602_PutChar(4, 1, NumTable[(RecordTableH[DataRecordFlag]/6000)%6]);		//显示记录的时间
			LCD1602_PutChar(5, 1, NumTable[(RecordTableH[DataRecordFlag]/600)%10]);
			LCD1602_PutChar(6, 1, ':');
			LCD1602_PutChar(7, 1, NumTable[(RecordTableH[DataRecordFlag]/100)%6]);
			LCD1602_PutChar(8, 1, NumTable[(RecordTableH[DataRecordFlag]/10)%10]);
			LCD1602_PutChar(9, 1, ':');
			LCD1602_PutChar(10, 1, NumTable[((RecordTableL[DataRecordFlag]%10)%10)%10]);
			LCD1602_PutChar(11, 1, NumTable[RecordTableL[DataRecordFlag]/10]);
			LCD1602_Clear();				//清屏
			delay_ms(5);						
			while(!key5)      //避开抖动防止多次触发,期间继续显示计数时间
			DisplayCountTime();					//等待按键释放,避开抖动防止多次触发,期间继续显示计数时间
		}
	}
	
}
void DisplayCountTime(void)
{
		LCD1602_PutChar(4, 0, NumTable[(CountTimeH/6000)%6]);	//显示计时的时间
		LCD1602_PutChar(5, 0, NumTable[(CountTimeH/600)%10]);
		LCD1602_PutChar(6, 0, ':');
		LCD1602_PutChar(7, 0, NumTable[(CountTimeH/100)%6]);	//10s
		LCD1602_PutChar(8, 0, NumTable[(CountTimeH/10)%10]);	//s
		LCD1602_PutChar(9, 0, ':');
		LCD1602_PutChar(10, 0, NumTable[((CountTimeH%10)%10)%10]);	//CountTimeH   100ms 0.1s计时  最大3599
		LCD1602_PutChar(11, 0, NumTable[CountTimeL/10]);			//CountTimeL   1ms计时   最大100ms  /10 10ms  0.01s
}
void timer0() interrupt 1
{
	TH0=0xFC; 				//定时1ms
	TL0=0x18;
	
	if(!(CountTimeL == 99 && CountTimeH == 6000))//判断是否到达计时最大值,到达09:59:99时无动作并且退出计时状态(毫秒的进制为100)
	{
		CountTimeL++;
		if(CountTimeL > 99)		//毫秒的计数时间到达100时进1到秒 10000us=10ms=0.01s
		{
			CountTimeL = 0;
			CountTimeH++;		//0.01s
			buzzer++;
			if(buzzer==10)      //蜂鸣器每秒响一次
			{
				if(key6==0)
				{
					Beep=0;
					delay_ms(100);
					Beep=1;
				}
				buzzer=0;
			}
			if(CountTimeH > 6000)		//设定计时最大值
	 		{
				CountTimeH = 6000;
				Counting = 0;
			}
		}
	}else {
		Counting = 0;		//计时标志位,为1时才开始计时
		Beep = 0;				//蜂鸣器响一下
		delay_ms(5);
		Beep = 0;
	}
}

Proteus仿真提取链接

链接:https://pan.baidu.com/s/14fx9nKl5zEEOhQ0Kd2dftAicon-default.png?t=M3K6https://pan.baidu.com/s/14fx9nKl5zEEOhQ0Kd2dftA
提取码:5fdf


版权声明:本文为weixin_57674408原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。