时钟芯片DS1302调整时间的思路是怎么样的最好能给一个调整时间的程序,谢谢

时钟芯片DS1302调整时间的思路是怎么样的最好能给一个调整时间的程序,谢谢,第1张

236 显示模块的设计

本设计中由于要对时间、温度进行显示,所以选择液晶显示屏1602模块作为输出。1602字符型LCD通常有14条引脚线或16条引脚线的LCD,多出来的2条线是背光电源线。它可以显示两行,每行16个字符,采用单+5V电源供电,外围电路配置简单,价格便宜,具有很高的性价比。1602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A”。管脚功能如表2-1所示。

表2-1 LCD1602引脚功能

引脚 符号 功能说明

1 VSS 一般接地

2 VDD 接电源(+5V)

3 V0 液晶显示器对比度调整端。

4 RS RS为寄存器选择。

5 R/W R/W为读写信号线。

6 E E(或EN)端为使能(enable)端,下降沿使能。

7 DB0 低4位三态、 双向数据总线 0位(最低位)

8 DB1 低4位三态、 双向数据总线 1位

9 DB2 低4位三态、 双向数据总线 2位

10 DB3 低4位三态、 双向数据总线 3位

11 DB4 高4位三态、 双向数据总线 4位

12 DB5 高4位三态、 双向数据总线 5位

13 DB6 高4位三态、 双向数据总线 6位

14 DB7 高4位三态、 双向数据总线 7位(最高位)

LCD1602主要管脚介绍:V0为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高,对比度过高时会产生鬼影使用时可以通过一个10K的电位器调整对比度。RS为寄存器选择端,高电平时选择数据寄存器,低电平时选择指令寄存器。R/W为读写信号线端,高电平时进行读 *** 作,低电平时进行写 *** 作。当RS和R/W共同为低电平时可以写入指令或者显示地址;当RS为高电平R/W 为低电平时可以写入数据。E为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。

将LCD1602的RS端和P20,R/W端和P21, E 端和P22相连,当RS=0时,对LCD1602写入指令;当RS=1时,对LCD1602写入数据。当R/W端接高电平时芯片处于读数据状态,反之处于写数据状态,E端为使能信号端。当R/W为高电平,E端也为高电平,RS为低电平时,液晶显示屏显示需要显示的示数。图211为1602液晶显示屏与单片机的硬件连接图。

图211 LCD液晶显示与单片机硬件连线图

附录二 部分程序说明

void delay(uint xms) //延时函数,有参函数//

{ uint x,y;

for(x=xms;x>0;x--)

for(y=110;y>0;y--);

}

//ds18b20芯片有关子程序/

void DS18B20_init() //初始化

{ uint i; tem_ds=0;

i=160; while(i>0) //给DS18B20单总线至少480uS的低电平信号

i--; tem_ds=1; //拉高15~60uS

i=8; while(i>0) i--;

}

void temwritebit(bit instruc_data) //写一位//

{ int time;

if(instruc_data)

{ tem_ds=0;

time=3; //延时14us

while(time>0)

time--;

tem_ds=1; //写"1"

time=8; //延时35us

while(time>0)

time--;

}

else

{

tem_ds=0; //写0

time=14; //延时62us

while(time>0)

time--;

}

tem_ds=1; //释放数据线

time++;time++;

}

bit temreadbit() //读一位//

{ uint i;

bit datbit;

tem_ds=0;

i++;

tem_ds=1;

i++;i++;

datbit=tem_ds; //读数据

i=10; //延时45 

while(i>0)

i--;

tem_ds=1;

return(datbit);

}

uchar temreadbyte() //读字节//

{ uchar i,j,dat;

dat=0;

for(i=0;i<8;i++)

{ j=temreadbit() ;

dat=(j<<7)|(dat>>1);

}

return(dat);

}

void temwritebyte(uchar instru) //写字节//

{ int i;

for(i=0;i<8;i++)

{temwritebit(instru&01);

instru>>=1;

}

}

uint get_tem() //获取温度//

{ uchar tem_L,tem_H;

DS18B20_init();

delay(1);

temwritebyte(0xcc); //写跳过ROM指令;

temwritebyte(0xbe); //读数据

tem_L=temreadbyte();

tem_H=temreadbyte();

tem=tem_H<<8|tem_L;

f_tem=tem00625;

tem=f_tem ;

return (tem);

}

//液晶相关程序/

write_1602com(uchar com) //液晶写入指令函数//

{ rs=0; //数据/指令选择置为指令

rw=0; //读写选择置为写

P0=com; //送入数据

delay(1);

en=1; //拉高使能端,为制造有效的下降沿做准备

delay(1);

en=0; //en由高变低,产生下降沿,液晶执行命令

}

write_1602dat(uchar dat) //液晶写入数据函数//

{ rs=1; //数据指令选择置为数据

rw=0; //读写选择置为写

P0=dat; //送入数据

delay(1);

en=1; //en置高电平,为制造下降沿做准备

delay(1);

en=0; //en由高变低,产生下降沿,液晶执行命令

}

lcd_init() //液晶初始化函数//

{ write_1602com(0x38); //设置液晶工作模式 意思:162行显示,57点阵,8位数据

write_1602com(0x01); //清显示

write_1602com(0x06); //整屏不移动,光标自动右移

write_1602com(yh+1); //日历显示固定符号从第一行第1个位置之后开始显示

write_1602com(0x0c); //开显示不显示光标

for(a=0;a<14;a++)

{ write_1602dat(tab1[a]); //向液晶屏写日历显示的固定符号部分

delay(3);

}

write_1602com(er+2); //时间显示固定符号写入位置,从第2个位置后开始显示

for(a=0;a<8;a++)

{write_1602dat(tab2[a]); //写显示时间固定符号,两个冒号

delay(3);

}

}

