单片机电子时钟设计报告
一、设计任务
本次课程设计的电子时钟电路,是基于单片机STC89C52、时钟芯片和液晶显示,运用C语言编程实现。电子时钟可以显示日期的年、月、日和时间的时、分、秒,具有复位功能。
二、系统硬件设备及芯片简介
数字电子钟系统设计已经成熟,但是目前系统设计时基本 都是采用 LED 作为显示电路,造成硬件电路复杂、功耗高、产 品体积庞大等特点;液晶显示模块由于具有低功耗、寿命长、 体积小、显示内容丰富、价格低、接口控制方便等优点,因此 在各类电子产品中被极广泛地推广和应用。字符型液晶显示模 块是一类专门用于显示字母、数字、符号等点阵式液晶显示模 块。本系统设计采用字符型液品显示模块 LCD1602 作为显示器 件,这样不仅简化了系统的硬件设计,而且极大地提高了系统 的可靠性。
1 LCD1602简介
字符型液晶显示模块 LCD1602已经是单片机应用设计中 最常用的信息显示器件。LCD1602可以显示两行,每行16个 字符,采用+5V电源供电,外围电路配置简单,价格便宜,具 有很高的性价比。 2 LCD1602功能介绍 2.1 引脚功能
LCD1602采用标准14脚(无背光)或16脚(带背光)接 口,各引脚功能见表1。
编号 符号 VSS VDD VL 表 1 引脚功能 引脚说明 电源地 电源正极 液晶显示偏压信号 编号 9 10 11 符号 D2 D3 D4 引脚说明 Data I/O Data I/O Data I/O
1 2 3 4 R S 数据 / 命令选择端 12 D5 Data I/O 总结 5 6 R/W E 读/写选择端 使能信号 13 14 D6 D7 Data I/O Data I/O -
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 sfr DPH sfr PCON sfr TCON sfr TMOD sfr TL0 sfr TL1 sfr TH0 sfr TH1 sfr P1 sfr SCON sfr SBUF sfr P2 sfr IE sfr P3 sfr IP sfr T2CON = 0x82; = 0x83; = 0x87; = 0x88; = 0x89; = 0x8A; = 0x8B; = 0x8C; = 0x8D; = 0x90; = 0x98; = 0x99; = 0xA0; = 0xA8; = 0xB0; = 0xB8; = 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; sbit TXD = 0xB1; sbit INT0 = 0xB2; sbit INT1 = 0xB3; sbit T0 = 0xB4; sbit T1 = 0xB5; sbit WR = 0xB6; sbit RD = 0xB7;
/* Serial data input */ /* Serial data output */ /* External interrupt 0 */ /* External interrupt 1 */ /* Timer 0 external input */ /* Timer 1 external input */
/* External data memory write strobe */ /* 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 */ #endif
LCD1602驱动程序为:
#ifndef LCD_CHAR_1602_2005_4_9 #define LCD_CHAR_1602_2005_4_9
#include Definitions********************************************************** 总结 - sbit LcdRs sbit LcdRw = P2^0; = P2^1; sbit LcdEn = P2^2; sfr DBPort //内部等待函数 ************************************************************************** unsigned char LCD_Wait(void) { LcdRs=0; LcdRw=1; LcdEn=1; _nop_(); _nop_(); = 0x80; //P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口 //while(DBPort&0x80);//在用Proteus仿真时,注意用屏蔽此语句,在调用 GotoXY()时,会进入死循环, //可能在写该控制字时,该模块没有返回写入完备命令, 即DBPort&0x80==0x80 } //向LCD写入命令或数据 //实际硬件时打开此语句 LcdEn=0; return DBPort; 总结 - ************************************************************ #define LCD_COMMAND 0 // Command #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_COMMAND, 0x08|DisplayMode); } //设置输入模式 ************************************************************ #define LCD_AC_UP 0x02 #define LCD_AC_DOWN 0x00 // default #define LCD_MOVE 0x01 // 画面可平移 #define LCD_NO_MOVE 0x00 //default void LCD_SetInput(unsigned char InputMode) { LCD_Write(LCD_COMMAND, 0x04|InputMode); } 总结 - //移动光标或屏幕 ************************************************************ /* #define LCD_CURSOR #define LCD_SCREEN #define LCD_LEFT 0x02 0x08 0x00 0x04 #define LCD_RIGHT void LCD_Move(unsigned char object, unsigned char direction) { } */ //初始化 LCD************************************************************ void LCD_Initial() { LcdEn=0; LCD_Write(LCD_COMMAND,0x38); //8位数据端口,2行显示,5*7点if(object==LCD_CURSOR) LCD_Write(LCD_COMMAND,0x10|direction); if(object==LCD_SCREEN) LCD_Write(LCD_COMMAND,0x18|direction); 总结 - 阵 } //************************************************************************ void GotoXY(unsigned char x, unsigned char y) { } void Print(unsigned char *str) { while(*str!='\\0') { LCD_Write(LCD_DATA,*str); str++; if(y==0) LCD_Write(LCD_COMMAND,0x80|x); LCD_Write(LCD_COMMAND,0x38); LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR); //开启显示, 无光标 LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN); //清屏 LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE); //AC递增, 画面不动 if(y==1) LCD_Write(LCD_COMMAND,0x80|(x-0x40)); 总结 - } /* } void LCD_LoadChar(unsigned char user[8], unsigned char place) { } */ //************************************************************************ #endif Intrins.h程序为: #ifndef __INTRINS_H__ #define __INTRINS_H__ extern void _nop_ (void); unsigned char i; LCD_Write(LCD_COMMAND,0x40|(place*8)); for(i=0; i<8; i++) LCD_Write(LCD_DATA,user[i]); 总结 - 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); #endif DS1302时钟程序为: #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) #define DS1302_SECOND 0x80 #define DS1302_MINUTE 0x82 #define DS1302_HOUR 0x84 #define DS1302_WEEK 0x8A #define DS1302_DAY 0x86 // 转成24小时制 总结 - #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; DS1302_CLK = 1; DS1302_CLK = 0; ACC = ACC >> 1; } } unsigned char DS1302OutputByte(void) { unsigned char i; for(i=8; i>0; i--) { //实时时钟写入一字节(内部函数) //相当于汇编中的 RRC //实时时钟读取一字节(内部函数) 总结 - ACC = ACC >>1; ACC7 = DS1302_IO; DS1302_CLK = 1; DS1302_CLK = 0; } return(ACC); } //相当于汇编中的 RRC 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); else Write1302(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) { } /******************************************************************************** void BurstWrite1302(unsigned char *pWClock) //往DS1302写入时钟数据(多字节方式) { unsigned char i; Write1302(0x8e,0x00); // 控制命令,WP=0,写操作? DS1302_RST = 0; DS1302_CLK = 0; DS1302_RST = 1; unsigned char Second=Read1302(DS1302_SECOND); if(Second&0x80) DS1302_SetTime(DS1302_SECOND,0); 总结 - DS1302InputByte(0xbe); // 0xbe:时钟多字节写命令 for (i = 8; i>0; i--) { 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++; //8Byte = 7Byte 时钟数据 + 1Byte 控制 总结 - } DS1302_CLK = 1; DS1302_RST = 0; } void DS1302_TimeStop(bit flag) // 是否将时钟停止 { } ********************************************************************************/ #endif- unsigned char Data; Data=Read1302(DS1302_SECOND); DS1302_SetProtect(0); if(flag) Write1302(DS1302_SECOND, Data|0x80); else Write1302(DS1302_SECOND, Data&0x7F); 四、心得体会 这是一次非常难得的理论与实践相结合的机会,通过这次比较完整的数字电子时钟设计,我摆脱了单纯的理论知识学习状态,和实际设计的结合锻炼了我综 总结 - 合运用所学的专业基础知识,解决实际工程的能力。 通过这次对数字时钟的设计与制作,让我了解了设计电路的程序。设计中曾遇到诸多问题,由于线路错综复杂,很容易在连线过程中出现错接、漏接的情况,即时连接正确也不一定会实现最初的目标。还要针对错误现象,排查原因,在连线正确的基础上,可能是元器件的故障,需要逐步更换调整。 正是这一次设计让我积累了很多经验,也必然让我在未来的工作学习中表现出更好的应变能力,更强的沟通和理解能力。 总结 因篇幅问题不能全部显示,请点此查看更多更全内容