51单片机DS1302时钟
实现功能
数码管上显示电子时钟 时 分 秒,格式为“XX-XX-XX”(以 13点51分47秒 为例 )
单片机型号:STC89C52
DS1302介绍
1、DS1302简介
?DS1302 是 DALLAS 公司推出的涓流充电时钟芯片,内含有一个实时时钟/日 历和 31 字节静态 RAM,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可自动调整。时钟操作可通过 AM/PM 指示决定采用 24 或 12 小时格式。DS1302 与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三根通信线:①RES 复位 ②I/O 数据线 ③SCLK 串行时钟。时钟/RAM 的读/写数据以一个字节或多达 31 个字节的字符组方式通信。
? 操作 DS1302 的大致过程,就是将各种数据写入 DS1302 的寄存器,以设置它当前的时间的格式。然后使 DS1302 开始运作,DS1302 时钟会按照设置情况运转,再用单片机将其寄存器内的数据读出。再用液晶显示,就是我们常说的简易电子钟。所以总的来说 DS1302 的操作分 2 步(显示部分属于液晶显示的内容, 不属于 DS1302 本身的内容),但是在讲述操作时序之前,我们要先看看寄存器, DS1302 有一个控制寄存器、12 个日历、时钟寄存器和 31 个 RAM。
2、控制寄存器
控制寄存器用于存放 DS1302 的控制命令字,DS1302 的 RST 引脚回到高电平后写入的第一个字节就为控制命令。它用于对 DS1302 读写过程进行控制,格式如下:
- 第 7 位永远都是 1。
- 第 6 位,1 表示 RAM,寻址内部存储器地址;0 表示 CK,寻址内部寄存器。
- 第 5 到第 1 位,为 RAM 或者寄存器的地址。
- 最低位,高电平表示 RD,即下一步操作将要 “读”;低电平表示 W,即 下一步操作将要 “写”。
比如要读秒寄存器则命令为 1000 0001,反之写为 1000 0000,要注意其含义。
3、日历/时钟寄存器
DS1302 共有 12 个寄存器,其中有 7 个与日历、时钟相关,存放的数据为 BCD 码形式。格式如下:
秒寄存器:低四位为秒的个位,高的次三位为秒的十位。最高位 CH 为 DS1302 的运行标志,当 CH=0 时,DS1302 内部时钟运行,反之 CH=1 时停止。
小时寄存器:时寄存器。最高位为 12/24 小时的格式选择位,该位为 1 时 表示 12 小时格式。当设置为 12 小时显示格式时,第 5 位的高电平表示下午 (PM);而当设置为 24 小时格式时,第 5 位具体的时间数据。
写保护寄存器:当该寄存器最高位 WP 为 1 时,DS1302 只读不写,所以要在往 DS1302 写数据之前确保 WP 为 0
4、DS1302的读写时序
在控制指令字输入后的下一个 SCLK 时钟的上升沿时,数据被写入 DS1302,数据输入从低位(位 0)开始。同样,在紧跟 8 位的控制指令字后的下一个 SCLK 脉冲的下降沿读出 DS1302 的数据,读出数据时从低位 0 位到高位 7。其时序图 如下所示:
上图就是 DS1302 的三个时序:复位时序,单字节写时序,单字节读时序。
CE(RST):复位时序,即在 RST 引脚产生一个正脉冲,在整个读写器件, RST 要保持高电平,一次字节读写完毕之后,要注意把 RST 返回低电平准备下次读写周期。
单字节读时序:注意读之前还是要先对寄存器写命令,从最低位开始写;可以看到,写数据是在 SCLK 的上升沿实现,而读数据在 SCLK 的下降沿实现。所以, 在单字节读时序中,写命令的第八个上升沿结束后紧接着的第八个下降沿就将要读寄存器的第一位数据读到数据线上了!这个就是 DS1302 操作中最特别的地方。 当然读出来的数据也是最低位开始。
单字节写时序:两个字节的数据配合 16 个上升沿将数据写入即可。
程序注意事项:
- 要记得在操作 DS1302 之前关闭写保护;
- 注意用延时来降低单片机的速度以配合器件时序;
- DS1302 读出来的数据是 BCD 码形式,要转换成我们习惯的 10 进制,转换 方法在源程序里;
- 读取字节之前,将 IO 设置为输入口,读取完之后,要将其改回输出口;
- 在写程序的时候,建议实现开辟数组(内存空间)来集中放置 DS1302 的 一系列数据,方便以后扩展键盘输入。
5、BCD码
前面我们提到在日历/时钟寄存器中都是以 BCD 码存放数据,那么 BCD 码是 什么呢?BCD 码是通过 4 位二进制码来表示 1 位十进制中的 0~9 这 10 个数码。 如下所示:
所以从 DS1302 中读取出来的时钟数据均为 BCD 码格式,需转换为我们习惯的 10 进制,转换方法在源程序里。
硬件原理
从上图中可知,DS1302 芯片的控制管脚接至单片机 P3.4-P3.6 上,在芯片的 X1、X2 管脚处外接了一个 32.768KHZ 晶振,为时钟运行提供一个稳定的时钟频 率,C2 和 C3 为旁路电容,目的是消除晶振起振时产生的电感干扰。
- VCC2:主电源引脚。
- X1、X2:DS1302 外部晶振引脚,通常需外接 32.768K 晶振。
- GND:电源地。
- CE:使能引脚,也是复位引脚(新版本功能变)。
- I/O:串行数据引脚,数据输出或者输入都从这个引脚。
- SCLK:串行时钟引脚。
- VCC1:备用电源。
软件编写
程序框架如下:
- 编写数码管显示功能
- 编写 DS1302 时钟读写功能
- 编写主函数
#include <REGX52.H>
#include <intrins.h>
#define LED P0
sbit DS1302_CE=P3^5;
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
unsigned char Write_Addr[7]={0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
unsigned char Read_Addr[7]={0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
unsigned char DS1302_Time[7]={0x47, 0x51, 0x13, 0x20, 0x05, 0x04, 0x21};
unsigned char Smg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
void Delay(unsigned int x)
{
while(x--);
}
void DS1302_Write_Byte(unsigned char Addr,unsigned char Data)
{
unsigned char i=0;
DS1302_CE=0;
_nop_();
DS1302_SCLK=0;
_nop_();
DS1302_CE=1;
_nop_();
for(i=0;i<8;i++)
{
DS1302_IO=Addr&0x01;
Addr>>=1;
DS1302_SCLK=1;
_nop_();
DS1302_SCLK=0;
_nop_();
}
for(i=0;i<8;i++)
{
DS1302_IO=Data&0x01;
Data>>=1;
DS1302_SCLK=1;
_nop_();
DS1302_SCLK=0;
_nop_();
}
DS1302_CE=0;
_nop_();
}
unsigned char DS1302_Read_Byte(unsigned char Addr)
{
unsigned char i=0;
unsigned char temp=0;
unsigned char value=0;
DS1302_CE=0;
_nop_();
DS1302_SCLK=0;
_nop_();
DS1302_CE=1;
_nop_();
for(i=0;i<8;i++)
{
DS1302_IO=Addr&0x01;
Addr>>=1;
DS1302_SCLK=1;
_nop_();
DS1302_SCLK=0;
_nop_();
}
for(i=0;i<8;i++)
{
temp=DS1302_IO;
value=(temp<<7)|(value>>1);
DS1302_SCLK=1;
_nop_();
DS1302_SCLK=0;
_nop_();
}
return value;
}
void DS1302_Write_Time()
{
unsigned char i=0;
DS1302_Write_Byte(0x8E,0x00);
for(i=0;i<7;i++)
DS1302_Write_Byte(Write_Addr[i],DS1302_Time[i]);
DS1302_Write_Byte(0x8E,0x80);
}
void DS1302_Read_Time()
{
unsigned char i=0;
for(i=0;i<7;i++)
DS1302_Time[i]=DS1302_Read_Byte(Read_Addr[i]);
}
void Smg_Display(unsigned char Data[],unsigned char Location)
{
unsigned char i=0;
unsigned char temp=Location-1;
for(i=temp;i<8;i++)
{
switch(i)
{
case 0: LSC=1;LSB=1;LSA=1;break;
case 1: LSC=1;LSB=1;LSA=0;break;
case 2: LSC=1;LSB=0;LSA=1;break;
case 3: LSC=1;LSB=0;LSA=0;break;
case 4: LSC=0;LSB=1;LSA=1;break;
case 5: LSC=0;LSB=1;LSA=0;break;
case 6: LSC=0;LSB=0;LSA=1;break;
case 7: LSC=0;LSB=0;LSA=0;break;
}
LED=Data[i-temp];
Delay(100);
LED=0x00;
}
}
void main()
{
unsigned char Time_Buf[8];
DS1302_Write_Time();
while(1)
{
DS1302_Read_Time();
Time_Buf[0]=Smg_code[DS1302_Time[2]/16];
Time_Buf[1]=Smg_code[DS1302_Time[2]%16];
Time_Buf[2]=0x40;
Time_Buf[3]=Smg_code[DS1302_Time[1]/16];
Time_Buf[4]=Smg_code[DS1302_Time[1]%16];
Time_Buf[5]=0x40;
Time_Buf[6]=Smg_code[DS1302_Time[0]/16];
Time_Buf[7]=Smg_code[DS1302_Time[0]%16];
Smg_Display(Time_Buf,1);
}
}
|