DS1302学习

为什么使用外部计时芯片?精度更高且不怕断电

根本原因是单片机内部计时具有如下两大缺点

因此使用外部计时芯片,更精确,而且使用了备用电池,不怕断电

DS1302的引脚

所有的引脚可以分为三个部分

数据传输:先写入地址,再读/写数据

DS1302中有很多位置(寄存器)放置数据。信号传输过程须先说明操作的地址,再进行读写数据操作。

读和写操作会放在不同的地方(寄存器),防止相互干扰。读写操作各对应8个寄存器,共16个寄存器,它们分别是

需注意,读取寄存器地址末尾都是奇数(1、3、5、7、9、B、D),对应地址末位是1;而写入寄存器地址的末尾都是偶数(2,4,6,8,A,C,E),对应地址末位是0。实际上,DS1302就是据此分辨是读还是写:1是读,0是写

读写操作具体实现:如何写一个字节

DS1302采用SPI通信,是最常用的串行通信方式,DS1302中有三条线,SCK,IO和 RST

比如写一个字节的程序如下

void Write_Ds1302(unsigned  char temp) 
{
	unsigned char i;
	for (i=0;i<8;i++)     	
	{ 
		SCK=0;//先把SCK置为低电平
		SDA=temp&0x01; //待写入数据temp和0x01求与操作,取最后一位放入SDA
		temp>>=1; //temp右移一位,让倒数第二位变为最后一位,为下一次传输做准备
		SCK=1;//SCK从低到高,形成上升沿,此时SDA数据写入DS1302
	}
}  

完整写入过程:写入两个字节(地址和数据)

前面提到,与写入相关的寄存器有8个。因此,我们在写入数据之前,必须要告诉DS1302数据写到哪里,也就是写入地址。

一个完整的写入过程如下:

void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{
	//不管是数据的读操作还是写操作,都要首先写入地址。
	//而根据"升写降读”的原则,需要SCL产生一个上升沿,也就是从0到1
	//如果SCL现在是1,直接设为0,那会首先产生一个下降延,可能会让DS1302误以为要进行读操作,它会放一个数据到IO口上,这样容易出错。
	//为了避免这个问题,先关闭数据传输,再把SCL设为0,再打开数据传输
 	RST=0;	_nop_(); //RST置为0,禁止数据传输
 	SCK=0;	_nop_(); // SCK设为0
 	RST=1; 	_nop_(); //打开数据传输
	
	//先写入地址,再写入数据
 	Write_Ds1302(address);	
 	Write_Ds1302(dat);	

	//最后再次关闭数据传输	
 	RST=0; 
}

完整读取过程:写入一个字节(地址),读取一个字节(数据)

一个完整的读取过程如下:

unsigned char Read_Ds1302_Byte ( unsigned char address )
{
 	unsigned char i,temp=0x00;
	
	//同样先在关闭数据传输的情况下拉低SCK
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1;	_nop_();
	
	//写入地址
 	Write_Ds1302(address);
	//需要注意的是,上述过程完成后SCK已经是高电平了
	
 	for (i=0;i<8;i++) 	
 	{		
		SCK=0; //SCK回到低电平,产生下降沿,DS1302把数据末位放到IO(SDA)口上
		temp>>=1;//右移1位,最高位补0,	
 		if(SDA) //如果SDA读到的数据是1的时候
 		temp|=0x80;	//那就把最高位修正为1
 		SCK=1; //SCK变为高电平
	} 
	
	//关闭数据传输
 	RST=0;	_nop_();
	
	//这几行代码还没搞清楚。。。。
 	SCK=0;	_nop_();
	SCK=1;	_nop_();
	SDA=0;	_nop_();
	SDA=1;	_nop_();
	
	
	return (temp);			
}

蓝桥杯给出资料包中的DS1302相关的完整代码

#ifndef __DS1302_H
#define __DS1302_H

void Write_Ds1302(unsigned char temp);
void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302_Byte( unsigned char address );
#endif
/*
  程序说明: DS1302驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/

#include <reg52.h>
#include <intrins.h>

sbit SCK=P1^7;		
sbit SDA=P2^3;		
sbit RST = P1^3;   // DS1302复位												

void Write_Ds1302(unsigned  char temp) 
{
	unsigned char i;
	for (i=0;i<8;i++)     	
	{ 
		SCK=0;
		SDA=temp&0x01;
		temp>>=1; 
		SCK=1;
	}
}   

void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1; 	_nop_();  
 	Write_Ds1302(address);	
 	Write_Ds1302(dat);		
 	RST=0; 
}

unsigned char Read_Ds1302_Byte ( unsigned char address )
{
 	unsigned char i,temp=0x00;
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1;	_nop_();
 	Write_Ds1302(address);
 	for (i=0;i<8;i++) 	
 	{		
		SCK=0;
		temp>>=1;	
 		if(SDA)
 		temp|=0x80;	
 		SCK=1;
	} 
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
	SCK=1;	_nop_();
	SDA=0;	_nop_();
	SDA=1;	_nop_();
	return (temp);			
}

蓝桥杯主函数的代码(可直接用于仿真)

#include <DS1302.H>

void delay()
{
	char i;
	for(i=0;i<100;i++);
}

//显示程序,在数码管的add位置显示数字dat
void display(unsigned char add,unsigned char dat)
{
	unsigned char a[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf};
	P2 = (P2 & 0x1f) | 0xc0; //前三位变为110,对应6,打开Y6C对应锁存器
	P0 = 0x01 << add; //输出位选信号
	P2 = (P2 & 0x1f) | 0xe0;//前三位变为111,打开Y7C对应锁存器
	P0 = a[dat];           //输出数字信号
	delay();               
	P0 = 0xff;             
}

void main()
{
	unsigned char i;
	unsigned char tens, ones;//定义存储十位和各位的变量
	//设定初始时间,比如下面的例子为,0秒,47分,17时,30日,4月,星期6,22年(年也只有2位)
	unsigned char timer[7] = {0x00,0x47,0x17,0x30,0x04,0x06,0x22};

	//----------定义DS1302寄存器地址,这是固定的,不可更改--------
	unsigned char code Write_Add[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
	unsigned char code Read_Add[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; 
	
	//---------关闭蜂鸣器,电机和LED灯------------------
	P2 = (P2 & 0x1f) | 0xa0;//前三位变为101,Y5C对应锁存器打开
	P0 = 0;//ULN2003芯片输入0,输出高阻态(可以理解为断路)
	P2 = (P2 & 0x1f) |0x80;//前三位变为100,Y4C对应锁存器打开
	P0 = 0xff;//L1-L7变为高电平,所有灯关闭
	
	//---------向DS1302写入初始时间------------------
	Write_Ds1302(0x8e,0x00);//关闭写保护,允许写入
	for(i=0;i<7;i++)
		Write_Ds1302(Write_Add[i],timer[i]);//写入初始时间信息
	Write_Ds1302(0x8e,0x80);//打开写保护,不再允许写入
	
	
	//---------不停的从DS1302芯片中读取数据,并显示在7段数码管上--------
	while(1)
	{
		//读取时间信息
		for(i=0;i<7;i++)
			timer[i] = Read_Ds1302(Read_Add[i]);
		
		//显示时间信息
		for(i=0;i<3;i++)
		{
			ones = timer[2-i]%16; //计算低四位获取个位
			tens = timer[2-i]/16;//计算高四位获取十位
			display(3*i,tens); //显示十位
			display(3*i+1,ones);//显示各位
			display(3*i+2,10); //显示-
		}
	}
}

仿真的结果如下所示

============================找指导老师布置任务吧==============================