/DS1302有关子函数/

void write_byte(uchar dat) //写一个字节//

{ ACC=dat;

RST=1;

for(a=8;a>0;a--)

{ IO=ACC0;

SCLK=0; /在控制指令字输入后的下一个SCLK时钟的上升沿时/

SCLK=1; /数据被写入DS1302/

ACC=ACC>>1;

}

}

uchar read_byte() //读一个字节//

{ RST=1;

for(a=8;a>0;a--)

{ ACC7=IO;

SCLK=1;

SCLK=0;

ACC=ACC>>1;

}

return (ACC);

}

void write_1302(uchar add,uchar dat) //向1302芯片写函数,指定写入地址,数据//

{ RST=0;

SCLK=0;

RST=1;

write_byte(add);

write_byte(dat);

SCLK=1;

RST=0;

}

uchar read_1302(uchar add) //从1302读数据函数,指定读取数据来源地址//

{ uchar temp;

RST=0;

SCLK=0;

RST=1;

write_byte(add);

temp=read_byte();

SCLK=1;

RST=0;

return(temp);

}

uchar BCD_Decimal(uchar bcd) //BCD码转十进制函数//

{ uchar Decimal;

Decimal=bcd>>4; //BCD高四位表示十位,低四位个位,8421码;

return(Decimal=Decimal10+(bcd&=0x0F));

}

void ds1302_init() //1302芯片初始化子函数(2010-01-07,12:00:00,week4)//

{ RST=0;

SCLK=0;

write_1302(0x8e,0x00); //允许写,禁止写保护

write_1302(0x80,0x00); //向DS1302内写秒寄存器80H写入初始秒数据00

write_1302(0x82,0x00); //向DS1302内写分寄存器82H写入初始分数据00

write_1302(0x84,0x12); //向DS1302内写小时寄存器84H写入初始小时数据12

write_1302(0x8a,0x04); //向DS1302内写周寄存器8aH写入初始周数据4

write_1302(0x86,0x07); //向DS1302内写日期寄存器86H写入初始日期数据07

write_1302(0x88,0x01); //向DS1302内写月份寄存器88H写入初始月份数据01

write_1302(0x8c,0x10); //向DS1302内写年份寄存器8cH写入初始年份数据10

write_1302(0x8e,0x80); //打开写保护

}

void write_temp(uchar add,uchar dat) //向LCD写温度数据,并指定显示位置//

{ uchar gw,sw;

gw=dat%10; //取得个位数字

sw=dat/10; //取得十位数字

write_1602com(er+add); //er是头文件规定的值0x80+0x40

write_1602dat(0x30+sw); //数字+30得到该数字的LCD1602显示码

write_1602dat(0x30+gw); //数字+30得到该数字的LCD1602显示码

write_1602dat(0xdf); //显示温度的小圆圈符号,0xdf是液晶屏字符库的该符号地址码

write_1602dat(0x43); //显示"C"符号,0x43是液晶屏字符库里大写C的地址码

}

void write_sfm(uchar add,uchar dat) //向LCD写时分秒,有显示位置加显示数据,两个参数

{ uchar gw,sw;

gw=dat%10; //取得个位数字

sw=dat/10; //取得十位数字

write_1602com(er+add); //er是头文件规定的值0x80+0x40//年月日显示将er改为yh即可,其他相同;

write_1602dat(0x30+sw); //数字+30得到该数字的LCD1602显示码

write_1602dat(0x30+gw); //数字+30得到该数字的LCD1602显示码

}

void write_week(uchar week) //写星期函数//

{ write_1602com(yh+0x0c); //星期字符的显示位置

switch(week)

{ case 1:write_1602dat('M'); //星期数为1时,显示

write_1602dat('O');

write_1602dat('N');

break; //下面六种选择形式相同将括号中的字符相应修改就行了

}

}

void keyscan() //键盘扫描有关函数

{ if(key1==0) //key1为功能键(设置键)

{delay(9); //延时,用于消抖动

if(key1==0) //延时后再次确认按键按下

{buzzer=0; //蜂鸣器短响一次

delay(20);

buzzer=1;

while(!key1); //按键等待释放

key1n++;

if(key1n==9)

key1n=1; //设置按键共有秒、分、时、星期、日、月、年、返回,8个功能循环

switch(key1n)

{ case 1: TR0=0; //关闭定时器

write_1602com(er+0x09); //设置按键按动一次,秒位置显示光标

write_1602com(0x0f); //设置光标为闪烁

temp=(miao)/1016+(miao)%10; //秒数据写入DS1302

write_1302(0x8e,0x00);

write_1302(0x80,0x80|temp); //miao

write_1302(0x8e,0x80);

break;

case 2: write_1602com(er+6); //按2次fen位置显示光标

write_1602com(0x0f); //下面case3~case7基本相同,改变地址就行了

case 8: write_1602com(0x0c); //按动到第8次,设置光标不闪烁

TR0=1;//打开定时器

temp=(miao)/1016+(miao)%10;

write_1302(0x8e,0x00);

write_1302(0x80,0x00|temp);//miao数据写入DS1302

write_1302(0x8e,0x80);

break;

}

}

}

//------------------------------加键key2----------------------------

if(key1n!=0) //当key1按下以下。再按以下键才有效(按键次数不等于零)

{if(key2==0) //上调键

{delay(10);

if(key2==0)

{buzzer=0; //蜂鸣器短响一次

delay(20);

buzzer=1;

while(!key2);

switch(key1n)

{ case 1:miao++; //设置键按动1次,调秒

if(miao==60)

miao=0;

write_sfm(0x08,miao);

temp=(miao)/1016+(miao)%10; //十进制转换成DS1302要求的BCD码

write_1302(0x8e,0x00); //允许写,禁止写保护

write_1302(0x80,temp); ;//向DS1302内写秒寄存器80H写入调整后的数据BCD码

write_1302(0x8e,0x80); //打开写保护

write_1602com(er+0x09); //因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位

break;

//下面case2~case7和case1相同将相应的地址改一下就行了。

}

}

}

下面的key3与加键key2形式基本相同;

}

}

