基于51单片机的秒表系统设计(源代码+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控制每秒提醒。


三、原理图


四、硬件电路设计

4.1 晶体震荡电路

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

4.2 按键控制电路

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

4.3 LCD1602显示电路

  所谓的1602是指显示的时候,有两行内容每行有16个字符。
  基本操作时序:
    读状态:输入:RS=L,RW=H,E=H
         --输出:D0-D7=状态字
    写指令:输入:RS=L,RW=L,D0-D7=指令码
         --输出:无
    读数据:输入:RS=H,RW=H,E=H
         --输入:D0-D7=数据
    写数据:输入:RS=H,RW=L,D0-D7=数据,E=高脉冲
         --输入:无

4.4蜂鸣器电路

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


五、代码实现

LCD1602部分代码

/*------------------------------------------------
              写入命令函数
------------------------------------------------*/
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 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 IniTC0(void)        
{
	TMOD=0x01;		//设置定时计数器的工作模式
	TR0 = 0;			//关闭定时计数器
	TF0 = 0;			//清空定时计数器标志位
	
	TH0=0xFC; 				//定时1ms,当计时时间不准在这修改
	TL0=0x18;
	
	ET0 = 1;			//开启定时计数器0中断
	EA=1; 				//开总中断
}

主函数代码

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();
	}
}

仿真、源代码

 🔓链接:仿真源代码网盘
 🔑 提取码:5fdf


文章转载自haohao_不秃头

  • 8
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
单片机C语言程序设计实训100例是一种基于AVR Proteus仿真的学习材料,旨在帮助学习者掌握单片机的编程技能。这份材料提供了100个实例,每个实例包含了相关的源代码。 这些实例涵盖了单片机的各个方面,包括输入输出、中断、定时器、串口通信等。每个实例都具有一定的难度,从基础到高级不等,可以逐步提升程序设计的水平。 通过使用AVR Proteus仿真,学习者可以在计算机上进行程序的编写、调试和仿真测试。这种仿真环境可以模拟真实的硬件运行情况,使学习者可以在不使用实际硬件的情况下进行程序的开发和测试,提高学习的便利性和效率。 每个实例的源代码都是以C语言编写的,学习者可以通过阅读和理解源代码来学习相关的编程技巧和知识。在编程过程中,学习者需要了解单片机的工作原理、寄存器的使用方法、中断的实现等相关知识,这有助于他们深入理解单片机的工作机制和编程方法。 通过完成这100个实例的编程练习,学习者不仅可以熟悉单片机的编程方法,还可以提高解决问题的能力和动手实践的能力。同时,学习者还可以通过仿真结果来验证自己的程序设计是否正确,从中发现和解决问题,提高自己的调试和排错能力。 综上所述,单片机C语言程序设计实训100例基于AVR Proteus仿真源代码,是一份帮助学习者掌握单片机编程技能的学习材料。通过完成这份实验,学习者可以提高自己的编程水平和实践能力。
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值