单片机电子时钟设计报告一、设计任务本次课程设计的电子时钟电路,是基于单片机STC89C52、时钟芯片和液晶显示,运用C语言编程实现。
电子时钟可以显示日期的年、月、日和时间的时、分、秒,具有复位功能。
二、系统硬件设备及芯片简介数字电子钟系统设计已经成熟,但是目前系统设计时基本都是采用LED作为显示电路,造成硬件电路复杂、功耗高、产品体积庞大等特点;液晶显示模块由于具有低功耗、寿命长、体积小、显示容丰富、价格低、接口控制方便等优点,因此在各类电子产品中被极广泛地推广和应用。
字符型液晶显示模块是一类专门用于显示字母、数字、符号等点阵式液晶显示模块。
本系统设计采用字符型液品显示模块LCD1602 作为显示器件,这样不仅简化了系统的硬件设计,而且极提高了系统的可靠性。
1 LCD1602简介字符型液晶显示模块LCD1602已经是单片机应用设计中最常用的信息显示器件。
LCD1602可以显示两行,每行16个字符,采用+5V电源供电,外围电路配置简单,价格便宜,具有很高的性价比。
2 LCD1602功能介绍2.1 引脚功能LCD1602采用标准14脚(无背光)或16脚(带背光)接口,各引脚功能见表1。
表1 引脚功能2.2 LCD1602读写指令LCD1602读写指令较多且较复杂,具体使用可以查相关资料,下面仅列出最常用的的一些命令:①写指令38H:显示模式设置;②写指令08H:显示关闭;③写指令01H:显示清屏;④写指令06H:显示光标移动设置;⑤写指令0CH:显示开及光标设置。
2.3 LCD1602 读写操作时序LCD1602 读写操作时序总体上来说是比较简单的,掌握其有两种方法:一种是只看时序图,另外一种方法是直接记忆和总结读写时电平高低和变化。
很显然第二种更简单和直接,下面就列出典型读写的时序要求,以方便编写程序。
(1)读状态:输入:RS=L,RW=H,E=H。
输出:D0-D7=状态字。
(2)写指令:输入:RS=L,RW=L,D0-D7=指令码,E=上升沿。
输出:无。
(3)读数据:输入:RS=H,RW=H,E=H。
输出:D0-D7=数据。
(4)写数据:输入:RS=H,RW=L,D0-D7=数据,E=上升沿。
输出:无。
2.4 LCD1602显示方法液晶显示模块是慢速显示器件,所以在执行每条指令之前一定要确认模块的忙标志为低电平(即不忙),否则该指令失效。
显示字符时,要先输入显示字符地址,即告诉模块在哪里显示字符。
因为写入显示地址时要求最高位D7 恒定为高电平,所以实际写入的数据应该是要显示地址值加上80H,即将最高位D7置为1。
在使用此显示模块时一般要对其进行初始化,设置所需要的显示参数。
液晶模块在显示字符时光标是自动右移的,无需人工干预。
每次输入指令前,都要判断液晶模块是否处于忙状态。
3 数字电子钟硬件电路设计硬件电路系统设计主要由单片机最小系统、输入电路、输出电路等组成。
单片机最小系统同所有单片机系统,在此不作讨论。
输入电路主要时间调节电路,为简化系统我们使两个外部中断来调节“时”和“分”数值,即将INT0、INT1分别接两个按钮。
LCD显示器和单片机接口电路可以采用总线方式或者是模拟口线方式,本设计采用第二种方式,即以单片机I/O 模拟控制信号。
具体电路见图1,P0口作为数据,并接上拉电阻提升电压,P2.0接LCD的RS端、P2.1 接LCD的R/W端、P2.2接LCD的E端,“分”调节按钮接INT0,“时”调节按钮接INT1,图1 省略了电源和单片机最小系统电路。
图1 数字电子钟硬件电路三、设计电路图及程序1.电路图如下原理图如下:2.程序共有四段程序,用Keil仿真是程序列表如图Regx52.h程序为:#ifndef __AT89X52_H__#define __AT89X52_H__sfr P0 = 0x80;sfr SP = 0x81; sfr DPL = 0x82; sfr DPH = 0x83; sfr PCON = 0x87; sfr TCON = 0x88; sfr TMOD = 0x89; sfr TL0 = 0x8A;sfr TL1 = 0x8B;sfr TH0 = 0x8C; sfr TH1 = 0x8D; sfr P1 = 0x90;sfr SCON = 0x98; sfr SBUF = 0x99; sfr P2 = 0xA0; sfr IE = 0xA8;sfr P3 = 0xB0; sfr IP = 0xB8;sfr T2CON = 0xC8;sfr T2MOD = 0xC9;sfr RCAP2L = 0xCA;sfr RCAP2H = 0xCB;sfr TL2 = 0xCC;sfr TH2 = 0xCD;sfr PSW = 0xD0;sfr ACC = 0xE0;sfr B = 0xF0;/*------------------------------------------------ P0 Bit Registers------------------------------------------------*/ sbit P0_0 = 0x80;sbit P0_1 = 0x81;sbit P0_2 = 0x82;sbit P0_3 = 0x83;sbit P0_4 = 0x84;sbit P0_5 = 0x85;sbit P0_6 = 0x86;sbit P0_7 = 0x87;/*------------------------------------------------PCON Bit Values------------------------------------------------*/ #define IDL_ 0x01#define STOP_ 0x02#define PD_ 0x02 /* Alternate definition */#define GF0_ 0x04#define GF1_ 0x08#define SMOD_ 0x80/*------------------------------------------------ TCON Bit Registers------------------------------------------------*/ sbit IT0 = 0x88;sbit IE0 = 0x89;sbit IT1 = 0x8A;sbit IE1 = 0x8B;sbit TR0 = 0x8C;sbit TF0 = 0x8D;sbit TR1 = 0x8E;sbit TF1 = 0x8F;/*------------------------------------------------ TMOD Bit Values------------------------------------------------*/ #define T0_M0_ 0x01#define T0_M1_ 0x02#define T0_CT_ 0x04#define T0_GATE_ 0x08#define T1_M0_ 0x10#define T1_M1_ 0x20#define T1_CT_ 0x40#define T1_GATE_ 0x80#define T1_MASK_ 0xF0#define T0_MASK_ 0x0F/*------------------------------------------------ P1 Bit Registers------------------------------------------------*/ sbit P1_0 = 0x90;sbit P1_1 = 0x91;sbit P1_2 = 0x92;sbit P1_3 = 0x93;sbit P1_4 = 0x94;sbit P1_5 = 0x95;sbit P1_6 = 0x96;sbit P1_7 = 0x97;sbit T2 = 0x90; /* External input to Timer/Counter 2, clock out */ sbit T2EX = 0x91; /* Timer/Counter 2 capture/reload trigger & dir ctl *//*------------------------------------------------SCON Bit Registers------------------------------------------------*/sbit RI = 0x98;sbit TI = 0x99;sbit RB8 = 0x9A;sbit TB8 = 0x9B;sbit REN = 0x9C;sbit SM2 = 0x9D;sbit SM1 = 0x9E;sbit SM0 = 0x9F;/*------------------------------------------------P2 Bit Registers------------------------------------------------*/ sbit P2_0 = 0xA0;sbit P2_1 = 0xA1;sbit P2_2 = 0xA2;sbit P2_3 = 0xA3;sbit P2_4 = 0xA4;sbit P2_5 = 0xA5;sbit P2_6 = 0xA6;sbit P2_7 = 0xA7;/*------------------------------------------------ IE Bit Registers------------------------------------------------*/ sbit EX0 = 0xA8; /* 1=Enable External interrupt 0 */ sbit ET0 = 0xA9; /* 1=Enable Timer 0 interrupt */ sbit EX1 = 0xAA; /* 1=Enable External interrupt 1 */ sbit ET1 = 0xAB; /* 1=Enable Timer 1 interrupt */ sbit ES = 0xAC; /* 1=Enable Serial port interrupt */ sbit ET2 = 0xAD; /* 1=Enable Timer 2 interrupt */sbit EA = 0xAF; /* 0=Disable all interrupts *//*------------------------------------------------P3 Bit Registers (Mnemonics & Ports)------------------------------------------------*/ sbit P3_0 = 0xB0;sbit P3_1 = 0xB1;sbit P3_2 = 0xB2;sbit P3_3 = 0xB3;sbit P3_4 = 0xB4;sbit P3_5 = 0xB5;sbit P3_6 = 0xB6;sbit P3_7 = 0xB7;sbit RXD = 0xB0; /* Serial data input */sbit TXD = 0xB1; /* Serial data output */sbit INT0 = 0xB2; /* External interrupt 0 */sbit INT1 = 0xB3; /* External interrupt 1 */sbit T0 = 0xB4; /* Timer 0 external input */sbit T1 = 0xB5; /* Timer 1 external input */sbit WR = 0xB6; /* External data memory write strobe */ sbit RD = 0xB7; /* External data memory read strobe */IP Bit Registers------------------------------------------------*/sbit PX0 = 0xB8;sbit PT0 = 0xB9;sbit PX1 = 0xBA;sbit PT1 = 0xBB;sbit PS = 0xBC;sbit PT2 = 0xBD;/*------------------------------------------------T2CON Bit Registers------------------------------------------------*/sbit CP_RL2= 0xC8; /* 0=Reload, 1=Capture select */sbit C_T2 = 0xC9; /* 0=Timer, 1=Counter */sbit TR2 = 0xCA; /* 0=Stop timer, 1=Start timer */sbit EXEN2= 0xCB; /* Timer 2 external enable */sbit TCLK = 0xCC; /* 0=Serial clock uses Timer 1 overflow, 1=Timer 2 */ sbit RCLK = 0xCD; /* 0=Serial clock uses Timer 1 overflow, 1=Timer 2 */ sbit EXF2 = 0xCE; /* Timer 2 external flag */sbit TF2 = 0xCF; /* Timer 2 overflow flag */T2MOD Bit Values------------------------------------------------*/#define DCEN_ 0x01 /* 1=Timer 2 can be configured as up/down counter */ #define T2OE_ 0x02 /* Timer 2 output enable *//*------------------------------------------------PSW Bit Registers------------------------------------------------*/sbit P = 0xD0;sbit FL = 0xD1;sbit OV = 0xD2;sbit RS0 = 0xD3;sbit RS1 = 0xD4;sbit F0 = 0xD5;sbit AC = 0xD6;sbit CY = 0xD7;/*------------------------------------------------Interrupt Vectors:Interrupt Address = (Number * 8) + 3------------------------------------------------*/#define IE0_VECTOR 0 /* 0x03 External Interrupt 0 */#define TF0_VECTOR 1 /* 0x0B Timer 0 */#define IE1_VECTOR 2 /* 0x13 External Interrupt 1 */#define TF1_VECTOR 3 /* 0x1B Timer 1 */#define SIO_VECTOR 4 /* 0x23 Serial port */#define TF2_VECTOR 5 /* 0x2B Timer 2 */#define EX2_VECTOR 5 /* 0x2B External Interrupt 2 */#endifLCD1602驱动程序为:#ifndef LCD_CHAR_1602_2005_4_9#define LCD_CHAR_1602_2005_4_9#include <intrins.h>//PortDefinitions**********************************************************sbit LcdRs = P2^0;sbit LcdRw = P2^1;sbit LcdEn = P2^2;sfr DBPort = 0x80; //P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口//部等待函数********************************************************************* *****unsigned char LCD_Wait(void){LcdRs=0;LcdRw=1; _nop_();LcdEn=1; _nop_();//while(DBPort&0x80);//在用Proteus仿真时,注意用屏蔽此语句,在调用GotoXY()时,会进入死循环,//可能在写该控制字时,该模块没有返回写入完备命令,即DBPort&0x80==0x80//实际硬件时打开此语句LcdEn=0;return DBPort;}//向LCD写入命令或数据************************************************************ #define LCD_MAND 0 // mand#define LCD_DATA 1 // Data#define LCD_CLEAR_SCREEN 0x01 // 清屏#define LCD_HOMING 0x02 // 光标返回原点void LCD_Write(bit style, unsigned char input){LcdEn=0;LcdRs=style;LcdRw=0; _nop_();DBPort=input; _nop_();//注意顺序LcdEn=1; _nop_();//注意顺序LcdEn=0; _nop_();LCD_Wait();}//设置显示模式************************************************************ #define LCD_SHOW 0x04 //显示开#define LCD_HIDE 0x00 //显示关#define LCD_CURSOR 0x02 //显示光标#define LCD_NO_CURSOR 0x00 //无光标#define LCD_FLASH 0x01 //光标闪动#define LCD_NO_FLASH 0x00 //光标不闪动void LCD_SetDisplay(unsigned char DisplayMode){LCD_Write(LCD_MAND, 0x08|DisplayMode);}//设置输入模式************************************************************ #define LCD_AC_UP 0x02#define LCD_AC_DOWN 0x00 // default#define LCD_MOVE 0x01 // 画面可平移#define LCD_NO_MOVE 0x00 //defaultvoid LCD_SetInput(unsigned char InputMode){LCD_Write(LCD_MAND, 0x04|InputMode);}//移动光标或屏幕************************************************************/*#define LCD_CURSOR 0x02#define LCD_SCREEN 0x08#define LCD_LEFT 0x00#define LCD_RIGHT 0x04void LCD_Move(unsigned char object, unsigned char direction){if(object==LCD_CURSOR)LCD_Write(LCD_MAND,0x10|direction);if(object==LCD_SCREEN)LCD_Write(LCD_MAND,0x18|direction);}*///初始化LCD************************************************************ void LCD_Initial(){LcdEn=0;LCD_Write(LCD_MAND,0x38); //8位数据端口,2行显示,5*7点阵LCD_Write(LCD_MAND,0x38);LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR); //开启显示, 无光标LCD_Write(LCD_MAND,LCD_CLEAR_SCREEN); //清屏LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE); //AC递增, 画面不动}//******************************************************************** ****void GotoXY(unsigned char x, unsigned char y){if(y==0)LCD_Write(LCD_MAND,0x80|x);if(y==1)LCD_Write(LCD_MAND,0x80|(x-0x40));}void Print(unsigned char *str){while(*str!='\0'){LCD_Write(LCD_DATA,*str);str++;}}/*void LCD_LoadChar(unsigned char user[8], unsigned char place){unsigned char i;LCD_Write(LCD_MAND,0x40|(place*8));for(i=0; i<8; i++)LCD_Write(LCD_DATA,user[i]);}*///******************************************************************** ****#endifIntrins.h程序为:#ifndef __INTRINS_H__#define __INTRINS_H__extern void _nop_ (void);extern bit _testbit_ (bit);extern unsigned char _cror_ (unsigned char, unsigned char); extern unsigned int _iror_ (unsigned int, unsigned char); extern unsigned long _lror_ (unsigned long, unsigned char); extern unsigned char _crol_ (unsigned char, unsigned char); extern unsigned int _irol_ (unsigned int, unsigned char); extern unsigned long _lrol_ (unsigned long, unsigned char); extern unsigned char _chkfloat_(float);extern void _push_ (unsigned char _sfr);extern void _pop_ (unsigned char _sfr);#endifDS1302时钟程序为:#ifndef _REAL_TIMER_DS1302_2003_7_21_#define _REAL_TIMER_DS1302_2003_7_21_sbit DS1302_CLK = P1^6; //实时时钟时钟线引脚sbit DS1302_IO = P1^7; //实时时钟数据线引脚sbit DS1302_RST = P1^5; //实时时钟复位线引脚sbit ACC0 = ACC^0;sbit ACC7 = ACC^7;typedef struct __SYSTEMTIME__{unsigned char Second;unsigned char Minute;unsigned char Hour;unsigned char Week;unsigned char Day;unsigned char Month;unsigned char Year;unsigned char DateString[9];unsigned char TimeString[9];}SYSTEMTIME; //定义的时间类型#define AM(X) X#define PM(X) (X+12) // 转成24小时制#define DS1302_SECOND 0x80#define DS1302_MINUTE 0x82#define DS1302_HOUR 0x84#define DS1302_WEEK 0x8A#define DS1302_DAY 0x86#define DS1302_MONTH 0x88#define DS1302_YEAR 0x8C#define DS1302_RAM(X) (0xC0+(X)*2) //用于计算DS1302_RAM 地址的宏void DS1302InputByte(unsigned char d) //实时时钟写入一字节(部函数) {unsigned char i;ACC = d;for(i=8; i>0; i--){DS1302_IO = ACC0; //相当于汇编中的RRCDS1302_CLK = 1;DS1302_CLK = 0;ACC = ACC >> 1;}}unsigned char DS1302OutputByte(void) //实时时钟读取一字节(部函数) {unsigned char i;for(i=8; i>0; i--){ACC = ACC >>1; //相当于汇编中的RRCACC7 = DS1302_IO;DS1302_CLK = 1;DS1302_CLK = 0;}return(ACC);}void Write1302(unsigned char ucAddr, unsigned char ucDa) //ucAddr: DS1302地址, ucData: 要写的数据{DS1302_RST = 0;DS1302_CLK = 0;DS1302_RST = 1;DS1302InputByte(ucAddr); // 地址,命令DS1302InputByte(ucDa); // 写1Byte数据DS1302_CLK = 1;DS1302_RST = 0;}unsigned char Read1302(unsigned char ucAddr) //读取DS1302某地址的数据{unsigned char ucData;DS1302_RST = 0;DS1302_CLK = 0;DS1302_RST = 1;DS1302InputByte(ucAddr|0x01); // 地址,命令ucData = DS1302OutputByte(); // 读1Byte数据DS1302_CLK = 1;DS1302_RST = 0;return(ucData);}void DS1302_SetProtect(bit flag) //是否写保护{if(flag)Write1302(0x8E,0x10);elseWrite1302(0x8E,0x00);}void DS1302_SetTime(unsigned char Address, unsigned char Value) // 设置时间函数{DS1302_SetProtect(0);Write1302(Address, ((Value/10)<<4 | (Value%10)));}void DS1302_GetTime(SYSTEMTIME *Time){unsigned char ReadValue;ReadValue = Read1302(DS1302_SECOND);Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_MINUTE);Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_HOUR);Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_DAY);Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_WEEK);Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_MONTH);Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_YEAR);Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); }void DateToStr(SYSTEMTIME *Time){Time->DateString[0] = Time->Year/10 + '0';Time->DateString[1] = Time->Year%10 + '0';Time->DateString[2] = '-';Time->DateString[3] = Time->Month/10 + '0';Time->DateString[4] = Time->Month%10 + '0';Time->DateString[5] = '-';Time->DateString[6] = Time->Day/10 + '0';Time->DateString[7] = Time->Day%10 + '0';Time->DateString[8] = '\0';}void TimeToStr(SYSTEMTIME *Time){Time->TimeString[0] = Time->Hour/10 + '0';Time->TimeString[1] = Time->Hour%10 + '0';Time->TimeString[2] = ':';Time->TimeString[3] = Time->Minute/10 + '0';Time->TimeString[4] = Time->Minute%10 + '0';Time->TimeString[5] = ':';Time->TimeString[6] = Time->Second/10 + '0';Time->TimeString[7] = Time->Second%10 + '0';Time->DateString[8] = '\0';}void Initial_DS1302(void){unsigned char Second=Read1302(DS1302_SECOND);if(Second&0x80)DS1302_SetTime(DS1302_SECOND,0);}/******************************************************************** ************void BurstWrite1302(unsigned char *pWClock) //往DS1302写入时钟数据(多字节方式){unsigned char i;Write1302(0x8e,0x00); // 控制命令,WP=0,写操作?DS1302_RST = 0;DS1302_CLK = 0;DS1302_RST = 1;DS1302InputByte(0xbe); // 0xbe:时钟多字节写命令for (i = 8; i>0; i--) //8Byte = 7Byte 时钟数据+ 1Byte 控制{DS1302InputByte(*pWClock); // 写1Byte数据pWClock++;}DS1302_CLK = 1;DS1302_RST = 0;}void BurstRead1302(unsigned char *pRClock) //读取DS1302时钟数据(时钟多字节方式){unsigned char i;DS1302_RST = 0;DS1302_CLK = 0;DS1302_RST = 1;DS1302InputByte(0xbf); // 0xbf:时钟多字节读命令for (i=8; i>0; i--){*pRClock = DS1302OutputByte(); // 读1Byte数据pRClock++;}DS1302_CLK = 1;DS1302_RST = 0;}void DS1302_TimeStop(bit flag) // 是否将时钟停止{unsigned char Data;Data=Read1302(DS1302_SECOND);DS1302_SetProtect(0);if(flag)Write1302(DS1302_SECOND, Data|0x80);elseWrite1302(DS1302_SECOND, Data&0x7F);}********************************************************************* ***********/#endif-四、心得体会这是一次非常难得的理论与实践相结合的机会,通过这次比较完整的数字电子时钟设计,我摆脱了单纯的理论知识学习状态,和实际设计的结合锻炼了我综合运用所学的专业基础知识,解决实际工程的能力。