void init() //定时器、计数器设置函数

{ TMOD=0x01; //指定定时/计数器的工作方式为1

TH0=0; //定时器T0的高四位=0

TL0=0; //定时器T0的低四位=0

EA=1; //系统允许有开放的中断

ET0=1; //允许T0中断

TR0=1; //开启中断,启动定时器

}

void main() //主函数

{ lcd_init(); //调用液晶屏初始化子函数

ds1302_init(); //调用DS1302时钟的初始化子函数

init(); //调用定时计数器的设置子函数

buzzer=0; //蜂鸣器长响一次

delay(80);

buzzer=1;

while(1) //无限循环下面的语句:

{keyscan();} //调用键盘扫描子函数

}

Void timer0() interrupt 1 //取得并显示日历和时间

{ TH0=0; //重复初始值

TL0=0;

DS18B20_init();

delay(1);

temwritebyte(0xcc); //写跳过ROM指令;

temwritebyte(0x44); //启动温度转换;

flag=get_tem(); //将18b20运行返回的函数结果送到变量flag中,用于显示

//读取秒时分周日月年七个数据(DS1302的读寄存器与写寄存器不一样):

miao = BCD_Decimal(read_1302(0x81));//下面分秒年月日形式相同,改变地址就可以了

//显示温度、秒、时、分数据:

write_temp(12,flag);//显示温度,从第二行第12个字符后开始显示

write_sfm(8,miao);//秒,从第二行第8个字后开始显示(调用时分秒显示子函数)

write_sfm(5,fen);//分,从第二行第5个字符后开始显示

write_sfm(2,shi);//小时,从第二行第2个字符后开始显示

//显示日、月、年数据:

write_nyr(9,ri);//日期,从第二行第9个字符后开始显示

write_nyr(6,yue);//月份,从第二行第6个字符后开始显示

write_nyr(3,nian);//年,从第二行第3个字符后开始显示

write_week(week);

}

我的是1602显示的,很容易改成数码管的,给你参考一下。

#include<stc12c5ah>

#include<intrinsh>

#define uchar unsigned char

#define uint unsigned int

sbit lcrs=P2^2;//数据/命令

sbit lcwr=P2^1;//读/写

sbit lcden=P2^0;//使能

sbit SCLK = P2^5;//DS1302时钟口P10

sbit IO = P2^4; //DS1302数据口P11

sbit RS = P2^3; //DS1302片选口P12

sbit key1=P4^4;

sbit key2=P4^5;

sbit key3=P4^1;

sbit key4=P4^6;

//秒 分 时 日 月 星期 年

uchar init[] = {0x45, 0x40, 0x20, 0x018, 0x04, 0x03, 0x12};

uchar now[7];

uchar code table[]={48,49,50,51,52,53,54,55,56,57};

uchar dis[16];

uchar H;//定义小时

uchar Mi;//定义分钟

uchar S;//定义秒

uchar Y;//定义年

uchar Mo;//定义月

uchar D;//定义日期

uchar W;//定义星期

uchar key=0;

uchar bcdto(uchar temp);

void delayms(uint ms);

void DS1302_Initial();

void DS1302_SetTime(uchar p);

void DS1302_GetTime(uchar p);

////////////////////// 延时子程序/////////////////////////////

void delayms(uint ms) //在110592M晶振下,单周期指令的ms级延时

{

uint i;

while(ms--)

{

for(i = 0; i < 850; i++);

}

}

//////1062/////////

void ydelay(uchar x)

{

uint a,b;

for(a=x;a>0;a--)

for(b=10;b>0;b--);

}

void write_com(uchar com)

{

P0=com;

lcwr=0;

lcrs=0;

lcden=0;

ydelay(20);

lcden=1;

ydelay(20);

lcden=0;

lcwr=1;

}

void write_date(uchar date)//写数据

{

P0=date;

lcwr=0;

lcrs=1;

lcden=0;

ydelay(20);

lcden=1;

ydelay(20);

lcden=0;

lcwr=1;

}

void init1602()//初始化

{

write_com(0x38);//设置显示模式

ydelay(40);

write_com(0x0c);//开显示

ydelay(40);

write_com(0x06);//指针和光标自动加一

ydelay(40);

write_com(0x01);//清屏指令

ydelay(40);

}

void display(uchar a)//显示

