DS1302可调时钟+单片机+普中+江科大自化协
1 功能:
2 参考程序
2.1 主程序
#include <REGX52.H> #include "LCD1602.H" #include "DS1302.H" #include "Key.H" #include "Timer0.H" unsigned char KeyNum; //记录按键值 unsigned char MODE; //功能模式选择,0为时间显示模式,1为时间调整模式 unsigned char TimeSetSelect; //时间设置选择位,0-5,年、月、日、时、分、秒 unsigned char TimeSetFlashFlag;//闪烁标志位,0为熄灭,1为显示 void TimeShow(void) //显示时间 { DS1302_ReadTime( ); //读取时间 LCD1602_ShowNum(1,1,DS1302_Time[0],2); //显示年 LCD1602_ShowNum(1,4,DS1302_Time[1],2); //显示月 LCD1602_ShowNum(1,7,DS1302_Time[2],2); //显示日 LCD1602_ShowNum(2,1,DS1302_Time[3],2); //显示时 LCD1602_ShowNum(2,4,DS1302_Time[4],2); //显示分 LCD1602_ShowNum(2,7,DS1302_Time[5],2); //显示秒 } void TimeSet(void) //调整时间 { if(KeyNum == 2) //按键2按下,调整时间设置选择位 { TimeSetSelect++; //时间设置选择位加1 TimeSetSelect%=6; //0-6循环,越界清零 } if(KeyNum == 3) //调整数值,加1操作 { DS1302_Time[TimeSetSelect]++; if(DS1302_Time[0]>99) {DS1302_Time[0] = 0;} //年越界判断 if(DS1302_Time[1]>12) {DS1302_Time[1] = 1;} //月越界判断 //日越界判断 if((DS1302_Time[1]==1)||(DS1302_Time[1]==3)||(DS1302_Time[1]==5)||(DS1302_Time[1]==7)|| (DS1302_Time[1]==8)||(DS1302_Time[1]==10)||(DS1302_Time[1]==12)) //大月31天 { if(DS1302_Time[2]>31) {DS1302_Time[2] = 1;} } else if((DS1302_Time[1]==4)||(DS1302_Time[1]==6)||(DS1302_Time[1]==9)||(DS1302_Time[1]==11)) { if(DS1302_Time[2]>30) {DS1302_Time[2] = 1;} } else if(DS1302_Time[1]==2) { if(DS1302_Time[0]%4==0) { if(DS1302_Time[2]>29) {DS1302_Time[2] = 1;} } else { if(DS1302_Time[2]>28) {DS1302_Time[2] = 1;} } } if(DS1302_Time[3]>23) {DS1302_Time[3] = 0;} //时越界判断 if(DS1302_Time[4]>59) {DS1302_Time[4] = 0;} //分越界判断 if(DS1302_Time[5]>59) {DS1302_Time[5] = 0;} //秒越界判断 } if(KeyNum == 4) { DS1302_Time[TimeSetSelect]--; if(DS1302_Time[0]<0) {DS1302_Time[0] = 99;} //年越界判断 if(DS1302_Time[1]<1) {DS1302_Time[1] = 12;} //月越界判断 //日越界判断 if((DS1302_Time[1]==1)||(DS1302_Time[1]==3)||(DS1302_Time[1]==5)||(DS1302_Time[1]==7)|| (DS1302_Time[1]==8)||(DS1302_Time[1]==10)||(DS1302_Time[1]==12)) //大月31天 { if(DS1302_Time[2]<1) {DS1302_Time[2] = 31;} if(DS1302_Time[2]>31) {DS1302_Time[2] = 1;} //在减的过程中,出现越界现象,即1131现象 } else if((DS1302_Time[1]==4)||(DS1302_Time[1]==6)||(DS1302_Time[1]==9)||(DS1302_Time[1]==11)) { if(DS1302_Time[2]<1) {DS1302_Time[2] = 30;} if(DS1302_Time[2]>30) {DS1302_Time[2] = 1;} } else if(DS1302_Time[1]==2) { if(DS1302_Time[0]%4==0) { if(DS1302_Time[2]<1) {DS1302_Time[2] = 29;} if(DS1302_Time[2]>29) {DS1302_Time[2] = 1;} } else { if(DS1302_Time[2]<1) {DS1302_Time[2] = 28;} if(DS1302_Time[2]>28) {DS1302_Time[2] = 1;} } } if(DS1302_Time[3]<0) {DS1302_Time[3] = 23;} //时越界判断 if(DS1302_Time[4]<0) {DS1302_Time[4] = 59;} //分越界判断 if(DS1302_Time[5]<0) {DS1302_Time[5] = 59;} //秒越界判断 } //更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能 if((TimeSetSelect==0)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(1,1," ");} //显示年 else {LCD1602_ShowNum(1,1,DS1302_Time[0],2);} if((TimeSetSelect==1)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(1,4," ");} //显示月 else {LCD1602_ShowNum(1,4,DS1302_Time[1],2);} if((TimeSetSelect==2)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(1,7," ");} //显示日 else {LCD1602_ShowNum(1,7,DS1302_Time[2],2);} if((TimeSetSelect==3)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(2,1," ");} //显示时 else {LCD1602_ShowNum(2,1,DS1302_Time[3],2);} if((TimeSetSelect==4)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(2,4," ");} //显示分 else {LCD1602_ShowNum(2,4,DS1302_Time[4],2);} if((TimeSetSelect==5)&&(TimeSetFlashFlag==1)) {LCD1602_ShowString(2,7," ");} //显示秒 else {LCD1602_ShowNum(2,7,DS1302_Time[5],2);} } void main() { //初始化操作 LCD1602_Init(); DS1302_Init(); Timer0Init(); LCD1602_ShowString(1,1," - - "); //静态字符初始化显示 LCD1602_ShowString(2,1," : : "); DS1302_SetTime( ); //设置初始时间 while(1) { KeyNum = Key(); //读取按键值 if(KeyNum == 1) //按键1按下,模式切换 { if(MODE == 0) {MODE = 1; TimeSetSelect = 0;} else if(MODE == 1) {MODE = 0;DS1302_SetTime( );} //切换回时间显示模式,同时更新时间数据 } switch(MODE) { case 0: {TimeShow();break;} //时间显示功能 case 1: {TimeSet(); break;} //时间调整功能 } } } //定时器中断,每500ms闪烁标志位TimeSetFlashFlag状态反转一次 void Timer0_Routine() interrupt 1 { static unsigned int T0Count; TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 T0Count++; if(T0Count>=500) //每500ms进入一次 { T0Count=0; TimeSetFlashFlag=!TimeSetFlashFlag; //闪烁标志位取反 } }
2.2 LCD1602控制源程序及头文件
#include <REGX52.H> #include "intrins.h" //引脚配置,硬件原理图,普中A2 sbit LCD1602_RS = P2^6; sbit LCD1602_RW = P2^5; sbit LCD1602_E = P2^7; #define LCD1602_DATAPORT P0 /** * @brief LCD1602延时函数 * @param xms=1, 延时1ms * @retval 无 */ void LCD1602_Delayms(unsigned char xms) //@11.0592MHz { unsigned char i, j,k; for(k=0;k<xms;k++) { _nop_(); i = 2; j = 199; do { while (--j); } while (--i); } } /** * @brief LCD1602写命令,时序图 * @param Command 要写入的命令 * @retval 无 */ void LCD1602_WriteCommand(unsigned char Command) { LCD1602_RS = 0; LCD1602_RW = 0; LCD1602_DATAPORT = Command; LCD1602_E = 1; LCD1602_Delayms(1); //延时1ms LCD1602_E = 0; LCD1602_Delayms(1); //延时1ms } /** * @brief LCD1602写数据,时序图 * @param Data 要写入的数据 * @retval 无 */ void LCD1602_WriteData(unsigned char Data) { LCD1602_RS = 1; LCD1602_RW = 0; LCD1602_DATAPORT = Data; LCD1602_E = 1; LCD1602_Delayms(1); //延时1ms LCD1602_E = 0; LCD1602_Delayms(1); //延时1ms } /** * @brief LCD1602初始化函数 * @param 无 * @retval 无 */ void LCD1602_Init(void) { LCD1602_WriteCommand(0x38); //0011_1000 功能设置,设置16x2显示,5x7点阵,8位数据接口 LCD1602_WriteCommand(0x0C); //0000_1100 显示开关控制,显示开,光标关,闪烁关 LCD1602_WriteCommand(0x06); //0000_0110 输入方式设置,数据写后,AC自动加一,画面不动 LCD1602_WriteCommand(0x01); //0000_0001 清屏,数据指针清零、所有显示清零 } /** * @brief LCD1602设置光标位置 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @retval 无 */ void LCD1602_SetCursor(unsigned char Line,unsigned char Column) { if(Line == 1) { LCD1602_WriteCommand(0x80|(Column-1)); //设置要存入数据的DDROM地址,第一行地址 } else { LCD1602_WriteCommand(0x80|(Column-1)+0x40); //设置要存入数据的DDROM地,第二行地址 } } /** * @brief 在LCD1602指定位置上显示一个字符 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @param Char 要显示的字符 * @retval 无 */ void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char) { LCD1602_SetCursor(Line,Column); LCD1602_WriteData(Char); } /** * @brief 在LCD1602指定位置上显示所给字符串 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @param Char 要显示的字符串 * @retval 无 */ void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String) //指针 { unsigned char i; LCD1602_SetCursor(Line,Column); for(i=0;String[i]!='\0';i++) //字符串结束标志'\0' { LCD1602_WriteData(String[i]); } } /** * @brief 返回值=X的Y次方,指数运算 */ int LCD_Power(int X,int Y) { unsigned char i; int result = 1; for(i=0;i<Y;i++) { result = result * X; } return result; } /** * @brief 在LCD1602指定位置开始显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~65535 * @param Length 要显示数字的长度,范围:1~5 * @retval 无 */ void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; LCD1602_SetCursor(Line,Column); for(i=Length;i>0;i--) { LCD1602_WriteData('0'+Number/LCD_Power(10,i-1)%10); // '0'=0x30,转换为字符显示; /** * 789, 789/100%10, 取出百位 * 789, 789/10%10, 取出十位 * 789, 789/1%10, 取出个位 */ } } /** * @brief 在LCD1602指定位置开始以有符号十进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:-32768~32767 * @param Length 要显示数字的长度,范围:1~5 * @retval 无 */ void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column, int Number,unsigned char Length) { unsigned char i; unsigned int Number1; LCD1602_SetCursor(Line,Column); if(Number >= 0) { LCD1602_WriteData('+'); Number1 = Number; } else { LCD1602_WriteData('-'); Number1 = -Number; //-32768 } for(i=Length;i>0;i--) { LCD1602_WriteData('0'+Number1/LCD_Power(10,i-1)%10); } } /** * @brief 在LCD1602指定位置开始以十六进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~0xffff * @param Length 要显示数字的长度,范围:1~4 * @retval 无 */ void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; unsigned char SingleNumber; LCD1602_SetCursor(Line,Column); for(i=Length;i>0;i--) { SingleNumber = Number/LCD_Power(16,i-1)%16; //从高位到低位,取出每一个十六进制数字 if(SingleNumber < 10) LCD1602_WriteData('0'+SingleNumber); else LCD1602_WriteData('A'+SingleNumber-10); //SingleNumber已含有10+x } } /** * @brief 在LCD1602指定位置开始以二进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~1111_1111_1111_1111 * @param Length 要显示数字的长度,范围:1~16 * @retval 无 */ void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; LCD1602_SetCursor(Line,Column); for(i=Length;i>0;i--) { LCD1602_WriteData('0'+Number/LCD_Power(2,i-1)%2); //从高位到低位,取出每一个二进制数字 } }
#ifndef __LCD1602_H__ #define __LCD1602_H__ void LCD1602_WriteCommand(unsigned char Command); void LCD1602_WriteData(unsigned char Data); void LCD1602_Init(void); void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char); void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String); void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); #endif
2.3 DS1302控制源程序及头文件
#include <REGX52.H> //引脚定义,与硬件原理图有关 sbit DS1302_SCLK = P3^6; sbit DS1302_IO = P3^4; sbit DS1302_CE = P3^5; #define DS1302_SECOND_ADDR 0x80 //DS1302秒寄存器写地址 #define DS1302_MINUTE_ADDR 0x82 //DS1302分寄存器写地址 #define DS1302_HOUR_ADDR 0x84 //DS1302时寄存器写地址 #define DS1302_DATE_ADDR 0x86 //DS1302日寄存器写地址 #define DS1302_MONTH_ADDR 0x88 //DS1302月寄存器写地址 #define DS1302_DAY_ADDR 0x8A //DS1302星期寄存器写地址 #define DS1302_YEAR_ADDR 0x8C //DS1302年寄存器写地址 #define DS1302_WP_ADDR 0x8E //DS1302控制寄存器写地址 char DS1302_Time[]={22,8,7,20,8,55,7}; //时间数字,索引0-6分别为年、月、日、时、分、秒、星期 /** * @brief DS1302初始化 * @param 无 * @retval 无 */ void DS1302_Init(void) { DS1302_CE = 0; DS1302_SCLK = 0; } /** * @brief DS1302写一个字节 * @param Command 命令字/地址 * @param Data 要写入的数据 * @retval 无 */ void DS1302_WriteByte(unsigned char Command,Data) { unsigned char i; DS1302_CE = 1; for(i=0;i<8;i++) //写地址/命令 { DS1302_IO = Command & (0x01<<i); //从最低位开始,一次取出相应位的数据 DS1302_SCLK = 1; //SCLK上升沿,MCU向DS1302写入数据 DS1302_SCLK = 0; //一个完整的脉冲 } for(i=0;i<8;i++) //写数据 { DS1302_IO = Data & (0x01<<i); //从最低位开始,一次取出相应位的数据 DS1302_SCLK = 1; //SCLK上升沿,MCU向DS1302写入数据 DS1302_SCLK = 0; //一个完整的脉冲 } DS1302_CE = 0; } /** * @brief DS1302读一个字节 * @param Command 命令字/地址 * @retval 读出的数据 */ unsigned char DS1302_ReadByte(unsigned char Command) { unsigned char i; unsigned char Data = 0x00; Command = Command | 0x01; //将读地址指令转换为相应的读地址指令 DS1302_CE = 1; for(i=0;i<8;i++) //写地址/命令 { DS1302_IO = Command & (0x01<<i); //从最低位开始,一次取出相应位的数据 DS1302_SCLK = 0; //一个完整的脉冲,与写地址写数据相反 DS1302_SCLK = 1; //SCLK上升沿,MCU向DS1302写入数据 } for(i=0;i<8;i++) { DS1302_SCLK = 1; //写地址读数据 DS1302_SCLK = 0; //SCLK下降沿,MUC读取DS1302的数据 if(DS1302_IO) {Data = Data | (0x01<<i);} } DS1302_CE = 0; DS1302_IO = 0; //读取后将IO设置为0,否则读出的数据会出错 return Data; } /** * @brief DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中 * @param 无 * @retval 无 */ void DS1302_SetTime(void) { DS1302_WriteByte(DS1302_WP_ADDR,0x00); //解除芯片写保护 DS1302_WriteByte(DS1302_YEAR_ADDR, DS1302_Time[0]/10*16+DS1302_Time[0]%10); //写入年数据,十进制转为十六进制 DS1302_WriteByte(DS1302_MONTH_ADDR, DS1302_Time[1]/10*16+DS1302_Time[1]%10); //写入月数据 DS1302_WriteByte(DS1302_DATE_ADDR, DS1302_Time[2]/10*16+DS1302_Time[2]%10); //写入日数据 DS1302_WriteByte(DS1302_HOUR_ADDR, DS1302_Time[3]/10*16+DS1302_Time[3]%10); //写入小时数据 DS1302_WriteByte(DS1302_MINUTE_ADDR, DS1302_Time[4]/10*16+DS1302_Time[4]%10); //写入分钟数据 DS1302_WriteByte(DS1302_SECOND_ADDR, DS1302_Time[5]/10*16+DS1302_Time[5]%10); //写入秒钟数据 DS1302_WriteByte(DS1302_DAY_ADDR, DS1302_Time[6]/10*16+DS1302_Time[6]%10); //写入星期数据 DS1302_WriteByte(DS1302_WP_ADDR,0x80); //开启芯片写保护 } /** * @brief DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中 * @param 无 * @retval 无 */ void DS1302_ReadTime(void) { unsigned char Temp; Temp = DS1302_ReadByte(DS1302_YEAR_ADDR); DS1302_Time[0]=Temp/16*10+Temp%16; Temp = DS1302_ReadByte(DS1302_MONTH_ADDR); DS1302_Time[1]=Temp/16*10+Temp%16; Temp = DS1302_ReadByte(DS1302_DATE_ADDR); DS1302_Time[2]=Temp/16*10+Temp%16; Temp = DS1302_ReadByte(DS1302_HOUR_ADDR); DS1302_Time[3]=Temp/16*10+Temp%16; Temp = DS1302_ReadByte(DS1302_MINUTE_ADDR); DS1302_Time[4]=Temp/16*10+Temp%16; Temp = DS1302_ReadByte(DS1302_SECOND_ADDR); DS1302_Time[5]=Temp/16*10+Temp%16; Temp = DS1302_ReadByte(DS1302_DAY_ADDR); DS1302_Time[6]=Temp/16*10+Temp%16; }
#ifndef __DS1302_H__ #define __DS1302_H__ //外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期 extern char DS1302_Time[]; void DS1302_Init(void); void DS1302_WriteByte(unsigned char Command,Data); unsigned char DS1302_ReadByte(unsigned char Command); void DS1302_SetTime(void); void DS1302_ReadTime(void); #endif
2.4 独立按键扫描函数及头文件
#include <REGX52.H> #include "Delay.h" /** * @brief 获取独立按键键码 * @param 无 * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0 */ unsigned char Key() { unsigned char KeyNumber=0; if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;} if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;} if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;} if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;} return KeyNumber; }
#ifndef __KEY_H__ #define __KEY_H__ unsigned char Key(); #endif
2.5 定时器中断函数及头文件
#include <REGX52.H> /** * @brief 定时器0初始化,1毫秒@12.000MHz * @param 无 * @retval 无 */ void Timer0Init(void) { TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0=1; EA=1; PT0=0; } /*定时器中断函数模板 void Timer0_Routine() interrupt 1 { static unsigned int T0Count; TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 T0Count++; if(T0Count>=1000) { T0Count=0; } } */
#ifndef __TIMER0_H__ #define __TIMER0_H__ void Timer0Init(void); #endif
2.6 延时函数及头文件
void Delay(unsigned int xms) { unsigned char i, j; while(xms--) { i = 2; j = 239; do { while (--j); } while (--i); } }
#ifndef __DELAY_H__ #define __DELAY_H__ void Delay(unsigned int xms); #endif