/*********************************************************
*Copyright (C), 2023, Shanghai Eastsoft Microelectronics Co., Ltd
*文件名:  rtc.c
*作  者:  AE Team
*版  本:  v1.0，编译于iDesigner(v4.2.3.187) + 工具链HRCC(v1.2.0.136)
*日  期:  2023/01/31
*描  述:  RTC驱动
注意:DS1302写寄存器是地址是偶数,读的时候地址+1
比如:往0x80地址写入0x01,读取时需要从0x81读取
DS1302控制字:
bit7  bit6     bit5   bit4    bit3    bit2    bit1     bit0
1    RAM/-CK    A4     A3      A2      A1      A1     RD/-WR
[7]  :控制字的最高有效位(位7)必须是逻辑1,如果它为 0 则不能把数据写入DS1302;
[6]  :如果为0，则表示存取日历时钟数据,为1表示存取RAM数据;
[5:1]:指示操作单元的地址;
[0]  :0表示要进行写操作,为1表示进行读操作;
控制字总是从最低位开始输出.在控制字指令输入后的下一个SCLK时钟的上升沿时数据
被写入DS1302,数据输入从最低位(0位)开始.同样,在紧跟8位的控制字指令后的下一个
SCLK脉冲的下降沿,读出DS1302的数据,读出的数据也是从最低位到最高位.
存取日历时钟(BCD码):其中9~31地址不能存储数据
写入地址:0x00|0x80~0x3E|0x80   =    0x80~0xBE(取偶数)
读取地址:0x00|0x81~0x3E|0x81   =    0x81~0xBF(取奇数)
存取RAM:其中31地址不能存储数据
写入地址:0x00|0xC0~0x3E|0xC0   =    0xC0~0xFE(取偶数)
读取地址:0x00|0xC1~0x3E|0xC1   =    0xC1~0xFF(取奇数)
0x80---秒寄存器的BIT7表示时间暂停位,BIT7为1时停止工作,0时开始工作
0x84---小时寄存器的BIT表示12/24小时制,BIT7为1表示12时制,为0表示24时制;
        BIT5为AM/PM位,BIT5为1表示PM,0表示AM
0x8E---写保护<写入0x00表示允许进行写操作,0x80表示不允许写操作>
写日期时间寄存器:
0x80---秒
0x82---分
0x84---时
0x86---日
0x88---月
0x8A---星期
0x8C---年
读日期时间寄存器:
0x81---秒
0x83---分
0x85---时
0x87---日
0x89---月
0x8B---星期
0x8D---年
*****************************************************************************/
#include <string.h>
#include <HIC.h>
#include "types.h"
#include "rtc.h"
 
type_time t_time;
void uart_puts(char *str);

void delay(uint16_t us)
{
	while (us--) {
		NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
		NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
	}
}
 
#if 0
u8 max_day[12]={31,28,31,30,31,30,31,31,30,31,30,31}; //各月的天数
/**********************************************************
检查是否闰年,返回当前二月最大天数
判断闰年的完整公式为A=X4*10+X3,B=4,A/B;A=B*10+X2,B=4,A/B;A=B*10+X1,B=4,A/B
**********************************************************/
u8 chk_max_day(void)
{
    if(t_time.month == 2)
    {
        if(t_time.year % 4) //t_time.year取值00~99
            return (29);  //如果是闰年，返回29天。
        else
            return (28);  //否则为28天。
    }
    else
    {
        return max_day[t_time.month-1];
    }
}	
#endif
 
void ds1302_write_byte(uint8_t dat)//写一个字节
{
    uint8_t i;

    DS1302_IO_OUT();

    delay(5);

    for(i = 8;i > 0;i --)
    {
        if(dat & 0x01)
        {
            DS1302_IO_HIGH();
        }
        else
        {
            DS1302_IO_LOW();
        }
        dat >>= 1;
        DS1302_CLK_HIGH();
        delay(5);
        DS1302_CLK_LOW();
        delay(5);
    }
}
 