{

uint i;

dis[0]='2';dis[1]='0';dis[2]=table[Y/10];dis[3]=table[Y%10];

dis[4]='';dis[5]=table[Mo/10];dis[6]=table[Mo%10];

dis[7]='';dis[8]=table[D/10];dis[9]=table[D%10];dis[10]=' ';

dis[11]='W';dis[12]='e';dis[13]='e';dis[14]=' ';

dis[15]=table[W%10];

if(a==3)

{dis[2]=' ';dis[3]=' ';}

else if(a==4)

{dis[5]=' ';dis[6]=' ';}

else if(a==5)

{dis[8]=' ';dis[9]=' ';}

else if(a==6)

{dis[15]=' ';}

write_com(0x0c);

write_com(0xc0);

for(i=0;i<16;i++)

write_date(dis[i]);

dis[0]=table[H/10];dis[1]=table[H%10];dis[2]=':';

dis[3]=table[Mi/10]; dis[4]=table[Mi%10];dis[5]=':';

dis[6]=table[S/10];dis[7]=table[S%10];dis[8]=' ';

if(a==1)

{

dis[0]=' ';dis[1]=' ';

}

else if(a==2)

{dis[3]=' ';dis[4]=' ';}

write_com(0x80+4);

for(i=0;i<9;i++)

write_date(dis[i]);

}

uchar tobcd(uchar temp)//十进制转换成BCD码

{

uchar a,b,c;

a=temp;

b=0;

if(a>=10)

{

while(a>=10)

{

a=a-10;

b=b+16;

c=a+b;

temp=c;

}

}

return temp;

}

uchar bcdto(uchar temp)//BCD码转换成十进制

{

uchar a,b,c;

a=temp;

b=0;

if(a>=16)

{

while(a>=16)

{

a=a-16;

b=b+10;

c=a+b;

temp=c;

}

}

return temp;

}

void keyf(uchar num,uchar up,uchar du)

{

uint z;

if(key2==0)

{

delayms(10);

if(key2==0)

{

if(num>=up)

{

if((key==4)||(key==5)||(key==6))

num=1;

else

num=0;

}

else

num=num+1;

for(z=0;z<50;z++)

{

display(0);

if(key2!=0)

break;

}

while(!key2)

{

for(z=0;z<25;z++)

{display(0);}

if(num>=up)

{

if((key==4)||(key==5)||(key==6))

num=1;

else

num=0;

}

else

num=num+1;

}

}

}

if(key3==0)

{

delayms(10);

if(key3==0)

{

if(num==du)

{

if(key==1)

num=23;

else if(key==2)

num=59;

else if(key==3)

num=99;

else if(key==4)

num=12;

else if(key==5)

num=31;

else if(key==6)

num=7;

}

else

num=num-1;

for(z=0;z<50;z++)

{

display(0);

if(key3!=0)

break;

}

while(!key3)

{

for(z=0;z<25;z++)

{display(0);}

if(num==du)

{

if(key==1)

num=23;

else if(key==2)

num=59;

else if(key==3)

num=99;

else if(key==4)

num=12;

else if(key==5)

num=31;

else if(key==6)

num=7;

}

else

num=num-1;

}

}

}

}

void keyjc()

{

uint i,j;

if(key1==0)

delayms(10);

if(key1==0)

{

key++;

if(key>=7)

{

key=0;

display(0);

}

while(!key1);

if(key==1)

{

for(i=0;i<25;i++)

{

for(j=0;j<25;j++)

display(1);

for(j=0;j<25;j++)

display(0);

if((key2==0)||(key3==0))

{

delayms(10);

if((key2==0)||(key3==0))

{

i=0;

keyf(&H,23,0);

}

}

if(key1==0)

{

delayms(10);

if(key1==0)

{

key=2;

while(!key1);

break;

}

}

}

}

if(key==2)

{

for(i=0;i<25;i++)

{

for(j=0;j<25;j++)

display(2);

for(j=0;j<25;j++)

display(0);

if((key2==0)||(key3==0))

{

delayms(10);

if((key2==0)||(key3==0))

{

i=0;

keyf(&Mi,59,0);

}

}

if(key1==0)

{

delayms(10);

if(key1==0)

{

key=3;

while(!key1);

break;

}

}

}

}

if(key==3)

{

for(i=0;i<25;i++)

{

for(j=0;j<25;j++)

display(3);

for(j=0;j<25;j++)

display(0);

if((key2==0)||(key3==0))

{

delayms(10);

if((key2==0)||(key3==0))

{

i=0;

keyf(&Y,99,0);

}

}

if(key1==0)

{

delayms(10);

if(key1==0)

{

key=4;

while(!key1);

break;

}

}

}

}

if(key==4)

{

for(i=0;i<25;i++)

{

for(j=0;j<25;j++)

display(4);

for(j=0;j<25;j++)

display(0);

if((key2==0)||(key3==0))

{

delayms(10);

if((key2==0)||(key3==0))

{

i=0;

keyf(&Mo,12,1);

}

}

if(key1==0)

{

delayms(10);

if(key1==0)

{

key=5;

while(!key1);

break;

}

}

}

}

if(key==5)

{

for(i=0;i<25;i++)

{

for(j=0;j<25;j++)

display(5);

for(j=0;j<25;j++)

display(0);

if((key2==0)||(key3==0))

{

delayms(10);

if((key2==0)||(key3==0))

{

i=0;

keyf(&D,31,1);

}

}

if(key1==0)

{

delayms(10);

if(key1==0)

{

key=6;

while(!key1);

break;

}

}

}

}

if(key==6)

{

for(i=0;i<25;i++)

{

for(j=0;j<25;j++)

display(6);

for(j=0;j<25;j++)

display(0);

if((key2==0)||(key3==0))

{

delayms(10);

if((key2==0)||(key3==0))

{

i=0;

keyf(&W,7,1);

}

}

if(key1==0)

{

delayms(10);

if(key1==0)

{

key=0;

while(!key1);

break;

}

}

}

}

init[0]=tobcd(S);

init[1]=tobcd(Mi);

init[2]=tobcd(H);

init[3]=tobcd(D);

init[4]=tobcd(Mo);

init[5]=tobcd(W);

init[6]=tobcd(Y);

DS1302_SetTime(init);

key=0;

}

}

