目录
总体设计
晶体震荡电路
复位电路
按键控制电路
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引脚分别为振荡器的反相放大器输入端,振荡器的反相放大输出端,再接上晶振形成一个完整电路,产生震荡。
复位电路采用上电加按键复位电路,也就是手动复位,上电后,由于电容充电,使RET持续一段高电平时间,当单片机运行时,按下复位键也能使RET持续一段时间的高电平,从而实现上电加开关复位的操作上电加复位电路。
按键控制电路需要实现秒表的开始/暂停、记录、查看上一条、查看下一条、复位、每秒提醒的开关。用6个按键实现。P1.1控制开始/暂停、P1.2、P1.3控制查看上一条、下一条、P1.4控制记录、P1.5控制复位、P1.6控制每秒提醒。
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=高脉冲
--输入:无
蜂鸣器电路
蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。由于题目内容有计时时间每秒都得有提醒功能,因此我们还得用蜂鸣器。如图所示:
Proteus仿真图调试结果
源代码:
#includeProteus仿真提取链接#include //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(); } 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; } }
链接:https://pan.baidu.com/s/14fx9nKl5zEEOhQ0Kd2dftAhttps://pan.baidu.com/s/14fx9nKl5zEEOhQ0Kd2dftA
提取码:5fdf



