/*********************************************************
*Copyright (C), 2023, Shanghai Eastsoft Microelectronics Co., Ltd.
 * @文件名:    rtc.c
 * @作  者:    AE Team
 * @版  本:    V1.00
 * @日  期:    2023/02/01
 * @描  述:    DS1302驱动
		注意: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---年
 * @note
 *          Change Logs:
 *          Date            Author          Notes
 *
 * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **********************************************************************************
 */
#include <string.h>
#include "rtc.h"

type_time t_time;

void delay(uint16_t us)
{
    uint32_t i;

    while (us--)
    {
        for (i = 0; i < 5; i++);
    }
}

#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) + ((bcd_dec(b) >> 5) * 20); //时
//    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.wday = bcd_dec(ds1302_read(0x8B));     //星期
    t_time.year = bcd_dec(ds1302_read(0x8D));       //年

    t_time.wday = week_conversion(t_time.year, t_time.month, t_time.day);
    //printf("%d : %d : %d\n",t_time.hour,t_time.minute,t_time.second);

    //printf("日期 : 20%d年%d月%d日  星期%d\n",t_time.year,t_time.month,t_time.day,t_time.wday);
    //printf("时间 : %02d : %02d : %02d\n\n",t_time.hour,t_time.minute,t_time.second);
}

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_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_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;
    GPIO_InitSettingType x;
	memset(&x, 0, sizeof(x));

    x.Signal = GPIO_Pin_Signal_Digital;
    x.Dir = GPIO_Direction_Output;
    x.Func = GPIO_Reuse_Func0;
    x.ODE = GPIO_ODE_Output_Disable;
    x.DS = GPIO_DS_Output_Normal;
    x.PUE = GPIO_PUE_Input_Disable;
    x.PDE = GPIO_PDE_Input_Disable;

    GPIO_Init(DS1302_RST_PIN, &x);
    GPIO_Init(DS1302_IO_PIN, &x);
    GPIO_Init(DS1302_CLK_PIN, &x);

    DS1302_RST_LOW();
    DS1302_CLK_LOW();

    temp = ds1302_read(0xFD);   //判断振荡器是否为开

    if (temp != 0x99) //如果特定寄存器的数据不是默认写入的值---重写默认时间
    {
        ds1302_reset(); //重新写入时间
        printf("%s", 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(0x90,TCS_ON|DS_DIODE_TWO|CS_RES_8K); //启动充电---2个二极管,8K电阻涓流充电
    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--;
    }

    //u8 week=(day+2*month+3*(month+1)/5+y+y/4-y/100+y/400)%7;
    //printf("new:%d.%d.%d\n",y,month,day);
    return (day + 2 * month + 3 * (month + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7 + 1;
}