void main()

{

init1602();

DS1302_Initial(); //初始化DS1302

P4SW=0x70;

while (1)

{

delayms(10);

DS1302_GetTime(now);//读取当前时间

Y=bcdto(now[6]);

Mo=bcdto(now[4]);

D=bcdto(now[3]);

W=bcdto(now[5]);

if(W==7)

W=8;

H=bcdto(now[2]);

Mi=bcdto(now[1]);

S=bcdto(now[0]);

display(0);

keyjc();

}

}

/

延时X微秒(STC12C5A60S2@12M)

不同的工作环境,需要调整此函数

此延时函数是使用1T的指令周期进行计算,与传统的12T的MCU不同

/

void Delay()

{

_nop_();

_nop_();

}

/

从DS1302读1字节数据

/

uchar DS1302_ReadByte()

{

uchar i;

uchar dat = 0;

for (i=0; i<8; i++) //8位计数器

{

SCLK = 0; //时钟线拉低

Delay(); //延时等待

dat >>= 1; //数据右移一位

if (IO) dat |= 0x80; //读取数据

SCLK = 1; //时钟线拉高

Delay(); //延时等待

}

return dat;

}

/

向DS1302写1字节数据

/

void DS1302_WriteByte(uchar dat)

{

char i;

for (i=0; i<8; i++) //8位计数器

{

SCLK = 0; //时钟线拉低

Delay(); //延时等待

dat >>= 1; //移出数据

IO = CY; //送出到端口

SCLK = 1; //时钟线拉高

Delay(); //延时等待

}

}

/

读DS1302某地址的的数据

/

uchar DS1302_ReadData(uchar addr)

{

uchar dat;

RS = 0;

Delay();

SCLK = 0;

Delay();

RS = 1;

Delay();

DS1302_WriteByte(addr); //写地址

dat = DS1302_ReadByte(); //读数据

SCLK = 1;

RS = 0;

return dat;

}

/

往DS1302的某个地址写入数据

/

void DS1302_WriteData(uchar addr, uchar dat)

{

RS = 0;

Delay();

SCLK = 0;

Delay();

RS = 1;

Delay();

DS1302_WriteByte(addr); //写地址

DS1302_WriteByte(dat); //写数据

SCLK = 1;

RS = 0;

}

/

写入初始时间

/

void DS1302_SetTime(uchar p)

{

uchar addr = 0x80;

uchar n = 7;

DS1302_WriteData(0x8e, 0x00); //允许写 *** 作

while (n--)

{

DS1302_WriteData(addr, p++);

addr += 2;

}

DS1302_WriteData(0x8e, 0x80); //写保护

}

/

读取当前时间

/

void DS1302_GetTime(uchar p)

{

uchar addr = 0x81;

uchar n = 7;

while (n--)

{

p++ = DS1302_ReadData(addr);

addr += 2;

}

}

/

初始化DS1302

/

void DS1302_Initial()

{

RS = 0;

SCLK = 0;

DS1302_WriteData(0x8e, 0x00);

DS1302_WriteData(0x80, 0x00);

DS1302_WriteData(0x90, 0xaa);

DS1302_WriteData(0x8e, 0x80);

}

  现在有很多流行的串行时钟芯片,如DS1302,DS1307,PCF8485等,由于简单的接口,低成本和易用性,他们被广泛应用于电话、传真、便携式仪器等产品领域。在本实验中,我们将使用DS1302实时时钟(RTC)模块获取当前日期和时间。

  DS1302可以用于数据记录,特别是对某些具有特殊意义的数据点的记录,能实现数据与出现该数据的时间同时记录。这种记录对长时间的连续测控系统结果的分析,及对异常数据出现的原因的查找具有重要意义。

  传统的数据记录方式是隔时采样或定时采样,没有具体的时间记录,因此,只能记录数据而无法准确记录其出现的时间;若采用单片机计时,一方面需要采用计数器,占用硬件资源,另一方面需要设置中断、查询等,同样耗费单片机的资源,而且,某些测控系统可能不允许。但是,如果在系统中采用时钟芯片DS1302,则能很好地解决这个问题。

★Raspberry Pi 3主板1

★树莓派电源1

★40P软排线1

★DS1302实时时钟模块1

★面包板1