uint8_t ds1302_read_byte(void)//读一个字节
{
    uint8_t i = 0;
    uint8_t dat = 0;	
    
	DS1302_IO_IN();
    delay(5);
    
	for(i = 8;i > 0;i --)
    {
        dat >>= 1;
        if(DS1302_IO_IS_HIGH())
        {
            dat |= BIT(7);
        }
        else
        {
            dat &= ~BIT(7);
        }
        DS1302_CLK_HIGH(); //下降沿读取数据
        delay(5);
        DS1302_CLK_LOW();	
        delay(5);
    }

    return dat;
}
 
void ds1302_write(uint8_t adr,uint8_t dat)//向1302芯片写函数，指定写入地址，数据
{
    DS1302_RST_LOW();
    DS1302_CLK_LOW();	
    DS1302_RST_HIGH();
    ds1302_write_byte(adr);
    ds1302_write_byte(dat);
    DS1302_RST_LOW();
    DS1302_CLK_HIGH();
}
 
uint8_t ds1302_read(uint8_t adr)//从1302读数据函数，指定读取数据来源地址
{
    uint8_t temp = 0;
 
    DS1302_RST_LOW();
    DS1302_CLK_LOW();	
    DS1302_RST_HIGH();
    ds1302_write_byte(adr);
    temp = ds1302_read_byte();
    DS1302_RST_LOW();
    //DS1302_CLK_HIGH();
 
    return temp;
}
 
uint8_t bcd_dec(uint8_t bcd)//BCD码转10进制函数
{
    return ((bcd/16)*10+(bcd%16));	
}
 
uint8_t dec_bcd(uint8_t dec)//10进制转BCD码
{
    return ((dec/10)*16+(dec%10));
}

//uint8_t a,b,c;
void ds1302_write_time(void)//写入时间日期
{
	ds1302_write(0x8E, 0x00);        //允许写
	//t_time.wday = week_conversion(t_time.year,t_time.month,t_time.day);
	ds1302_write(0x80, dec_bcd(t_time.second));   //秒
	ds1302_write(0x82, dec_bcd(t_time.minute));  //分
	//a = t_time.hour/20;
	//b = t_time.hour - a*20;
	//c = b + (a<<5);
	//ds1302_write(0x84, dec_bcd(c));    //时
    ds1302_write(0x84, dec_bcd(t_time.hour));    //时
	ds1302_write(0x86, dec_bcd(t_time.day));     //日
	ds1302_write(0x88, dec_bcd(t_time.month));   //月
	//ds1302_write(0x8A,dec_bcd(t_time.wday));    //星期
	ds1302_write(0x8C, dec_bcd(t_time.year));    //年
	ds1302_write(0x8E, 0x80);        //禁止写
}

void ds1302_read_time(void) //读取时间数据
{
	t_time.second = bcd_dec(ds1302_read(0x81) & 0x7F);    //秒
	t_time.minute = bcd_dec(ds1302_read(0x83)); //分
	//b = ds1302_read(0x85);
	//t_time.hour = bcd_dec((b)&0x1F+(b>>5)*20);      //时
    t_time.hour = bcd_dec(ds1302_read(0x85));       //时
	t_time.day = bcd_dec(ds1302_read(0x87));        //日
	t_time.month = bcd_dec(ds1302_read(0x89));      //月
	t_time.year = bcd_dec(ds1302_read(0x8D));       //年
	t_time.wday = week_conversion(t_time.year, t_time.month, t_time.day);
}
 
void ds1302_conversion_hourly(uint8_t hourly, uint8_t ampm) //转换12/24小时制,AM/PM<此函数未验证>
{
    ds1302_write(0x8E,0x00);	//允许写
    if(hourly == HOURLY_12) 	//12小时制
    {
        t_time.hour |= BIT(7);
        if(ampm == HOURLY_PM) 	//PM
            t_time.hour |= BIT(5);
        else if(ampm == HOURLY_AM)
            t_time.hour &= ~BIT(5);
    }
    else if(hourly == HOURLY_24)	//24小时制
    {
        t_time.hour &= ~BIT(7);
    }
    ds1302_write(0x84,dec_bcd(t_time.hour));
    ds1302_write(0x8E,0x80);    	//禁止写
}

