课程名称: 单片机原理与应用 设计题目: LCD电子钟
院 系: 电子信息工程学院
班 级: 自动化0706 设 计 者: 全宏宇 指导教师: 一,设计目标
LCD显示电子钟的基本功能
1,实现时钟功能;
2,实现闹铃功能;
3,实现秒表功能;
4,具有一定的计时精度。
LCD显示电子钟的基本要求
1,掌握单片机开发编程设计的基本流程;
2,了解Keil及Proteus软件的基本使用;
3,了解LCD的基本使用;
4,学习单片机硬件制作。 二,具体实现 1,软件平台
1)Keil编程
Keil 的开发工具的使用的基本过程:
1创建 C 或汇编语言的源程序; ○
2编译或汇编源文件; ○
3纠正源文件中的错误; ○
4从编译器和汇编器连接目标文件; ○
5测试连接的应用程序。 ○
2)Proteus仿真
Proteus软件不仅具有其它EDA工具软件的仿真功能,还能仿真单片机及外围器件。它是目前最好的仿真单片机及外围器件的工具。在编译方面,它也支持IAR,Keil,PLAB等多种编译器。
2,硬件开发平台
JD51开发板的基本资料
1 LED 电路,8 只独立LED 发光管,可做指示或各种闪烁效果用。 ○
2 数码管电路,○4只共阳一体8 段数码显示管,可实现各种数据显示,如计数、时钟等。
3蜂鸣器电路,可用于设计各种提示音、演奏音乐等。 ○
4键盘电路,学习按键控制相关编程。 ○
5 LCD 显示电路,编程控制LCD 显示。 ○
6串口电路,学习编程实现JD51 和PC 或其他符合该通信协议的电路之间的通信。 ○
7红外电路,通过选配的红外遥控器,学习红外解码并可实现红外遥控JD51。 ○
8 温度模块电路,采用一线式温度传感器实现温度的采集并可显示在数码管或者LCD ○
上,通过温度数据处理便可实现温度控制器功能。
9除了以上提到的可编程电路本学习板还有一些常用的不可编程电路,包括电源电路、○
复位电路、晶振电路等。
本次LCD电子钟实验用到其中的蜂鸣器,按键,LCD显示接口。
3,总体设计
1) 基本资源的使用
本次实验采用了89C52型单片机,1602LCD液晶显示屏,蜂鸣器。为了实现时钟,定时,闹钟,秒表的功能,用到了单片机的外部中断,计时器中断,及
I/O端口.
资源 功能 外部中断 外部中断INT1 模式选择(时钟,调时,秒表,闹钟) 外部中断INT0 秒表的暂停,清零 定时器 定时器T0 秒表,时钟定时基准 定时器T1 音乐闹铃的音符产生 I/O端口 P1,P2 LCD接口,蜂鸣器 2)软件仿真
(1)程序设计的一些关键问题(具体参照后面的程序清单)
1建立Keil工程时,注意对程序编译环境进行设置; ○
设置内容包括器件,频率,产生hex文件等。
2LCD管脚及控制字; ○
LCD管脚的定义
控制字
1602液晶模块的读写操作,屏幕和光标的操作都是通过指令编程来实现的。
指令1:清显示,指令码01H,光标复位到地址00H位置
指令2:光标复位,光标返回到地址00H
指令3:光标和显示位置设置I/D,光标移动方向,高电平右移,低电平左移,S:屏幕上所有文字是否左移或右移,高电平表示有效,低电平表示无效。
指令4:显示开关控制。D:控制整体的显示开与关,高电平表示开显示,低电平表示关显示。C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。
指令5:光标或显示移位 S/C :高电平时显示移动的文字,低电平时移动光标
指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时为双行显示,F:低电平时显示5X7的点阵字符,高电平时显示5X10的显示字符。
指令7:字符发生器RAM地址设置。
指令8:DDRAM地址设置。
指令9:读忙信号和光标地址 BF:忙标志位,高电平表示忙,此时模块不能接收命令或数据,如果为低电平表示不忙。
3LCD实现滚动显示; ○
方法一:采用LCD1602控制命令,显示后画面移动一个字符;
方法二:循环使用显示不同位置,形成滚动效果。
5秒表 ○
主要是产生计时基准0.01秒,有了基准之后需要解决的问题是如何实现有效的暂停,清零(达到精准动作),采用中断是很不错的结果。
6闹铃 ○
主要是如何实现闹铃关闭的逻辑,if(f&&g)语句可以实现这一动作。
7提高计时精度 ○
本程序除了调时模式外,其他模式的时候时钟都在跑动,提高了因模式转换而导致的时间精度降低。
(2)Proteus仿真
在Proteus中画出如下电路图,添加hex文件,进行虚拟仿真。
2) 开发板运行调试
在JD51板上烧制程序,根据实际的效果,对中断的反应时间,显示效果做出了调
整,整体效果不错,只是音乐的效果不佳,主要是由于如果将蜂鸣器换成扬声器效果会好些,还可以在程序中增加节拍来增加音乐效果。 4,硬件制作
根据仿真电路图购买元件进行焊接,下载程序效果后,液晶显示器显示乱码,检查电路后已无连接错误,但功能依然没有实现,相关检查在进行中。 三,设计体会
本次课程设计系统地学习了单片编程开发及实际电路的制作。学习了Keil的C语言编程的基本知识,Proteus仿真软件的模拟仿真,JD51开发实验板的使用,本次课设,时间了较多的其他案例,比较全面的掌握了单片机的运营,包括编程,电路,LCD器件等,在知识上有了很多的长进。
本次课程设计还有很多其他收获,锻炼了自学的能力,特别是动手实践方面的东西,让我深深感受到理论与实际的差距,实际当中会有一些小的问题,这些问题是意想不到的,需要去思考,去解决,这是很锻炼人的。
四,程序
#include #include #define uchar unsigned char #define ulong unsigned long #define uint unsigned int uchar idata welcome[16]={\" clock \ //定义第一行显示数组 uchar idata lwelcome[16]={\" clock \//定义第一行显示数组 uchar idata llwelcome[16]={\" clock \uchar idata lllwelcome[16]={\" clock \uchar idata sslcdtimer[16]={\" 00:00:00 \uchar idata tsslcdtimer[16]={\" 00:00:00 \uchar idata lcdtimer[16]={\" 00:00:00 \uchar idata mlcdtimer[16]={\" 00: : \uchar idata hlcdtimer[16]={\" :00: \uchar idata setlcdtimer[16]={\" 00:01:00 \uchar timecount; uchar temps; bit timeflag; bit sstimeflag; sbit ring=P2^0;//蜂鸣闹铃 //定义第一行显示数组 //定义第一行显示数组 //定义第二行显示数组 //定义第二行显示数组 //定义第二行显示数组 sbit e=P2^7;//LCD显示器E sbit rw=P2^6;//LCD显示器RW sbit rs=P2^5;//LCD显示器RS sbit P10=P1^0; //测试用闪灯 sbit Key=P3^2;//秒表暂停,清零 sbit Key0=P3^3;//调时,设定闹钟时间,秒表选择 sbit Key1=P3^4;//调时,设定闹钟时间改变 uchar th1_f; //在中断中装载的T0的值高8位 uchar tl1_f; //在中断中装载的T0的值低8位 uint m=0; uint n=0; ulong i; uint f=0,g=1; uint j,k; uchar code freq[3][14]= { {0xF2,0x42, 0xF3,0xC1, 0xF5,0x17, 0xF5,0xB6, 0xF6,0xD0, 0xF7,0xD1, 0xF8,0xB6}, {0xF9,0x21, 0xF9,0xE0, 0xFA,0x8B, 0xFA,0xD7, 0xFB,0x68, 0xFB,0xE8, 0xFC,0x5B }, {0xFC,0x8D, 0xFC,0xEE, 0xFD,0x44, 0xFD,0x6D, 0xFD,0xB4, 0xFD,0xF4, 0xFE,0x2D}, }; //音乐频率表,低音,中音,高音,系统振荡频率11.0592MHz uint code song1[]={26,25,23,25,31,26,25,26,23,25,26,25,23,22,21,16,25,23,22,22,23,23,23,26,23,22,21,25,23,22,21,16,21,15,26,25,23,25,31,26,25,26,23,25,26,25,23,22,21,16,25,23,22,22,23,23,23,26,23,22,21,25,23,22,21,16,21,15}; //编制音乐 //************************************************** //软件延时函数(系统晶振为11.0592MHz),延时时间约为40μs //入口条件:无符号整型变量del1,最终的延时时间为(del1*40)μs //************************************************** void delay(uint del1) { uchar del2; for(;del1>0;del1--) for(del2=10;del2>0;del2--); } //************************************************** //液晶显示器判忙函数 //************************************************** void busy() { uchar temp; temp=0x00; rs=0; rw=1; while((temp&0x80)==0x80) { P0=0xff; e=1; temp=P0; e=0; } } //************************************************** //向液晶显示器写命令函数 //************************************************** void WR_Com(uchar temp) { busy(); rs=0; rw=0; P0=temp; e=1; e=0; } //************************************************** //向液晶显示器写数据函数 //************************************************** void WR_Data(uchar num) { busy(); rs=1; rw=0; P0=num; e=1; e=0; } //************************************************** //向液晶显示器写入显示数据函数 //入口条件:液晶显示器行首地址(指示第一行还是第二行)和待显示数组的首地址 //************************************************** //void disp_lcdchar(uchar addr,uchar temp1) //{ // WR_Com(addr); // delay(100); // WR_Data(temp1); //} void disp_lcd(uchar addr,uchar *temp1) { uchar i; WR_Com(addr); delay(100); for(i=0;i<16;i++) { WR_Data(temp1[i]); delay(100); } } //************************************************** //液晶显示器初始化函数 //************************************************** void lcd_ini() { char i; for(i=3;i>0;i--) { P0=0x30; rs=0; rw=0; e=1; e=0; delay(100); } P0=0x38; rs=0; rw=0; e=1; e=0; delay(100); } //************************************************** //液晶显示器复位函数 //************************************************** void lcd_Reset() { WR_Com(0x01); delay(100); WR_Com(0x06); delay(100); WR_Com(0x0c); delay(100); } //************************************************** //外部中断函数,秒表暂停,清零 //************************************************** void modess() interrupt 0 using 0 { delay(10000); n++; if(n==3) n=0; } //************************************************** //外部中断函数,模式选择 //************************************************** void modepp() interrupt 2 using 1 { delay(30000); m++; if(m==6) n=0; if(m==7) m=0; } //************************************************** //定时/计数器中断函数,1S及0.01S //************************************************** void timer0() interrupt 1 using 2 { TH0=0xdc; TL0=0x23; sstimeflag=1; if(--timecount==0) { timecount=100; timeflag=1; } } //************************************************** //定时/计数器中断函数,装入音乐频率计数初值 //************************************************** void timer1() interrupt 3 using 3 { TL1=tl1_f;TH0=th1_f; //调入预定时值 ring=!ring; //取反音乐输出IO } //************************************************** //主函数 //************************************************** void main() { P10=1; lcd_ini(); lcd_Reset(); /* for(i=0;i<10;i++) { temps=welcome[i]; disp_lcdchar(0x80+1,temps); //LCD 单字符从左到右显示 delay(10000); }*/ disp_lcd(0x80,welcome); delay(10000); disp_lcd(0x80,lwelcome); delay(10000); disp_lcd(0x80,llwelcome); delay(10000); disp_lcd(0x80,lllwelcome); delay(10000); disp_lcd(0x80,llwelcome); delay(10000); disp_lcd(0x80,lwelcome); delay(10000); disp_lcd(0x80,welcome); //LCD滚动显示 delay(10000); disp_lcd(0xc0,lcdtimer); P10=0; TMOD=0x11; TH0=0xdc; TL0=0x23; TR0=1; IE=0x86; timeflag=0; //1秒计时标志 sstimeflag=0; //0.01秒计时标志 timecount=100; while(1) { while(m==6)//秒表 { IE=0x87; if(n==0) //秒表跑动 { if(sstimeflag==1) { sstimeflag=0; sslcdtimer[11]+=1; if(sslcdtimer[11]>=0x3a) { sslcdtimer[11]=0x30; sslcdtimer[10]+=1; if(sslcdtimer[10]>=0x3a) { sslcdtimer[10]=0x30; sslcdtimer[8]+=1; if(sslcdtimer[8]>=0x3a) { sslcdtimer[8]=0x30; sslcdtimer[7]+=1; if(sslcdtimer[7]>=0x36) { sslcdtimer[7]=0x30; } } } } disp_lcd(0xc0,sslcdtimer); } if(n==1)//秒表暂停 { disp_lcd(0xc0,sslcdtimer); } if(n>=2)//秒表清零 { disp_lcd(0xc0,tsslcdtimer); for(i=0;i<16;i++) sslcdtimer[i]=tsslcdtimer[i]; } } } while(m==0||m>=3) //时钟程序,只要不是调时,就一直运行 { IE=0x86; if(timeflag==1) { timeflag=0; lcdtimer[11]+=1; if(lcdtimer[11]>=0x3a) { lcdtimer[11]=0x30; lcdtimer[10]+=1; if(lcdtimer[10]>=0x36) { lcdtimer[10]=0x30; lcdtimer[8]+=1; if(lcdtimer[8]>=0x3a) { lcdtimer[8]=0x30; lcdtimer[7]+=1; if(lcdtimer[7]>=0x36) { lcdtimer[7]=0x30; lcdtimer[5]+=1; if(lcdtimer[5]>=0x3a) { lcdtimer[5]=0x30; lcdtimer[4]+=1; if(lcdtimer[4]>=0x33&&lcdtimer[5]>=0x35) { lcdtimer[5]=0x30; lcdtimer[4]=0x30; } } } } if(m==0) { } } } disp_lcd(0xc0,lcdtimer); if(!Key1)//关掉闹钟 { g=0; } if(lcdtimer[8]==setlcdtimer[8]&&lcdtimer[7]==setlcdtimer[7]&&lcdtimer[5]==setlcdtimer[5]&&lcdtimer[4]==setlcdtimer[4]) f=1; //开启闹钟 ,播放音乐 if(f&&g) { /*ring=1; delay(10000); IE=0x8e; k=2*(song1[i]%10-1); th1_f=freq[j][k]; //定时值送给T0 tl1_f=freq[j][++k]; TR1=1; ring=0; f=0; */ j=song1[i]/10-1; 取出对应的 for(n=0;n<50000;n++); //音符间延时 } } } //调时分 { if(!Key1) { delay(10000); timeflag=0; lcdtimer[8]+=1; if(lcdtimer[8]>=0x3a) { lcdtimer[8]=0x30; i++; TR1=0; f=0; while(m==1) lcdtimer[7]+=1; if(lcdtimer[7]>=0x36) { lcdtimer[7]=0x30; } } } disp_lcd(0xc0,lcdtimer); delay(8000); mlcdtimer[11]=lcdtimer[11]; mlcdtimer[10]=lcdtimer[10]; mlcdtimer[5]=lcdtimer[5]; mlcdtimer[4]=lcdtimer[4]; disp_lcd(0xc0,mlcdtimer); } while(m==2) //调时时 { if(!Key1) { delay(10000); lcdtimer[5]+=1; if(lcdtimer[5]>=0x3a) { lcdtimer[5]=0x30; lcdtimer[4]+=1; } if(lcdtimer[4]==0x32&&lcdtimer[5]>=0x34) { lcdtimer[5]=0x30; lcdtimer[4]=0x30; } } disp_lcd(0xc0,lcdtimer); delay(8000); hlcdtimer[11]=lcdtimer[11]; hlcdtimer[10]=lcdtimer[10]; hlcdtimer[8]=lcdtimer[8]; hlcdtimer[7]=lcdtimer[7]; disp_lcd(0xc0,hlcdtimer); } while(m==3)//显示闹铃时间 { if(!Key1) { delay(10000); } disp_lcd(0xc0,setlcdtimer); } while(m==4) //调闹铃分 { if(!Key1) { delay(10000); setlcdtimer[8]+=1; if(setlcdtimer[8]>=0x3a) { setlcdtimer[8]=0x30; setlcdtimer[7]+=1; if(setlcdtimer[7]>=0x36) { setlcdtimer[7]=0x30; } } } disp_lcd(0xc0,setlcdtimer); delay(8000); mlcdtimer[11]=setlcdtimer[11]; mlcdtimer[10]=setlcdtimer[10]; mlcdtimer[5]=setlcdtimer[5]; mlcdtimer[4]=setlcdtimer[4]; disp_lcd(0xc0,mlcdtimer); } while(m==5)//调闹铃时 { if(!Key1) { delay(10000); setlcdtimer[5]+=1; if(setlcdtimer[5]>=0x3a) { setlcdtimer[5]=0x30; setlcdtimer[4]+=1; } if(setlcdtimer[4]==0x32&&setlcdtimer[5]>=0x34) { setlcdtimer[5]=0x30; setlcdtimer[4]=0x30; } } disp_lcd(0xc0,setlcdtimer); delay(8000); hlcdtimer[11]=setlcdtimer[11]; hlcdtimer[10]=setlcdtimer[10]; hlcdtimer[8]=setlcdtimer[8]; hlcdtimer[7]=setlcdtimer[7]; disp_lcd(0xc0,hlcdtimer); } } } 因篇幅问题不能全部显示,请点此查看更多更全内容