蓝桥杯之单片机设计与开发(20)——DS1302

版权声明:让结局不留遗憾,让过程更加完美。 https://blog.csdn.net/Xiaomo_haa/article/details/88061555

DS1302硬件信息

DS1302引脚图

 

DS1302典型电路

DS1302寄存器介绍

DS1302的一条指令一个字节共8位。

其中第7位(最高位)固定为1。这一位如果是0的话,那写进去也是无效的。

第6位是选择RAM还是CLOCK的。第6位是0就是选择CLOCK功能,第6位是1就是选择RAM。

第5到第1位,决定了寄存器的5位地址。

第0位是读写位。第0位是0就是写,第0位是1就是读。 

DS1302时钟的寄存器,有8个与时钟相关的。

注意:DS1302内部是BCD码!!!

寄存器0:最高位CH是一个时钟停止标志位。如果时钟电路有备用电源,上电后,我们要先检测一下这一位,如果这一位是0,那说明时钟芯片在系统掉电后,由于备用电源的供给,时钟是持续正常运行的;如果这一位是1,那么说明时钟芯片在系统掉电之后,时钟部分不工作了。我们可以通过这一位判断时钟在单片机系统掉电后是否在正常运行。剩下的7位,高3位是秒的十位,低4位是秒的个位。

寄存器1:最高位未使用,剩下的7位中高3位是分钟的十位,低4位是分钟的个位。

寄存器2:bit7是1的话代表是12小时制,0代表是24小时制。bit6固定是0。bit5在12小时制下0代表是上午,1代表是下午,在24小时制下和bit4一起代表了小时的十位。低4位代表的是小时的个位。

寄存器3:高2位固定是0,bit5和bit4是日期的十位,低4位是日期的个位。

寄存器4:高3位固定是0,bit4是月的十位,低4位是月的个位。

寄存器5:高5位固定是0,低3位代表了星期。

寄存器6:高4位代表了年的十位,低4位代表了年的个位。注意:00~99指的是2000~2099年。

寄存器7:最高位一个写保护位,如果这一位是1,那么是禁止给任何其他寄存器或者那31个字节的RAM写数据的。因此在写数据之前,这一位必须先写成0。

DS1302通信时序

DS1302的通信是SPI的变异种类,它用了SPI的通信时序,但是通信的时候没有完全按照SPI的规则来。

上升沿DS1302写入数据,下降沿DS1302读出数据! 上沿采样,下沿输出。

单字节读时序

单字节写时序

读操作有两处需要特别注意的地方。第一,DS1302的时序图上的箭头都是针对DS1302来说的,因此读操作的时候,先写第一个字节指令,上升沿的时 候DS1302来锁存数据,下降沿我们用单片机发送数据。到了第二个字数据,由于我们这个时序过程相当于CPOL=0/CPHA=0,前沿发送数据,后沿读取数据,第二个字节是DS1302下降沿输出数据,我们的单片机上升沿来读取,因此箭头从DS1302角度来说,出现在了下降沿。

第二个需要注意的地方就是,我们的单片机没有标准的SPI接口,和I2C一样需要用IO口来模拟通信过程。在读DS1302的时候,理论上SPI是上升沿读取,但是程序是用IO口模拟的,所以数据的读取和时钟沿的变化不可能同时了,必然就有一个先后顺序。通过实验发现,如果先读取IO线上的数据,再拉高SCLK产生上升沿,那么读到的数据一定是正确的,而颠倒顺序后数据就有可能出错。这个问题产生的原因还是在于DS1302的通信协议与标准SPI协议存在的差异造成的,如果是标准SPI的数据线,数据会一-直保持到下一个周期的下降沿才会变化,所以读取数据和上升沿的先后顺序也就无所谓了。但DS1302的IO线会在时钟上升沿后被DS1302释放,也就是撤销强推挽输出变为弱下拉状态,而此时在单片机引脚内部上拉的作用下,IO线上的实际电平会慢慢上升,从而导致在上升沿产生后再读取IO数据的话就可能会出错。因此这里的程序我们按照先读取IO数据,再拉高SCLK产生上升沿的顺序。