void ds1302_write_ram(uint8_t *buf, uint8_t len)
{
	uint8_t i;
	
	ds1302_write(0x8E, 0x00);
	for(i=0; i<len; i++)
	{
		ds1302_write(0xC0 + (i<<1), buf[i]);
	}
	ds1302_write(0x8E, 0x80);
}

void ds1302_read_ram(uint8_t *buf, uint8_t len)
{
	uint8_t i;
	
	ds1302_write(0x8E, 0x00);
	for(i=0; i<len; i++)
	{
		buf[i] = ds1302_read(0xC1 + (i<<1));
	}
	ds1302_write(0x8E, 0x80);
}

void ds1302_reset(void)//复位---写入寄存器时间日期和记忆信息
{
    //默认时间日期初始化
    t_time.second = 0;
    t_time.minute = 0;
    t_time.hour = 0;
    t_time.day = 1;
    t_time.month = 1;
    t_time.year = 23;
    t_time.wday = week_conversion(t_time.year,t_time.month,t_time.day);	
    //写入时间日期
    //ds1302_write_time();
/*********************************************************************
    //写入RAM的记忆信息
    ds1302_write_ram();
*********************************************************************/
    ds1302_write_time();
 
    ds1302_write(0x8E,0x00);		//允许写
    ds1302_write(0xFC,0x99);   		//用于判断是否曾经初始化过时间
    ds1302_write(0x8E,0x80);    	//禁止写
}

static char buf1[]="DS1302 reinit ......\n";
static char buf2[]="DS1302 init successful !\n";
static char buf3[]="DS1302 init error !\n";
void ds1302_init(void) //1302芯片初始化
{
    uint8_t temp = 0;
 
    DS1302_IO_INIT();
 
    DS1302_RST_LOW();
    DS1302_CLK_LOW();	
 
    temp = ds1302_read(0xFD);	//判断振荡器是否为开
    if(temp != 0x99) //如果特定寄存器的数据不是默认写入的值---重写默认时间	
    {	
        ds1302_reset(); //重新写入时间
        uart_puts(buf1);
    }

/********************************************************************************/
    ds1302_write(0x8E,0x00);	//允许写
    ds1302_write(0xFA,0x89);   	//写0x89
    ds1302_write(0x8E,0x80);    //禁止写
    //delay_ms(10);
    temp = ds1302_read(0xFB);	
    /*if(temp == 0x89)            //读取写进去的值
    {	
		char a='c';
        uart_puts(buf2);	
        uart_puts(a);
    }
    else
    {
        uart_puts(buf3);	
    }*/
/********************************************************************************/	
	
    ds1302_read_time();			//读取时间

	/********************************************************/	
	ds1302_write(0x8E,0x00);	//允许写

	if (t_time.second & 0x80) {
		t_time.second &= 0x7F; 		//启动DS1302时钟振荡器(0x80寄存器的bit7置0)
		ds1302_write(0x80,dec_bcd(t_time.second));
	}

    ds1302_write(0x90,TCS_OFF|DS_OFF);		//充电被禁止
 
    //ds1302_conversion_hourly(HOURLY_12,HOURLY_PM);//设置小时制
/********************************************************/	
    ds1302_write(0x8E,0x80);    //禁止写
/*
    if(t_time.second > 59)
        t_time.second = 0;
    if(t_time.minute > 59)
        t_time.minute = 0;
    if(t_time.hour > 23)
        t_time.hour = 0;	
    if(t_time.day > 31)
        t_time.day = 1;		
    if(t_time.month > 12)
        t_time.month = 1;
    if(t_time.year > 99)
        t_time.year = 0;	
*/
}
 
/****************************************************************************
算法:日期+年份+所过闰年数+月较正数之和除7 的余数就是星期,但如果是在闰年又不到
3月份,上述之和要减一天再除7,星期数为0
century=0 为21世纪,century=1 为20世纪
输入输出数据均为BCD数据
年月日为16进制数
*****************************************************************************/
uint8_t week_conversion(uint8_t year, uint8_t month, uint8_t day) //星期转换
{	
    uint16_t y = year + 2000;

    //一月和二月被当作前一年的
    if((month==1)||(month==2))
    {
        month+=12;
        y--;
    }

    return (day+2*month+3*(month+1)/5+y+y/4-y/100+y/400)%7+1;
}