★跳线若干

  DS1302是DALLAS(达拉斯)公司出的一款涓流充电时钟芯片,2001年DALLAS被MAXIM(美信)收购。

  DS1302实时时钟芯片广泛应用于电话、传真、便携式仪器等产品领域,他的主要性能指标如下:

  1、DS1302是一个实时时钟芯片,可以提供秒、分、小时、日期、月、年等信息,并且还有软年自动调整的能力,可以通过配置AM/PM来决定采用24小时格式还是12小时格式。

  2、拥有31字节数据存储RAM。

  3、串行I/O通信方式,相对并行来说比较节省IO口的使用。

  4、DS1302的工作电压比较宽,大概是20V~55V都可以正常工作。

  5、DS1302这种时钟芯片功耗一般都很低,它在工作电压20V的时候,工作电流小于300nA。

  6、DS1302共有8个引脚,有两种封装形式,一种是DIP-8封装,芯片宽度(不含引脚)是300mil,一种是SOP-8封装,有两 种宽度,一种是150mil,一种是208mil。我们看一下DS1302的引脚封装图:

  7、当供电电压是5V的时候,兼容标准的TTL电平标准,这里的意思是,可以完美的和单片机进行通信。

   8、由于DS1302是DS1202的升级版本,所以所有的功能都兼容DS1202。此外DS1302有两个电源输入,一个是主电源, 另外一个是备用电源,比如可以用电池或者大电容,这样是为了保证系统掉电的情况下,我们的时钟还会继续走。如果使用的是充电电池,还可以在正常工作时,设置充电功能,给我们的备用电池进行充电。

  DS1302的特点第二条“拥有31字节数据存储RAM”,这是DS1302额外存在的资源。这31字节的RAM相当于一个存储器一样,我们编写单片机程序的时候,可以把我们想存储的数据存储在DS1302里边,需要的时候读出来,这块功能和EEPROM有点类似,相当于一个掉电丢失数据的“EEPROM”,如果我们的时钟电路加上备用电池,那么这31个字节的RAM就可以替代EEPROM的功能了。

  DS1302一共有8个引脚,下边要根据引脚分布图和典型电路图来介绍一下每个引脚的功能:

  DS1302的电路一个重点就是时钟电路,它所使用的晶振是一个32768k的晶振,晶振外部也不需要额外添加其他的电容或者电阻电路了。时钟的精度,首先取决于晶振的精度以及晶振的引脚负载电容。如果晶振不准或者负载电容过大过小,都会导致时钟误差过大。在这一切都搞定后,最终一个考虑因素是晶振的温漂。随着温度的变化,晶振往往精度会发生变化,因此,在实际的系统中,其中一种方法就是经常校对。比如我们所用的电脑的时钟,通常我们会设置一个选项“将计算机设置于internet时间同步”。选中这个选项后,一般可以过一段时间,我们的计算机就会和internet时间校准同步一次。

  对DS1302的 *** 作就是对其内部寄存器的 *** 作,DS1302内部共有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式。此外,DS1302还有年份寄存器、控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等。时钟突发寄存器可一次性顺序读/写除充电寄存器以外的寄存器。

  DS1302的一条指令一个字节8位,其中第7位(即最高位)是固定1,这一位如果是0的话,那写进去是无效的。第6位是选择RAM还是CLOCK的,这里主要讲CLOCK时钟的使用,它的RAM功能我们不用,所以如果选择CLOCK功能,第6位是0,如果要用RAM,那第6位就是1。从第5到第1位,决定了寄存器的5位地址,而第0位是读写位,如果要写,这一位就是0,如果要读,这一位就是1。

  DS1302时钟的寄存器,其中8个和时钟有关的,5位地址分别是00000一直到00111这8个地址,还有一个寄存器的地址是01000,这是涓流充电所用的寄存器,我们这里不讲。在DS1302的数据手册里的地址,直接把第7位、第6位和第0位值给出来了,所以指令就成了80H、81H那些了,最低位是1,那么表示读,最低位是0表示写。

  寄存器一:最高位CH是一个时钟停止标志位。如果我们的时钟电路有备用电源部分,上电后,我们要先检测一下这一位,如果这一位是0,那说明我们的时钟在系统掉电后,由于备用电源的供给,时钟是持续正常运行的;如果这一位是1,那么说明我们的时钟在系统掉电后,时钟部分不工作了。若我们的Vcc1悬空或者是电池没电了,当我们下次重新上电时,读取这一位,那这一位就是1,我们可以通过这一位判断时钟在单片机系统掉电后是否持续运行。剩下的7位高3位是秒的十位,低4位是秒的个位,这里注意再提一次,DS1302内部是BCD码,而秒的十位最大是5,所以3个二进制位就够了。

  寄存器二:bit7没意义,剩下的7位高3位是分钟的十位,低4位是分钟的个位。

  寄存器三:bit7是1的话代表是12小时制,是0的话代表是24小时制,bit6固定是0,bit5在12小时制下0代表的是上午,1代表的是下午,在24小时制下和bit4一起代表了小时的十位,低4位代表的是小时的个位。

  寄存器四:高2位固定是0,bit5和bit4是日期的十位,低4位是日期的个位。

  寄存器五:高3位固定是0,bit4是月的十位,低4位是月的个位。

  寄存器六:高5位固定是0,低3位代表了星期。

  寄存器七:高4位代表了年的十位,低4位代表了年的个位。这里特别注意,这里的00到99年指的是2000年到2099年。

  寄存器八:bit7是一个保护位,如果这一位是1,那么是禁止给任何其他的寄存器或者那31个字节的RAM写数据的。因此在写数据之前,这一位必须先写成0。

   物理上,DS1302的通信接口由3个口线组成,即RST,SCLK,I/O。其中RST从低电平变成高电平启动一次数据传输过程,SCLK是时钟线,I/O是数据线。这个DS1302的通信线定义和SPI很像,事实上,DS1302的通信是SPI的变异种类,它用了SPI的通信时序,但是通信的时候没有完全按照SPI的规则来,下面我们介绍DS1302的变异SPI通信方式。

  请注意数据是对时钟信号敏感的,而且一般数据是在下降沿写入,上升沿读出。平时SCLK保持低电平,当需要写命令或者写数据时,在时钟输出变为高电平之前先输出数据;当需要读数据时,在时钟输出变为高电平之前采样读取数据。

   第1步: 连接电路。

   第2步: DS1302的Python程序比较复杂,我们先编写一个模块ds1302py,在里面创建一个类DS1302(),在里面编写读取时钟信息等方法。

   第3步: 编写实际控制程序,导入上面的模块ds1302。运行本文件,不断循环读取并打印时钟信息。

  实验结果示例:

1302c

#include<DS1302h>

#include<keyh>

uchar bit_ser[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};

uchar seven_seg[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

/时间显示/

void timer0_init(void)  //T0初始化函数,用于时间的动态显示

{

TMOD = 0x21;

TL0 = (65536-5000) % 256;

TH0 = (65536-5000) / 256;

EA = 1;

ET0 = 1;

TR0 = 1;

}

void timer0_isr(void) interrupt 1  //T0中断处理函数

{

char flag;   //flag用于表示调整时闪烁的亮或灭

TR0 = 0;

TL0 = (65536-5000) % 256;

TH0 = (65536-5000) / 256;

TR0 = 1;

flag = x / 100  0xff; //设置闪烁标志,如果x大于100则flag为0xff,小于100则为0x00

x++;

if(x > 200)

x = 0;

switch(i)

{

case 0:

P2 = bit_ser[0];

if(setflag == 3)  //根据setflag的值判断当前位是否需要闪烁

P0 = flag | seven_seg[dis_buffer[0]];

else

P0 = seven_seg[dis_buffer[0]];

break;

case 1:

P2 = bit_ser[1];

if(setflag == 3)

P0 =flag | seven_seg[dis_buffer[1]];

else

P0 =seven_seg[dis_buffer[1]];

break;

case 2:

P2 = bit_ser[2];

if(setflag == 2)

P0 =flag | seven_seg[dis_buffer[2]];

else

P0 =seven_seg[dis_buffer[2]];

break;

case 3:

P2 = bit_ser[3];

if(setflag == 2)

P0 =flag | seven_seg[dis_buffer[3]];

else

P0 =seven_seg[dis_buffer[3]];

break;

case 4:

P2 = bit_ser[4];

if(setflag == 1)

P0 =flag | seven_seg[dis_buffer[4]];

else

P0 =seven_seg[dis_buffer[4]];

break;

case 5:

P2 = bit_ser[5];

if(setflag == 1)

P0 =flag | seven_seg[dis_buffer[5]];

else

P0 =seven_seg[dis_buffer[5]];

break;

}

i++;

if(i >= 6)

{

i = 0;

if(j == 10)

{

j = 0;

if(setflag == 0)

DS1302_GetTime(&Time); //如果setflag是0,就从1302中读出时间,因为setflag不是0时,说明处于调整状态,不需要读时间

dis_buffer[5] = TimeSecond % 10; //把当前时间放入显示缓冲区

dis_buffer[4] = TimeSecond / 10;

dis_buffer[3] = TimeMinute % 10;

dis_buffer[2] = TimeMinute / 10;

dis_buffer[1] = TimeHour % 10;

dis_buffer[0] = TimeHour / 10;

}

j++;

}

}

void main()

{

Initial_DS1302(Time);

timer0_init();

while(1)

{

set_down();

timer_down();

up_down();

down_down();

beepflag_down();

if(setflag == 0 && TimeHour == romhour && TimeMinute == romminute && Beepflag == 1) //判断蜂鸣器是否要响

Beep = !Beep;

}

}

//keyc

#include<reg51h>

#define uchar unsigned char

#define uint unsigned int

uchar i = 0,j = 0,x = 0,setflag,flag_set,flag_timer;   //setflag用来表示调整的位置,flag_set和flag_timer分别表示当前处于调整状态还是定时状态

SYSTEMTIME Time={0,20,15,3,30,6,10};    //系统时间的初始值2010年6月30日星期三,15时20分0秒

char dis_buffer[6];    //存放显示数据的缓冲区

sbit Beep_flag = P3^2;    //蜂鸣器的接口

sbit key_timer = P3^4;    //定时按钮

sbit key_set = P3^5;    //调整按钮

sbit key_up = P3^6;    //增加按钮

sbit key_down = P3^7;    //减小按钮

char romhour,romminute,romsec;    //分别存放定时的时,分,秒

bit Beepflag;    //标记闹钟是否开启

//延时函数

void delays(uchar x)

{

while(x) x--;

}

//设置键的处理函数

void set()

{

setflag ++;

flag_set = 1;

if(setflag >= 4)

{

setflag = 0;

flag_set = 0;

Initial_DS1302(Time);

}

}

//定时间的处理函数

void timer()

{

setflag ++;

flag_timer = 1;

if(setflag == 1)

{

TimeHour = romhour;

TimeMinute = romminute;

TimeSecond = romsec;

}

else if(setflag >= 4)

{

setflag = 0;

flag_timer = 0;

romhour = TimeHour;

romminute = TimeMinute;

romsec = TimeSecond;

}

}

//增加键的处理函数

void up()

{

switch(setflag)

{

case 0:

break;

case 1:

TimeSecond ++;

if(TimeSecond >= 60)

TimeSecond = 0;

break;

case 2:

TimeMinute ++;

if(TimeMinute >= 60)

TimeMinute = 0;

break;

case 3:

TimeHour ++;

if(TimeHour >= 24)

TimeHour = 0;

break;

}

}

//减小键的处理函数

void down()

{

switch(setflag)

{

case 0:

break;

case 1:

TimeSecond --;

if(TimeSecond < 0)

TimeSecond = 59;

break;

case 2:

TimeMinute --;

if(TimeMinute < 0)

TimeMinute = 59;

break;

case 3:

TimeHour --;

if(TimeHour < 0)

TimeHour = 23;

break;

}

}

//设置键的扫描函数

void set_down()

{

if(key_set == 0 && flag_timer == 0)

{

delays(100);

if(key_set == 0)

{

set();

}

while(!key_set);

}

}

//定时键的扫描函数

void timer_down()

{

if(key_timer == 0 && flag_set == 0)

{

delays(100);

if(key_timer == 0)

{

timer();

}

while(!key_timer);

}

}

//增加键的扫描函数

void up_down()

{

if(key_up == 0 && setflag != 0)

{

delays(100);

if(key_up == 0)

{

up();

while(!key_up);

}

}

}

//减少键的处理函数

void down_down()

{

if(key_down == 0 && setflag != 0)

{

delays(100);

if(key_down == 0)

{

down();

while(!key_down);

}

}

}

//定时开关的扫描处理函数

void beepflag_down()

{

if(Beep_flag == 0)

{

delays(100);

{

Beepflag = !Beepflag;

while(!Beep_flag);

}

}

}

//ds1302h

#ifndef _REAL_TIMER_DS1302

#define _REAL_TIMER_DS1302

#include <REG51h>

sbit  DS1302_CLK = P1^1;              //实时时钟时钟线引脚

sbit  DS1302_IO  = P1^2;              //实时时钟数据线引脚

sbit  DS1302_RST = P1^3;              //实时时钟复位线引脚

sbit  ACC0 = ACC^0;

sbit  ACC7 = ACC^7;

sbit  Beep = P2^7;

typedef struct __SYSTEMTIME__

{   char Second;

char Minute;

char Hour;

char Week;

char Day;

char Month;

char Year;

}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;            //相当于汇编中的 RRC

DS1302_CLK = 1;

DS1302_CLK = 0;                 //发一个高跳变到低的脉冲

ACC = ACC >> 1;

}

}