DS1302的BURST模式

定时器时间到了 200ms 后,我们连续把 DS1302 的时间参数的 7 个字节读了出来。但是不管怎么读,都会有一个时间差,在极端的情况下就会出现这样一种情况:假如我们当前的时间是 00:00:59,我们先读秒,读到的秒是 59,然后再去读分钟,而就在读完秒到还未开始读分钟的这段时间内,刚好时间进位了,变成了 00:01:00 这个时间,我们读到的分钟就是 01,显示在液晶上就会出现一个 00:01:59,这个时间很明显是错误的。出现这个问题的概率极小,但却是实实在在可能存在的。

所以这个时候就有了BURST模式,BURST模式就是一次把7 个字节全部读或写到缓冲区,然后再来进行后续操作!

当写指令到DS1302的时候,只要将要写的5位地址全部写1,即读操作用0xBF,写操作用0xBE,这样的指令送给DS1302之后,它就会自动识别出来是burst模式,马上把所有的8个字节同时锁存到另外的8个字节的寄存器缓冲区中,这样时钟继续走,而我们读数据是从另外一个缓冲区内读取的。同样的道理,如果我们是用burst模式写数据,那么我们也是先写到这个缓冲区内,最终DS1302会把这个缓冲区内的数据一次性送到它的时钟寄存器内。

注意:不管是读还是写,只要使用时钟的burst模式,则必须一次性读写8个寄存器,要把时钟的寄存器完全读出来或者完全写进去。

#include "sys.h"

sbit DS1302_IO = P2^3;
sbit DS1302_CK = P1^7;
sbit DS1302_CE = P1^3;

struct sTime{
	unsigned int year;		//年
	unsigned char mon;		//月
	unsigned char day;		//日
	unsigned char hour;		//时
	unsigned char min;		//分
	unsigned char sec;		//秒
	unsigned char week;		//周
};


/*******************************************************************************
* 函数名	:DS1302ByteWrite
* 输入值	:unsigned char dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:发送一个字节到DS1302通信总线上
* 备注		:
*******************************************************************************/
void DS1302ByteWrite(unsigned char dat) 
{
	unsigned char mask;
	DS1302_IO = 1;					//拉低IO总线
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在前,逐位移出
	{
		if((dat&mask) != 0)		//首先输出该位数据
			DS1302_IO = 1;
		else
			DS1302_IO = 0;
		DS1302_CK = 1;				//拉高时钟线
		DS1302_CK = 0;				//拉低时钟线,完成一个位的操作
	}
	DS1302_IO = 1;  				//写完之后确保释放IO总线
}  

/*******************************************************************************
* 函数名	:DS1302ByteRead
* 输入值	:none
* 返回值	:unsigned char dat
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:由DS1302通信总线上读取一个字节
* 备注		:返回读到的字节数据
*******************************************************************************/
unsigned char DS1302ByteRead(void)
{
	unsigned char mask, dat = 0;
	
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在前,逐位读取
	{
		if(DS1302_IO)			//首先读取此时的IO引脚,并设置dat中的对应位
			dat |= mask;

		DS1302_CK = 1;		//拉高时钟
		DS1302_CK = 0;		//再拉低时钟,完成一个位的操作
	}
	return dat;					//返回读到的字节数据
} 

/*******************************************************************************
* 函数名	:DS1302SingleWrite
* 输入值	:unsigned char reg, unsigned char dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用单次写操作向某一寄存器写入一个字节
* 备注		:reg为寄存器地址,dat为待写入字节
*******************************************************************************/
void DS1302SingleWrite(unsigned char reg, unsigned char dat)     
{
	DS1302_CE = 1;					//使能片选信号
	DS1302ByteWrite((reg << 1) | 0x80);	//发送写寄存器指令
	DS1302ByteWrite(dat);		//写入字节数据
	DS1302_CE = 0;					//除能片选信号
}

