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;
//---DS1302写入和读取时分秒的地址命令//
//---秒分时日月年 最低位读写位------//
unsigned char Write_Addr[7]={0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
unsigned char Read_Addr[7]={0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
//---DS1302时钟初始化2021年5月20日星期四13点51分47秒---//
//---存储顺序是秒分时日月周年,存储格式是用 BCD 码-----//
unsigned char DS1302_Time[7]={0x47, 0x51, 0x13, 0x20, 0x05, 0x04, 0x21};
//共阴极数码管显示0~F的段码数据
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--);
}
//DS1302写单字节函数(Addr:地址,Data:数据)
void DS1302_Write_Byte(unsigned char Addr,unsigned char Data)
{
unsigned char i=0;
DS1302_CE=0;
_nop_();
DS1302_SCLK=0; //SCLK低电平
_nop_();
DS1302_CE=1; //CE由低到高变化
_nop_();
//写入地址
for(i=0;i<8;i++) //循环8次,每次写1位,先写低位在写高位
{
DS1302_IO=Addr&0x01; //将地址的最后一位送到数据口
Addr>>=1;
DS1302_SCLK=1; //SCLK由低到高产生一个上升沿,从而写入数据
_nop_();
DS1302_SCLK=0;
_nop_();
}
//写入数据
for(i=0;i<8;i++) //循环8次,每次写1位,先写低位在写高位
{
DS1302_IO=Data&0x01;
Data>>=1;
DS1302_SCLK=1;
_nop_();
DS1302_SCLK=0;
_nop_();
}
DS1302_CE=0;
_nop_();
}
//DS1302读单字节(Addr:地址)
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++) //循环8次,每次读一位,先读低位再读高位
{
temp=DS1302_IO;
value=(temp<<7)|(value>>1); //先将value右移1位,然后temp左移7位
DS1302_SCLK=1;
_nop_();
DS1302_SCLK=0;
_nop_();
}
return value;
}
//DS1302写入时间
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); //打开写保护
}
//DS1302读取时间
void DS1302_Read_Time()
{
unsigned char i=0;
for(i=0;i<7;i++)
DS1302_Time[i]=DS1302_Read_Byte(Read_Addr[i]);
}
//动态数码管显示(Data[]:段选数据,Location:位选)
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); //通过数码管显示
}
}