DS1302是美国DALLAS公司推出的高性能、低功耗的实时时钟,附加31字节的静态RAM,采用SP三线接口与MCU进行同步通信,并可采用突发方式一次传送多个字节的时钟参数和RAM数据。实时时钟可提供秒、分、时、日、星期、月和年,一个月小于31天时可以自动调整,并具有润年补偿功能。简单来说,DS1302可以理解为一个电子手表,里面带有一个31字节的内存。当然,基本的使用方法和我们平时使用电子手表差不多,你可以设定时间,也可以读取时间,只不过这些工作是通过SPI接口有MCU去完成而已。
在DS1302中有两块存储器:日历时钟寄存器和今天RAM存储器。前者用于记录实时时间,后者用于记录其他数据。对于基本计时应用,重点关注的是日历时钟寄存器。设定时间参数就是往这些寄存器写入内容,读取实时时间也是从这些寄存器读出数据。
DS1302有关日历和时钟的寄存器有12个,我们最常用的有7个
这里数据格式为BCD码,是用十六进制来表示十进制。
例如,十六进制数0x13的值为整数19,但BCD码表示的是整数13。后续需要转换。
uchar read(uchar add)
{
uchar zhi;
uchar dat1,dat2;
CE=0;//初始CE线置0
SCLK=0;//初始时钟线置0
CE=1;//CE置1,开始传输
writebyte(add);
zhi=readbyte();
IO=0;//读完io置0
dat1=zhi/16; //0xff 1111 1111 ds1302读的是BCD码,需转为16进制
dat2=zhi%16;
zhi=dat1*10+dat2;//转为10进制
return zhi;
}
uchar readbyte()//循环读字节
{
uchar i;
uchar temp;
for(i=0;i<8;i++)
{
SCLK=0;
temp>>=1;//temp的各个二进制位右移一位,最高位补0再赋值给temp
if(IO)//如果io口有数据则读入
{
temp|=0x80;
}
SCLK=1;
}
return temp;
}
void writebyte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
SCLK=0;
IO=dat&0x01;
SCLK=1;
dat>>=1;
}
}
void write(uchar add,uchar dat)
{
uchar num;
CE=0;
SCLK=0;
CE=1;
writebyte(add);
num=(dat/10<<4)|(dat%10);//高四位 10位数 低四位 个位数 分开存,此处合并 10进制转BCD
writebyte(num);
CE=0;
}
完整代码
/*显示从23-59-30秒开始的自动时钟*/
#include <STC15F2K60S2.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit CE=P1^3;//复位脚
sbit IO=P2^3;
sbit SCLK=P1^7;//时钟线
void allinit();
void delayms(uchar z);
void display1(uchar yi,uchar er);
void display2(uchar san,uchar si);
void display3(uchar wu,uchar liu);
void display4(uchar qi,uchar ba);
void dsinit();
void write(uchar add,uchar dat);
void writebyte(uchar dat);
uchar read(uchar add);
uchar readbyte();
void readtime();
uchar code ds[]={30,59,23,10,10,1,16};
//sec min hour day month week year
uchar time[7];//时间数组
uchar code tab[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0XBF,0XFF};
uchar yi,er,san,si,wu,liu,qi,ba;
void delayms(uchar z) //@12.000MHz
{
unsigned char i, j;
uchar k;
for(k=z;k>0;k--)
{
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
void display1(uchar yi,uchar er)
{
P2=0XC0;
P0=0X01;
P2=0XFF;
P0=tab[yi];
delayms(1);
P2=0XC0;
P0=0X02;
P2=0XFF;
P0=tab[er];
delayms(1);
}
void display2(uchar san,uchar si)
{
P2=0XC0;
P0=0X04;
P2=0XFF;
P0=tab[san];
delayms(1);
P2=0XC0;
P0=0X08;
P2=0XFF;
P0=tab[si];
delayms(1);
}
void display3(uchar wu,uchar liu)
{
P2=0XC0;
P0=0X10;
P2=0XFF;
P0=tab[wu];
delayms(1);
P2=0XC0;
P0=0X20;
P2=0XFF;
P0=tab[liu];
delayms(1);
}
void display4(uchar qi,uchar ba)
{
P2=0XC0;
P0=0X40;
P2=0XFF;
P0=tab[qi];
delayms(1);
P2=0XC0;
P0=0X80;
P2=0XFF;
P0=tab[ba];
delayms(1);
}
void allinit()
{
P2=0X80;//打开控制数码管的573(u6),3-8译码器为001(ABC)
P0=0XFF;//关闭所有流水灯
P2=0XC0;//打开控制数码管位选的573,3-8译码器为011
P0=0XFF;
P2=0XFF;//打开控制数码管段选573,3-8译码器为111
P0=0XFF;//关闭所有数码管
P2=0XA0;//打开控制数码管573,3-8译码器为101
P0=0X00;//关闭继电器和蜂鸣器
}
void main()
{
allinit();
dsinit();
yi=11,er=11,san=11,si=11,wu=11,liu=11,qi=11,ba=11;
while(1)
{
readtime();
yi=time[2]/10;er=time[3]%10;san=10;
si=time[1]/10;wu=time[1]%10;liu=10;
qi=time[0]/10;ba=time[0]%10;
display1(yi,er);
display2(san,si);
display3(wu,liu);
display4(qi,ba);
}
}
uchar readbyte()//循环读字节
{
uchar i;
uchar temp;
for(i=0;i<8;i++)
{
SCLK=0;
temp>>=1;//temp的各个二进制位右移一位,最高位补0再赋值给temp
if(IO)//如果io口有数据则读入
{
temp|=0x80;
}
SCLK=1;
}
return temp;
}
uchar read(uchar add)
{
uchar zhi;
uchar dat1,dat2;
CE=0;
SCLK=0;
CE=1;
writebyte(add);
zhi=readbyte();
IO=0;//读完io置0
dat1=zhi/16; //0xff 1111 1111 ds1302读的是BCD码,需转为16进制
dat2=zhi%16;
zhi=dat1*10+dat2;//转为10进制
return zhi;
}
void readtime()
{
uchar i;
uchar add=0x81;
write(0x8e,0x00);
for(i=0;i<7;i++)
{
time[i]=read(add);
add=add+2;
}
write(0x8e,0x80);
}
void writebyte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
SCLK=0;
IO=dat&0x01;
SCLK=1;
dat>>=1;
}
}
void write(uchar add,uchar dat)
{
uchar num;
CE=0;
SCLK=0;
CE=1;
writebyte(add);
num=(dat/10<<4)|(dat%10);//高四位 10位数 低四位 个位数 分开存,此处合并 10进制转BCD
writebyte(num);
CE=0;
}
void dsinit()//初始化时间显示
{
uchar i;
uchar add=0x80;//初始化首地址 80h开始
write(0x8e,0x00);// 关掉写保护 打开进行操作
for(i=0;i<7;i++)
{
write(add,ds[i]);
add=add+2;
}
write(0x8e,0x80);//打开写保护
}