系列文章目录
第一章 51单片机Proteus仿真(二)可调时钟
前言
本文介绍51单片机Proteus仿真,实现可调时钟。
伪代码:
开机自动显示时间
按下第一个按键K1可以切换模式, 第一次按下K1,LCD1602显示刚才时间,可以进行更改操作。再次按下按键K1,显示修改后时间
按下第二个按键选择位(年月日),并且光标闪烁
第三个按下加一,假如越界判断,闰年判断
第四个按下减一
一、代码逻辑
当没有按键按下时,LCD显示数组存储的时间。
按下第一个按键K1,进入设置时间函数Set_Time()。此时LCD1602显示的是当前时间,LCD不会动,此时其实时钟芯片在工作。若没有其他按键按下则继续显示刚才时间,也只是经过微秒级别,接着写入刚才时间显示。若刚好检测到按键(K1 K2 K3)其中一个按下,返回按键值,加一或减一,写入时间,然后显示。
因为按键按下延时,时钟芯片还在工作。所以在执行完最后一次显示函数Show_Time()后,此时读取了一次数据,数组中保存的就是按键按下之前的时间,几乎就是按键按下时间,因为不再执行show函数,差几个机器周期。此时在对这些数据进行处理(操作也只是改变数组里的值),按键操作之后,再将这些数据写进寄存器(调用DS1302_SetTime()),下一次按键K1按下,就可以重新显示改变后的时间。
二、完整代码
#include <regx52.h>
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
//LCD1602
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8A
#define DS1302_YEAR 0x8C
#define DS1302_WP 0x8E
//初始化时间0-6年月日小时分秒星期
char DS1302_Time[]={22,4,26,21,59,55,2};
//LCD1602º¯Êý
void LCD_Delay();
void LCD_Init();
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void DS1302_Init();
void DS1302_WriteByte(unsigned char command,Data);
unsigned char DS1302_ReadByte(unsigned char command);
void DS1302_ReadTime(void);
void DS1302_SetTime();
void Set_Time();
void Show_Time();
void Timer0_Init();
unsigned char key_value=0;
unsigned char key_set=0;
bit flag=0;
sbit K1=P3^1;
sbit K2=P3^0;
sbit K3=P3^2;
sbit K4=P3^3;
unsigned char Key();
void Delay(unsigned int xms);
int main()
{
bit mode=0;
LCD_Init();
DS1302_Init();
LCD_ShowString(1,1," - - ");//
LCD_ShowString(2,1," : : ");
DS1302_SetTime();
Timer0_Init();
while(1)
{
key_value=Key();
if(key_value==1)
{
mode=!mode;
key_set=0;
}
if(mode==0)
{
Show_Time();
}
else if(mode==1)
{
Set_Time();
}
}
return 0;
}
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
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;
}
void Timer0_Init()
{
TMOD&=0XF0;
TMOD|=0X01;
TH0=(65536-1000)/256;
TL0=(65536-1000)%256;
TR0=1;
EA=1;
ET0=1;
}
void Timer0_Rountine() interrupt 1
{
static int count=0;
TH0=(65536-1000)/256;
TL0=(65536-1000)%256;
count++;
if(count==500)
{
count=0;
flag=!flag;
}
}
//时间设置,用于修改时间,K2控制哪个位。K3加,K4减
void Set_Time()
{
if(key_value==2)
{
key_set++;
if(key_set==6)
key_set=0;
}
if(key_value==3)
{
DS1302_Time[key_set]++;
if(DS1302_Time[0]>99)
DS1302_Time[0]=0;
if(DS1302_Time[1]>12)
DS1302_Time[1]=0;
if(DS1302_Time[1]==1||DS1302_Time[1]==3||DS1302_Time[1]==5||DS1302_Time[1]==8
||DS1302_Time[1]==7||DS1302_Time[1]==10||DS1302_Time[1]==12)
{
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[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(key_value==4)
{
DS1302_Time[key_set]--;
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)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=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]<1){DS1302_Time[2]=30;}
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
else
{
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;
}
//光标闪烁实现代码
if(key_set==0&&flag==0)
{
LCD_ShowString(1,1," ");
}
else
LCD_ShowNum(1,1,DS1302_Time[0],2);
if(key_set==1&&flag==0)
{
LCD_ShowString(1,4," ");
}
else
LCD_ShowNum(1,4,DS1302_Time[1],2);
if(key_set==2&&flag==0)
{
LCD_ShowString(1,7," ");
}
else
LCD_ShowNum(1,7,DS1302_Time[2],2);
if(key_set==3&&flag==0)
{
LCD_ShowString(2,1," ");
}
else
LCD_ShowNum(2,1,DS1302_Time[3],2);
if(key_set==4&&flag==0)
{
LCD_ShowString(2,4," ");
}
else
LCD_ShowNum(2,4,DS1302_Time[4],2);
if(key_set==5&&flag==0)
{
LCD_ShowString(2,7," ");
}
else
LCD_ShowNum(2,7,DS1302_Time[5],2);
DS1302_SetTime();//这一句很重要,将修改后数组的时间数据存入时钟芯片寄存器
}
//显示时间。第一句代码先读取时钟芯片寄存器数据,然后显示。
void Show_Time()
{
DS1302_ReadTime();
LCD_ShowNum(1,1,DS1302_Time[0],2);
LCD_ShowNum(1,4,DS1302_Time[1],2);
LCD_ShowNum(1,7,DS1302_Time[2],2);
LCD_ShowNum(2,1,DS1302_Time[3],2);
LCD_ShowNum(2,4,DS1302_Time[4],2);
LCD_ShowNum(2,7,DS1302_Time[5],2);
}
void DS1302_Init()
{
DS1302_CE=0;
DS1302_SCLK=0;
}
/**
*@brief DS1302Ð写数据
*@param
*@retval
*/
void DS1302_WriteByte(unsigned char command,Data)
{
int i;
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=command&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
for(i=0;i<8;i++)
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0;
}
/**
*@brief DS1302读数据
*@param
*@retval
*/
unsigned char DS1302_ReadByte(unsigned char command)
{
int i;
unsigned char Data=0x00;
DS1302_CE=1;
command|=0x01;
for(i=0;i<8;i++)
{
DS1302_IO=command&(0x01<<i);
DS1302_SCLK=0;
DS1302_SCLK=1;//
}
for(i=0;i<8;i++)
{
DS1302_SCLK=1;//
DS1302_SCLK=0;//
if(DS1302_IO==1) //
Data|=(0x01<<i);//
}
DS1302_IO=0;
DS1302_CE=0;
return Data;
}
void DS1302_SetTime(void)
{
DS1302_WriteByte(DS1302_WP,0x00);
DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
DS1302_WriteByte(DS1302_WP,0x80);
}
void DS1302_ReadTime(void)
{
unsigned char Temp;
Temp=DS1302_ReadByte(DS1302_YEAR);
DS1302_Time[0]=Temp/16*10+Temp%16;//
Temp=DS1302_ReadByte(DS1302_MONTH);
DS1302_Time[1]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DATE);
DS1302_Time[2]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_HOUR);
DS1302_Time[3]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MINUTE);
DS1302_Time[4]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_SECOND);
DS1302_Time[5]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DAY);
DS1302_Time[6]=Temp/16*10+Temp%16;
}
/**
* @brief LCD1602延时,12MHz1ms
* @param
* @retval
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602дÃüÁî
* @param Command
* @retval
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602дÊý¾Ý
* @param Data
* @retval
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602ÉèÖÃÖ¸ÕëλÖÃ
* @param Line :1~2
* @param Column :1~16
* @retval ?
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));//
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602
* @param ?
* @retval ?
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//
LCD_WriteCommand(0x0c);//
LCD_WriteCommand(0x06);//
LCD_WriteCommand(0x01);//
}
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
LCD_SetCursor(Line,Column);
while(*String!='\0')//
{
LCD_WriteData(*String++);
}
}
/**
*@brief
*@param ·µ»ØXµÄY´Î·½
*@retval
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief LCD1602ÏÔʾʮ½øÖÆ
* @param Line :1~2
* @param Column :1~16
* @param Number :0~65535
* @param Length :1~5
* @retval
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
三、遇到问题
四、遇到问题
(1) 将秒寄存器关掉,读出寄存器数据,最后再设置时间。其实这样做没有用,因为程序很快执行,已经读出寄存器数据了,不需要关时钟芯片。只需要操作读出的数据即可。读出的数据存储在数组里。
(2) 一直在设置时间函数Set_Time()里加个读取寄存器数据,希望操作按键之后出现理想结果(若不修改时间,再次按下K1显示第一次按下K1时的时间)。解决也很容易,软件实现。最关键是按键操作完,要重新给时钟芯片写入数组数据。保存修改后时间。
(3) 因为循环调用显示函数Show_Time(),时钟显示才变化
将显示函数放在while外面,只显示一个不动的时间,应该是只有循环才能出现变化的时间显示。原因是LCD显示一次时间初始化,而时钟在变化,只有再次显示,才能将寄存器实时时间读出来,从而显示在LCD1602上。
五、总结
写这套程序花费大约5个小时,而且最后参考了下其他老师程序。走了很多弯路,好歹最终实现目的。深刻明白代码逻辑的重要性,没有架构逻辑,写代码难如登山。通过一次次修改,还是有很大收获。诸君共勉。若能帮助到您,跪求点赞关注。如有问题,评论即可。