#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit sclk=P1^7;   //DS1302的Sclk时钟线位定义
sbit io=P2^4;		//DS1302的i/o数据线位定义
sbit ce=P1^6;		//DS1302的ce片选线位定义
sbit dula=P2^6;    //数据码段选位定义
sbit wela=P2^7;   //数据码位选位定义
uchar code table_du[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,
                    0x39,0x5e,0x79,0x71};   //数据管段选编码
uchar time_data[]={13,6,4,17,11,58,30};   //设置时间初值（年、周、月、日、时、分、秒）
uchar write_add[]={0x8c,0x8a,0x88,0x86,
                0x84,0x82,0x80};  //年、周、月、日、时、分、秒的寄存器写入地址 
uchar read_add[]={0x8d,0x8b,0x89,0x87,
                0x85,0x83,0x81};  //年、周、月、日、时、分、秒的寄存器读取地址
uchar disp[6];

delay(uchar x)   //延时函数
{
	uchar a,b;
	for(a=x;a>0;a--)
		for(b=200;b>0;b--);
}

void write_ds1302_byte(uchar add)   //字节写操作函数
{
	uchar i;
	for(i=0;i<8;i++)   //分8次循环，把（add）一位一位的写入
	{
		sclk=0;	
		io=add&0x01;   //add和0x01进行按位与，保留最低位。目的是：从低位开始写入
		add=add>>1;   //add右移一位
		sclk=1; 
	}
}

void write_ds1302(uchar add,uchar dat)  //单字节写操作函数（包含地址和数据）
{
	ce=0;      //刚开始ce为低电平
	sclk=0;     //刚开始sclk为低平
	ce=1;      //ce变为高电平，允许单片机向DS1302中的寄存器写入地址命令和数据
	_nop_();   //稍微延时（一个机器周期）
	write_ds1302_byte(add);  //写入控制命令,（指明写入的寄存器地址及后续的操作为写操作）
	write_ds1302_byte(dat);  //写入一个字节数据
	ce=0;     //地址命令和数据写完之后，把ce拉低，防止对写入的数据进行误操作
	_nop_();  //稍微延时
	io=1;     //释放IO数据线
	sclk=1;   //释放sclk时钟线
}

uchar read_ds1302(uchar add)  //单字节读操作函数
{
	uchar i,value;
	ce=0;     //刚开始ce为低电平
	sclk=0;    //刚开始sclk为低平
	ce=1;     //ce变为高电平，允许单片机向DS1302中的寄存器写入地址命令，并读出数据
	_nop_();  //稍微延时（一个机器周期）
	write_ds1302_byte(add); //写入控制命令,（指明要读取的寄存器地址及后续的操作为读操作）
	for(i=0;i<8;i++)   //分8次循环，把数据一位一位的读出
	{
		value=value>>1;   //value右移一位
			sclk=0;  //把sclk时钟置为低电平，开始读取数据
		if(io)
		{
			value=value|0x80;  //把I0读到的值，放到value 的最高位
		}
		sclk=1; 
	}
	ce=0;      //一个字节的数据读完之后，把ce拉低
	_nop_();
	sclk=0;     //释放sclk时钟线
	_nop_();
	sclk=1;
	io=1;      //释放io数据线
	return value;  //把 value值返回
}

void set_rtc()   //设置时间初值函数
{
	uchar i,j,k;
	for(i=0;i<7;i++)  //把数组time_data[]中的7个十进制数转换成BCD码
	{
		j=time_data[i]/10;
		k=time_data[i]%10;
		time_data[i]=k+j*16;
	}
	write_ds1302(0x8e,0x00);  //在往寄存器写之前，要去除写保护（8e为写保护寄存器的地址）
	for(i=0;i<7;i++)   //分7次循环，向年、周、月、日、时、分、秒的寄存器写入时间数据
	{
		write_ds1302(write_add[i],time_data[i]);
	}
	write_ds1302(0x8e,0x80);  //加写保护
}

void read_rtc()   //读时间函数
{
	uchar i;
	for(i=0;i<7;i++)   //分7次循环，向年、周、月、日、时、分、秒的寄存器读取时间数据
	{
		time_data[i]=read_ds1302(read_add[i]);
	}
}

void time_pros()  //时间处理函数（把要显示的时分秒BCD码转换成十进制数）
{
	disp[0]=time_data[4]/16;   //时的十位数
	disp[1]=time_data[4]%16;  //时的个位数
	disp[2]=time_data[5]/16;   //分的十位数
	disp[3]=time_data[5]%16;  //分的个位数
	disp[4]=time_data[6]/16;   //秒的十位数
	disp[5]=time_data[6]%16;  //秒的个位数
}

void display()   //数码管显示函数
{
	P0=0xff;
	wela=1;
	wela=0;
	P0=table_du[disp[0]];
	dula=1;
	dula=0;
	P0=0xfe;  
	wela=1;
	wela=0;
	delay(3);

	P0=0xff;
	wela=1;
	wela=0;
	P0=table_du[disp[1]]|0x80;  //或上0x80目的是：让该位数码管显示出小数点
	dula=1;
	dula=0;
	P0=0xfd;
	wela=1;
	wela=0;
	delay(3);
	
	P0=0xff;
	wela=1;
	wela=0;
	P0=table_du[disp[2]];
	dula=1;
	dula=0;
	P0=0xfb;
	wela=1;
	wela=0;
	delay(3);

	P0=0xff;
	wela=1;
	wela=0;
	P0=table_du[disp[3]]|0x80;  //或上0x80目的是：让该位数码管显示出小数点
	dula=1;
	dula=0;
	P0=0xf7;
	wela=1;
	wela=0;
	delay(3);

	P0=0xff;
	wela=1;
	wela=0;
	P0=table_du[disp[4]];
	dula=1;
	dula=0;
	P0=0xef;
	wela=1;
	wela=0;
	delay(3);

	P0=0xff;
	wela=1;
	wela=0;
	P0=table_du[disp[5]];
	dula=1;
	dula=0;
	P0=0xdf;
	wela=1;
	wela=0;
	delay(3);
}

void main()  //主函数
{
	uchar ch;  
	ch=read_ds1302(0x81)>>7;  //读取秒寄存器的值，并保留最高位（0x81为寄存器读地址）
	if(ch)   //判断：若ch为0，不设置初值；若ch为1，重新设置初值
	{
	set_rtc();   //对DS1302设置时间初值
	}
	
	while(1)
	{
		read_rtc();   //向13012读取时间
		time_pros();  //时间处理
		display();    //时间显示
	}
}