/*******************************************************************************
* 函数名	:DS1302SingleRead
* 输入值	:unsigned char reg
* 返回值	:unsigned char dat
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用单次读操作从某一寄存器读取一个字节
* 备注		:reg为寄存器地址,返回值dat为读到的字节
*******************************************************************************/
unsigned char DS1302SingleRead(unsigned char reg)
{
	unsigned char dat;
	
	DS1302_CE = 1;					//使能片选信号
	DS1302ByteWrite((reg << 1) | 0x81);	//发送读寄存器指令
	dat = DS1302ByteRead();	//读取字节数据
	DS1302_CE = 0;					//除能片选信号
	
	DS1302_IO = 0;					//单字节读必须加的!
	
	return dat;         
}

/*******************************************************************************
* 函数名	:DS1302BurstWrite
* 输入值	:unsigned char *dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用突发模式连续写入8个寄存器数据
* 备注		:reg为寄存器地址,返回值dat为读到的字节
*******************************************************************************/
void DS1302BurstWrite(unsigned char *dat)
{
	unsigned char i;
	
	DS1302_CE = 1;
	DS1302ByteWrite(0xBE);				//发送突发写寄存器指令
	for(i = 0; i < 7; i ++)				//连续写入8字节数据
		DS1302ByteWrite(*dat++);        
	DS1302_CE = 0;      
}

/*******************************************************************************
* 函数名	:DS1302BurstRead
* 输入值	:unsigned char *dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用突发模式连续读取8个寄存器的数据
* 备注		:dat为读到的字节
*******************************************************************************/
void DS1302BurstRead (unsigned char *dat)
{
	unsigned char i;
	
	DS1302_CE = 1;
	DS1302ByteWrite(0xBF);				//发送突发读寄存器指令
	for(i = 0; i < 7; i++)				//连续读取8个字节
		dat[i] = DS1302ByteRead();      
	DS1302_CE = 0;  
	
	DS1302_IO = 0;								//突发读必须加
}	

/*******************************************************************************
* 函数名	:GetRealTime
* 输入值	:struct sTime *time
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:读取DS1302时间
* 备注		:
*******************************************************************************/
void GetRealTime(struct sTime *time)
{
	unsigned char buf[8];
	
	DS1302BurstRead(buf);
	time -> year = buf[6] + 0x2000;
	time -> mon  = buf[4];
	time -> day  = buf[3];
	time -> hour = buf[2];
	time -> min  = buf[1];
	time -> sec  = buf[0];
	time -> week = buf[5];
}

/*******************************************************************************
* 函数名	:SetRealTime
* 输入值	:struct sTime *time
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:设置DS1302时间
* 备注		:
*******************************************************************************/
void SetRealTime(struct sTime *time)
{
	unsigned char buf[8];
	
	buf[7] = 0;
	buf[6] = time -> year;
	buf[4] = time -> mon;
	buf[3] = time -> day;
	buf[2] = time -> hour;
	buf[1] = time -> min;
	buf[0] = time -> sec;
	buf[5] = time -> week;
	DS1302BurstWrite(buf);
}

/*******************************************************************************
* 函数名	:InitDS1302
* 输入值	:none
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:DS1302初始化
* 备注		:
*******************************************************************************/
void InitDS1302()
{
	struct sTime InitTime[] = {  //2019年3月1日 星期五 21:50:00
			0x19, 0x03, 0x01, 0x21, 0x50, 0x00, 0x05
	};
	DS1302_CE = 0;								//初始化DS1302通信引脚
	DS1302_CK = 0;
	DS1302SingleWrite(7, 0x00);  	//撤销写保护以允许写入数据
	SetRealTime(&InitTime);				//设置DS1302为默认时间
}


 

猜你喜欢

转载自blog.csdn.net/Xiaomo_haa/article/details/88061555
今日推荐