单片机课设-基于51单片机的秒表系统设计
前言
本文是为了记录一下单片机课程设计, 帮助以后有需要的学弟学妹能够顺利的度过课程设计,开的专栏,不定期免费更新,专栏地址如下:
📎 单片机课程设计专栏(完整代码)
专栏中的源代码和仿真均放在文末,需要自取。
一、课设任务是什么?
二、总体设计
本设计是采用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_不秃头