本帖最后由 jasonchao1120 于 2012-8-15 11:47 编辑
****************************************
今天搞了半天终于也给按键部分搞定了,但是最后发现更改了时间之后保存不了,恢复后又成了原来的时间,
还有个就是更改年的时间,按增大,减小后年时间的光标跑到了月上,但是不按之前没问题,地址也木有错。。
求帮忙啊,,,,我纠结了好久的问题, 2012.8.15
*****************************************
经过昨天晚上的思考,然后今天调试下,终于把存储问题搞定,
但是年的光标问题依旧,更换年的地址依旧不行。。我会再程序里标注错误。。。
也谢谢各位大神的帮助 2012.8.16
******************************************
基于DS1302&LCD1602的电子时钟,年月日,时分秒的显示,掉电不影响数据的存储。后续我会将闹钟功能,温度功能添加上。。
******************************************
正确的程序附件:
源程序如下:
***********************************************************
#include
#define uchar unsigned char
#define uint unsigned int
uchar code digit[10]={"0123456789"};
***it sclk=P1^0; //位定义1302芯片的接口,时钟输出端口定义在P1.0引脚
***it DATA=P1^1; //位定义1302芯片的接口,数据输出端定义在P1.1引脚
***it rst=P1^2; //位定义1302芯片的接口,复位端口定义在P1.2引脚
***it rs=P2^0; //1602寄存器选择位
***it rw=P2^1; //1602读写选择位
***it e=P2^2; //1602使能信号位
***it k5=P1^4;
***it k6=P1^5;
***it k7=P1^6;
***it k8=P1^7;
void keyscan();
uchar num,flag;
//uchar second,minute,hour,day,month,year; 有朋友指出这里应该是char型的,因为后面有second=-1;
char second,minute,hour,day,month,year;
uchar readvalue;
/**********************************
延时功能,延时若干秒
***********************************/
void delayms(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
/**************************
向1302写一个字节数据
入口参数:x
********************************/
void Write1302(uchar dat)
{
uchar i;
sclk=0;
delayms(2);
for(i=0;i<8;i++)
{
DATA=dat&0x01; //取出dat的第0位数据写入1302 低位在前,高位在后
delayms(2);
sclk=1;
delayms(2);
sclk=0;
dat>>=1; //将dat的各数据位右移1位,准备写入下一个数据位
}
}
/*****************************************************
函数功能:根据命令字,向1302写一个字节数据
入口参数:Cmd,储存命令字;dat,储存待写的数据
***************************************************/
void WriteSet1302(uchar cmd,uchar dat)
{
rst=0; //禁止数据传输
sclk=0;
rst=1;
delayms(2);
Write1302(cmd);
Write1302(dat);
sclk=1;
rst=0;
}
/*****************************************************
函数功能:从1302读一个字节数据
入口参数:x
***************************************************/
uchar Read1302(void)
{
uchar i,dat;
delayms(2);
for(i=0;i<8;i++)
{
dat>>=1;
if(DATA==1)
dat|=0x80;
sclk=1;
delayms(2);
sclk=0;
delayms(2);
}
return dat;
}
/*****************************************************
函数功能:根据命令字,从1302读取一个字节数据
入口参数:Cmd
***************************************************/
uchar ReadSet1302(uchar cmd)
{
uchar dat;
rst=0;
sclk=0;
rst=1;
Write1302(cmd);
dat=Read1302();
sclk=1;
rst=0;
return dat;
}
/*****************************************************
函数功能: 1302进行初始化设置
***************************************************/
void Init_DS1302(void)
{
unsigned char flag0;
flag0=ReadSet1302(0x81);
if(flag0&0x80) //判断时钟芯片是否关闭
{
WriteSet1302(0x8E,0x00); //根据写状态寄存器命令字,写入不保护指令
WriteSet1302(0x80,((0/10)<<4|(0%10))); //根据写秒寄存器命令字,写入秒的初始值
WriteSet1302(0x82,((0/10)<<4|(0%10))); //根据写分寄存器命令字,写入分的初始值
WriteSet1302(0x84,((0/10)<<4|(0%10))); //根据写小时寄存器命令字,写入小时的初始值
WriteSet1302(0x86,((1/10)<<4|(1%10))); //根据写日寄存器命令字,写入日的初始值
WriteSet1302(0x88,((1/10)<<4|(1%10))); //根据写月寄存器命令字,写入月的初始值
WriteSet1302(0x8c,((0/10)<<4|(0%10))); //根据写年寄存器命令字,写入年的初始值
WriteSet1302(0x90,0xa5); //打开充电功能 选择2K电阻充电方式
WriteSet1302(0x8E,0x80); //根据写状态寄存器命令字,写入保护指令
}
//如果不想每次都初始化时间,也就是掉电后还想让时钟继续走时的话 就用上面的语句
/*--------------------这是每次都初始化的语句-----------------*/
/*
WriteSet1302(0x8E,0x00); //根据写状态寄存器命令字,写入不保护指令
WriteSet1302(0x80,((55/10)<<4|(55%10))); //根据写秒寄存器命令字,写入秒的初始值
WriteSet1302(0x82,((59/10)<<4|(59%10))); //根据写分寄存器命令字,写入分的初始值
WriteSet1302(0x84,((23/10)<<4|(23%10))); //根据写小时寄存器命令字,写入小时的初始值
WriteSet1302(0x86,((18/10)<<4|(18%10))); //根据写日寄存器命令字,写入日的初始值
WriteSet1302(0x88,((6/10)<<4|(6%10))); //根据写月寄存器命令字,写入月的初始值
WriteSet1302(0x8c,((9/10)<<4|(9%10))); //根据写年寄存器命令字,写入年的初始值
WriteSet1302(0x90,0xa5); //打开充电功能 选择2K电阻充电方式
WriteSet1302(0x8E,0x80); //根据写状态寄存器命令字,写入保护指令
*/
}
/************************************
液晶模块
*************************************/
void write_com(uchar com)
{
rs=0;
rw=0;
e=0;
delayms(5);
P0=com;
delayms(5);
e=1;
delayms(5);
e=0;
}
void write_data(uchar date)
{
rs=1;
rw=0;
e=0;
P0=date;
delayms(5);
e=1;
delayms(5);
e=0;
}
void LcdInit()
{
delayms(5);
write_com(0x38);
delayms(2);
write_com(0x38);
delayms(2);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
delayms(5);
}
/**********************
1302数据显示程序
***********************/
void display(uchar add,uchar x)
{
uchar i,j;
i=x/10;
j=x%10;
write_com(0x80+add);
write_data(digit);
write_data(digit[j]);
delayms(50);
}
/************************
初始化程序
**************************/
void init()
{
LcdInit();
write_com(0x80+0x01);
write_data('D');
write_data('a');
write_data('t');
write_data('e');
write_data(':');
write_com(0x80+0x08);
write_data('-');
write_com(0x80+0x0b);
write_data('-');
write_com(0x80+0x46);
write_data(':');
write_com(0x80+0x49);
write_data(':');
delayms(5);
Init_DS1302();
delayms(10);
}
/*******************************
主函数
*******************************/
void main()
{
init();
while(1)
{
keyscan();
if(flag==0)
{
readvalue=ReadSet1302(0x81); //从秒寄存器读数据
second=((readvalue&0x70)>>4)*10 + (readvalue&0x0F);//将读出数据转化
display(0x4a,second); //显示秒
readvalue =ReadSet1302(0x83); //从分寄存器读
minute=((readvalue&0x70)>>4)*10 + (readvalue&0x0F); //将读出数据转化
display(0x47,minute); //显示分
readvalue =ReadSet1302(0x85); //从分寄存器读
hour=((readvalue&0x70)>>4)*10 + (readvalue&0x0F); //将读出数据转化
display(0x44,hour); //显示小时
readvalue =ReadSet1302(0x87); //从分寄存器读
day=((readvalue&0x70)>>4)*10 + (readvalue&0x0F); //将读出数据转化
display(0x0c,day); //显示日
readvalue =ReadSet1302(0x89); //从分寄存器读
month=((readvalue&0x70)>>4)*10 + (readvalue&0x0F); //将读出数据转化
display(0x09,month); //显示月
readvalue=ReadSet1302(0x8d); //从分寄存器读
year=((readvalue&0xf0)>>4)*10 + (readvalue&0x0F); //将读出数据转化
display(0x06,year); //显示年
delayms(5);
}
}
}
void keyscan()
{
if(k5==0)
{
delayms(5);
if(k5==0)
{
num++;
WriteSet1302(0x8E,0x00); //根据写状态寄存器命令字,写入不保护指令
flag=1;
while(!k5);
switch(num)
{
case 1:write_com(0x80+0x45); //hour
write_com(0x0f); //光标显示
break;
case 2:write_com(0x80+0x48); //minute
break;
case 3:write_com(0x80+0x4b); //second
break;
case 4:write_com(0x80+0x07); //year
break;
case 5:write_com(0x80+0x0a); //month
break;
case 6:write_com(0x80+0x0d); //day
break;
case 7:write_com(0x0c);
num=0;
//错误开始
WriteSet1302(0x8E,0x80);
WriteSet1302(0x80,((second/10)<<4|(second%10))); //根据写秒寄存器命令字,写入秒的值
WriteSet1302(0x82,((minute/10)<<4|(minute%10))); //根据写分寄存器命令字,写入分的值
WriteSet1302(0x84,((hour/10)<<4|(hour%10))); //根据写小时寄存器命令字,写入小时的值
WriteSet1302(0x86,((day/10)<<4|(day%10))); //根据写日寄存器命令字,写入日的值
WriteSet1302(0x88,((month/10)<<4|(month%10))); //根据写月寄存器命令字,写入月的值
WriteSet1302(0x8c,((year/10)<<4|(year%10))); //根据写年寄存器命令字,写入年的值
WriteSet1302(0x8E,0x80); //根据写状态寄存器命令字,写入保护指令
flag=0;
/*这里我将flag=0标志位放到了存储函数的上面,当进入case(也就是退出功能键的时候),它先检测到了flag=0;标志位等于0之后,主函数里的显示开始执行,然后后面的存储就被掠过了。。*/
break;
}
}
}
if(num!=0) //只有功能键被按下,增大,减小才有效
{
if(k6==0)
{
delayms(5);
if(k6==0)
{
while(!k6);
switch(num)
{
case 1:hour++;
if(hour==24)
hour=0;
display(0x44,hour);
write_com(0x80+0x45);
break;
case 2:minute++;
if(minute==60)
minute=0;
display(0x47,minute);
write_com(0x80+0x48);
break;
case 3:second++;
if(second==60)
second=0;
display(0x4a,second);
write_com(0x80+0x4b);
break;
case 4:year++;
if(year==99) //DS1302年最大2100
year=0;
display(0x06,year);
write_com(0x80+0x07); //年的地址,不管怎么改光标就是跑到了月的上面
case 5:month++;
if(month==13)
month=1;
display(0x09,month);
write_com(0x80+0x0a);
break;
case 6:day++;
if(day==32)
day=1;
display(0x0c,day);
write_com(0x80+0x0d);
break;
}
}
}
if(k7==0)
{
delayms(5);
if(k7==0)
{
while(!k7);
switch(num)
{
case 1:hour--;
if(hour==-1)
hour=23;
display(0x44,hour);
write_com(0x80+0x45);
break;
case 2:minute--;
if(minute==-1)
minute=59;
display(0x47,minute);
write_com(0x80+0x48);
break;
case 3:second--;
if(second==-1)
second=59;
display(0x4a,second);
write_com(0x80+0x4b);
break;
case 4:year--;
if(year==-1) //DS1302年最大2100
year=99;
display(0x06,year);
write_com(0x80+0x07);//年的地址
case 5:month--;
if(month==0)
month=12;
display(0x09,month);
write_com(0x80+0x0a);
break;
case 6:day--;
if(day==0)
day=31;
display(0x0c,day);
write_com(0x80+0x0d);
break;
}
}
}
}
}
|