unsigned char DS1302OutputByte(void)  //实时时钟读取一字节(内部函数)

{  unsigned char i;

for(i=8; i>0; i--)

{ ACC = ACC >>1;          //相当于汇编中的 RRC

ACC7 = 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;                  //RST 0->1->0,CLK 0->1

}

unsigned char Read1302(unsigned char ucAddr) //读取DS1302某地址的数据

{ unsigned char ucData;

DS1302_RST = 0;

DS1302_CLK = 0;

DS1302_RST = 1;                      //enable

DS1302InputByte(ucAddr|0x01);        // 地址,命令

ucData = DS1302OutputByte();         // 读1Byte数据

DS1302_CLK = 1;                      //RST 0->1->0,CLK 0->1

DS1302_RST = 0;

return(ucData);

}

void DS1302_SetProtect(bit flag)        //是否写保护

{ if(flag)

Write1302(0x8E,0x80); //WP=1,不能写入

else

Write1302(0x8E,0x00);//WP=0,可以写入

}

void DS1302_SetTime(unsigned char Address, unsigned char Value)        // 设置时间函数

{ DS1302_SetProtect(0);

Write1302(Address, ((Value/10)<<4 | (Value%10))); //高4位为十位,低4位为个位

DS1302_SetProtect(1);

}

//获取时间函数,从DS1302内读取时间然后存入Time内

void DS1302_GetTime(SYSTEMTIME Time)

{ unsigned char ReadValue;

ReadValue = Read1302(DS1302_SECOND);

Time->Second = ((ReadValue&0x70)>>4)10 + (ReadValue&0x0F);//转换成10进制的秒

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);

}

//利用STime初始化DS1302

void Initial_DS1302(SYSTEMTIME STime)

{ unsigned char Second=Read1302(DS1302_SECOND);

if(Second&0x80)   DS1302_SetTime(DS1302_SECOND,0);  //如果第七为1(表明没有启动), 则启动时钟

DS1302_SetTime(DS1302_SECOND,STimeSecond);  //设定起始时间

DS1302_SetTime(DS1302_MINUTE,STimeMinute);

DS1302_SetTime(DS1302_HOUR,STimeHour);

DS1302_SetTime(DS1302_DAY,STimeDay);

DS1302_SetTime(DS1302_MONTH,STimeMonth);

DS1302_SetTime(DS1302_YEAR,STimeYear);

DS1302_SetTime(DS1302_WEEK,STimeWeek);

}

#endif

以上就是关于时钟芯片DS1302调整时间的思路是怎么样的最好能给一个调整时间的程序,谢谢全部的内容,包括:时钟芯片DS1302调整时间的思路是怎么样的最好能给一个调整时间的程序,谢谢、有木有大佬帮我解释一下这段ds1302时钟芯片程序是什么意思、ds1302时钟芯片工作原理等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/9397489.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-27
下一篇 2023-04-27

发表评论

登录后才能评论

评论列表(0条)

保存