一.概述
HT1380/1381是一款串行时钟芯片,提供年、月、日、星期、时、分、秒数据信息,每月的不同日数和闰年都会自动调整,有12小制和24小时制两种时钟模式可供选择。
HT1380/1381和DS1302都是很常用的实时时钟芯片,这两种芯片的管脚兼容,操作时序和命令均相同,唯一不同的是DS1302多了31个字节的RAM和涓流充电功能。所以,HT1380/1381的驱动程序和DS1302的驱动程序可以直接互相套用(涉及到RAM和涓流充电的操作部分除外)。
编写实时时钟芯片驱动程序少不了向芯片写入初始化时间或设定时间,初次编写这个驱动程序的人可能会被一个问题困扰---写入的时间读出来总是变成乱码。这是因为没有把设定的时间化为BCD码(二进制数码表示的十进制数),而是直接写入造成的。将一个常数或者一个变量化成BCD码的公式:变量/10*16+变量%10=变量的BCD码。同样,从芯片读出来BCD码也要化成十进制数,显示器才能正常显示数值,公式:读值/16*10+读值%16=读值的十进制数。
二.电路设计
在WSF-51DB开发板上,利用AT89S52单片机的P0.1、P0.2、P0.3口来驱动HT1380/1381。为了让PCB板兼容DS1302芯片,故采用DS1302的管脚定义和接线,如下图,实际上,HT1380/1381的第一脚是悬空的,第八脚才是电源引脚(由备份电池供电)。经笔者测试,第一脚和第八脚都悬空,HT1380/1381也能正常工作。备份电池使用的是CR2032 3.6V电池。32.768KHZ的晶振直接和芯片连接。
三.软件设计
/**************************************************************************
*程序名称:HT1380/1381(DS1302)驱动程序
*程序功能:读取HT1380/1381的时钟数据,数码管显示时间。
* MCU型号:AT89S52-24PU
*时钟频率:12MHZ
**************************************************************************/
#include<reg52.h>
unsigned char code segmcode[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//共阴极数码管段码
unsigned char code bitcode[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
//8位共阴极数码管位码
sbit ser=P2^0;//74HC595串行数据输入
sbit oe=P2^1;//74HC595使能
sbit rclk=P2^2;//74HC595数据锁存
sbit srclk=P2^3;//74HC595串行时钟
sbit sclk=P0^0;//HT1380/1381时钟线
sbit io=P0^1;//HT1380/1381数据线
sbit rst=P0^2;//HT1380/1381复位线
//延时1毫秒函数:
void Delay1ms(unsigned int t)
{
unsigned int x,y;
for(x=t;x>0;x--)
for(y=120;y>0;y--);
}
//任意位数码管显示一个字符函数:
void DTDisplayChar(unsigned char segmd,unsigned char bitd )//数码管段码和数码管位码
{
unsigned char i;
unsigned int dat;
oe=1;//74HC595禁止
dat=bitd;
dat=(dat<<8)|segmd; //位码段码合并为一个int型数据
for(i=0;i<16;i++)//16位数据从高位依次移入74HC595
{
ser=(dat&0x8000)?1:0; //判断最高位,为真取1,为假取0
srclk=1;//上升沿送数据
srclk=0;
dat<<=1;//左移取下一位
}
rclk=1;//74HC595锁存数据
rclk=0;
oe=0;//75HC595使能
}
//写数据函数:
void Write1380(unsigned char waddr,unsigned char wdat)
{
unsigned char i;
rst=1;//使能
for(i=0;i<8;i++)
{
io=(waddr&0x01)?1:0;//先写地址,从低位开始写
sclk=1;//上升沿写数据
sclk=0;
waddr>>=1;//右移取下一位
}
for(i=0;i<8;i++)
{
io=(wdat&0x01)?1:0;//写数据,从低位开始写
sclk=1;//上升沿写数据
sclk=0;
wdat>>=1;//右移取下一位
}
rst=0;//禁止
}
//读数据函数:
unsigned char Read1380(unsigned char raddr)
{
unsigned char i,rdat=0;
rst=1;//使能
for(i=0;i<8;i++) //写地址命令字节
{
io=(raddr&0x01)?1:0;
sclk=1;//上升沿写数据
sclk=0;
raddr>>=1;//右移取下一位
}
for(i=0;i<8;i++)//读数据
{
sclk=1;//上升沿读数据
rdat>>=1;//右移一位
if(io==1) rdat|=0x80;//从低位开始读取放入变量
//else rdat|=0x00;//如果是0,可以不作处理
sclk=0;
}
rst=0;//禁止
return rdat;
}
//显示时间子函数:
void DisplayTime()
{
unsigned char y,mo,day,da,h,mi,s;
s=Read1380(0x81);//读秒钟
mi=Read1380(0x83);//读分钟
h=Read1380(0x85);//读小时
da=Read1380(0x87);//读日期
mo=Read1380(0x89);//读月份
day=Read1380(0x8b);//读星期
y=Read1380(0x8d);//读年份
//将读值化成十进制数,然后送数码管显示
DTDisplayChar(segmcode[h/16],bitcode[0]);//第0位显示小时十位
Delay1ms(1);
DTDisplayChar(segmcode[h%16],bitcode[1]);//第1位显示小时个位
Delay1ms(1);
DTDisplayChar(0x40,bitcode[2]);//第2位显示“-”
Delay1ms(1);
DTDisplayChar(segmcode[mi/16],bitcode[3]);//第3位显示分钟的十位
Delay1ms(1);
DTDisplayChar(segmcode[mi%16],bitcode[4]);//第4位显示分钟的个位
Delay1ms(1);
DTDisplayChar(0x40,bitcode[5]);//第5位显示“-”
Delay1ms(1);
DTDisplayChar(segmcode[s/16],bitcode[6]);//第6位显示秒钟的十位
Delay1ms(1);
DTDisplayChar(segmcode[s%16],bitcode[7]);//第7位显示秒钟的个位
Delay1ms(1);
}
//主函数:
int main(void)
{
rst=0;sclk=0;//初始化数据线
Write1380(0x8e,0);//解除写保护
Write1380(0x80,0);//使能晶振
//注意:要先把设定的时间化为BCD码再写入
Write1380(0x80,48);//初始化时间 30秒,BCD码:30/10*16+30%10=48
Write1380(0x82,89);//初始化时间 59分,BCD码:59/10*16+59%10=89
Write1380(0x84,35);//初始化时间 23时,BCD码:23/10*16+23%10=35
Write1380(0x86,49);//初始化时间 31日,BCD码:31/10*16+31%10=49
Write1380(0x88,6);//初始化时间星期6,BCD码:6/10*16+6%10=6
Write1380(0x8a,18);//初始化时间 12月,BCD码:12/10*16+12%10=18
Write1380(0x8c,17);//初始化时间 11年,BCD码:11/10*16+11%10=17
while(1)
DisplayTime();//显示时间
return 0;
}