基于蓝桥杯的单片机模块练习——DS1302时钟芯片
相关知识点
1.特点
实时时钟计算秒、分、小时、月、日、周和年,闰年补偿有效期最长为2100年。
2.详细描述
DS1302芯片包含一个实时时钟/日历和31字节的静态随机存取存储器。它通过简单的串行接口与微处理器通信。实时时钟/日历提供秒、分、小时、日、月、星期和年的信息。对于少于31天的月份,月末日期会自动调整,包括闰年的更正。时钟以24小时制或12小时制运行,带有上午/下午指示器。
3.地址命令字节
MSB(位7)必须是逻辑1。如果为0,对DS1302的写入将被禁用。位6如果是逻辑0则指定时钟/日历数据,如果逻辑1,则指定随机存取存储器数据。位1至5指定要输入或输出的指定寄存器。LSB(位0)是逻辑0时则为写操作(输入),逻辑1时为读操作(输出)。命令字节总是从LSB(位0)开始输入。
4.数据的输入
在输入写命令字节的八个SCLK周期之后,在接下来的八个SCLK周期的上升沿输入一个数据字节。如果无意中出现额外的SCLK循环,则忽略这些循环。数据从位0开始输入。
5.数据的输出
在输入读命令字节的八个SCLK周期之后,在接下来的八个SCLK周期的下降沿输出一个数据字节。请注意,要传输的第一个数据位出现在命令字节的最后一位写入后的第一个下降沿。
6.时钟和日历
时间和日历信息通过读取适当的寄存器字节获得。通过写入适当的寄存器字节来设置或初始化时间和日历。时间和日历寄存器的内容是二进制编码的十进制(BCD)格式。
注意:星期几的登记从午夜开始递增。对应于一周中某一天的值是用户定义的,但必须是连续的(如果1号对应星期日,则2号对应星期一,依此类推)不合逻辑的时间和日期输入相当于未定义。
DS1302可以在12小时或24小时模式下运行。小时寄存器的位7定义为12或24小时模式选择位。高电平时,选择12小时模式。在12小时模式下,第5位是上午/下午位,逻辑高为下午。在24小时模式下,第5位是第二个10小时位(20–23小时)。每当12/24位发生变化时,小时数据必须重新初始化。
秒寄存器的位7被定义为时钟停止标志。当该位设为逻辑1时,时钟振荡器停止,DS1302进入低功耗待机模式,漏电流小于100nA。当该位写入逻辑0时,时钟将启动。通电后默认为0。
控制寄存器的位7是写保护位(通电后默认为0。前七位(位0至位6)被强制为0,并且在读取时总是读取0。在对时钟或随机存取存储器进行任何写操作之前,位7必须为0。高电平时,写保护位阻止对任何其他寄存器的写操作。因此,在尝试写入设备之前,应清除WP位。
7.数据的传输
1.单字节写入
//==========写入8bit数据==============================
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++) //传输8bit数据
{
SCK=0;//初始时钟线置0
SDA=temp&0x01;//取最低位,注意 DS1302 的数据和地址都是从最低位开始传输的
temp>>=1; //数据右移一位,准备传输下一位数据
SCK=1;//时钟线拉高,制造上升沿,SDA 的数据被传输
}
}
/*****************************************************************
说明:先写地址,后写数据
功能:像相应的地址中,写入一个字节的数据
**************************************************************/
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();//初始CE置0
SCK=0; _nop_();//初始时钟线置0
RST=1; _nop_(); //CE为1,传输开始
Write_Ds1302(address);//传输命令字,要写入的时间或日历地址
Write_Ds1302(dat); //写入要修改的时间或日期
RST=0; //CE位0,结束数据传输
}
2.单字节读取
/*****************************************************************
说明: 先写地址,后读数据
功能:从 相应地址中读取一个字节的数据
**************************************************************/
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();//初始 CE 线置为 0
SCK=0; _nop_();//初始时钟线置为 0
RST=1; _nop_();//初始 CE 置为 1,传输开始
Write_Ds1302(address);//传输命令字,要读取的时间/日历地址
for (i=0;i<8;i++) //读取要得到的时间/日期
{
SCK=0;//制造下降沿读取数据
temp>>=1;//要返回的数据左移一位
if(SDA)//当数据线为高时,证明该位数据为 1
temp|=0x80; //要传输数据的当前值置为 1,若不是,则为 0
SCK=1;//拉高时钟线
}
RST=0; _nop_();//读取结束,CE 置为 0,结束数据的传输
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp); //返回得到的时间/日期
}
8.比赛时需要改写ds1302驱动
/*
程序说明: DS1302驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include "ds1302.h"
unsigned char write_address[] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
unsigned char read_address[] = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
unsigned char ds1302data[] = {0x06,0x59,0x23,0x09,0x02,0x02,0x21};
unsigned char time[] = {0x06,0x59,0x23,0x09,0x02,0x02,0x21};
//==========写入8bit数据==============================
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++) //传输8bit数据
{
SCK=0;//初始时钟线置0
SDA=temp&0x01;//取最低位,注意 DS1302 的数据和地址都是从最低位开始传输的
temp>>=1; //数据右移一位,准备传输下一位数据
SCK=1;//时钟线拉高,制造上升沿,SDA 的数据被传输
}
}
/*****************************************************************
说明:先写地址,后写数据
功能:像相应的地址中,写入一个字节的数据
**************************************************************/
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();//初始CE置0
SCK=0; _nop_();//初始时钟线置0
RST=1; _nop_(); //CE为1,传输开始
Write_Ds1302(address);//传输命令字,要写入的时间或日历地址
Write_Ds1302(dat); //写入要修改的时间或日期
RST=0; //CE位0,结束数据传输
}
/*****************************************************************
说明: 先写地址,后读数据
功能:从 相应地址中读取一个字节的数据
**************************************************************/
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();//初始 CE 线置为 0
SCK=0; _nop_();//初始时钟线置为 0
RST=1; _nop_();//初始 CE 置为 1,传输开始
Write_Ds1302(address);//传输命令字,要读取的时间/日历地址
for (i=0;i<8;i++)//读取要得到的时间/日期
{
SCK=0;//制造下降沿读取数据
temp>>=1;//要返回的数据左移一位
if(SDA)//当数据线为高时,证明该位数据为 1
temp|=0x80; //要传输数据的当前值置为 1,若不是,则为 0
SCK=1;//拉高时钟线
}
RST=0; _nop_();//读取结束,CE 置为 0,结束数据的传输
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp); //返回得到的时间/日期
}
void DS1302_Config()
{
unsigned char i;
Write_Ds1302_Byte( 0x8e,0x00 );
for(i = 0; i < 7; i++)
{
Write_Ds1302_Byte( write_address[i],ds1302data[i] );
}
Write_Ds1302_Byte( 0x8e,0x80 );
}
void DS1302_Read()
{
unsigned char i;
for(i = 0; i < 7; i++)
{
time[i] = Read_Ds1302_Byte( read_address[i] );
}
}
头文件ds1302.h
#ifndef __DS1302_H
#define __DS1302_H
#include "SystemInit.h"
#include <intrins.h>
sbit SCK=P1^7;
sbit SDA=P2^3; //数据传输IO
sbit RST = P1^3; // DS1302复位(SE)
void Write_Ds1302(unsigned char temp);
void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302_Byte( unsigned char address );
void DS1302_Config();
void DS1302_Read();
#endif
部分相关代码及注解
unsigned char write_address[] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};//对应RTC寄存器的写地址
unsigned char read_address[] = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};//对应RTC寄存器的读地址
unsigned char ds1302data[] = {0x50,0x59,0x23,0x09,0x02,0x02,0x21};//要配置的初始时间/日期,一定要按秒、分、小时、日、月、星期和年的顺序
unsigned char time[7];//用于存储 读取到的时间/日期数据
void DS1302_Config()//配置初始时间函数
{
unsigned char i;
Write_Ds1302_Byte( 0x8e,0x00 );//清除WP(取消写保护)
for(i = 0; i < 7; i++)
{
Write_Ds1302_Byte( write_address[i],ds1302data[i] );//将数据写入秒、分、小时、日、月、星期和年寄存器。注意,按对应顺序写入
}
Write_Ds1302_Byte( 0x8e,0x80 );//置位WP(打开写保护)
}
void DS1302_Read()//读取芯片里的当前时间/日期
{
unsigned char i;
for(i = 0; i < 7; i++)
{
time[i] = BCD_TO_DECIMAL(Read_Ds1302_Byte( read_address[i] ));
}
}
// BCD 码 转 十进制
#define BCD_TO_DECIMAL(x) ((((x) & 0xf0) >> 4) * 10 + ((x) & 0x0f))
// 十进制 转 BCD 码
#define DECIMAL_TO_BCD(x) ((((x) / 10) << 4) + ((x) % 10))
注意事项
DS1302外部晶振有一个起振时间,所以单片机需要等待他晶振起振完成才可以对它操作,建议把关于DS1302的配置函数写到最后。