/******************************************************************************
版权所有：	
文件名称：	rtc_ds1338.c
文件版本：	01.01
创建作者:	sunxi
创建日期：	2008-07-09
功能说明：	DS1338 实时时钟驱动程序。
其它说明：	DS1338中的年从2000年开始。	
修改记录：
*/

/*------------------------------- 头文件 --------------------------------------
*/
#include "bspconfig.h"
#include "i2c.h"
#include "my_rtc.h"
#include "watchdog.h"
//#include "rt_ints.h"


/*------------------------------- 宏定义 --------------------------------------
*/
#if CFG_BSP_DEBUG

#define _DEBUG_RTC

#endif


#define RTC_I2C_ID			0X64

#define RTC_ADDR_EXT		0X0D
#define RTC_ADDR_FLAG		0X0E
#define RTC_ADDR_CONTROL	0X0F

#define RTC_FLAG_VLF		0X02

//rtc rx8025内部结构定义
typedef struct
{
	u8 second;	//0~59
	u8 minute;	//0~59
	u8 hour;	//0~23
	u8 week;	//每个星期用一个bit表示
	u8 date;	//1~31
	u8 month;	//1~12 	*与tm结构范围不同
	u8 year;	//0~99  *与tm结构范围不同
}rtc_t;

typedef struct
{
	u8 ext_reg;
	u8 flg_reg;
	u8 ctl_reg;	//
}rtc_ctrl_t;

// 此结构仅供参考，不可使用
typedef struct
{
	rtc_t rtc;
	
	u8 ram;		// 可以任意读写
	u8 min_alm; //
	u8 hour_alm;
	u8 day_alm;
	u8 tim_cnt0;
	u8 tim_cnt1;
	
	rtc_ctrl_t rtc_ctrl;
}rtc_all_t;


/*------------------------------ 全局变量 -------------------------------------
*/

int	  g_rtc_write_5ms; 	// 离实际写入时间还差多少个5ms
rtc_t g_rtc_write_data;	// 需要写入RTC芯片的时间


/*------------------------------ 函数声明 -------------------------------------
*/
int _rtc_read(uint32_t offset, unsigned char *buffer, uint32_t length);
int _rtc_write(uint32_t offset, unsigned char *buffer, uint32_t length);


/*------------------------------ 外部函数 -------------------------------------
*/
/******************************************************************************
函数名称：	rtc_init
函数版本：	01.01
创建作者:	sunxi
创建日期：	2008-08-06
函数说明：	rtc初始化。
参数说明：	无
返回值：  	成功返回0.
修改记录：	
*/
int rtc_init(void)
{
	return 0;
}


/******************************************************************************
函数名称：	rtc_time_read
函数版本：	01.01
创建作者:	sunxi
创建日期：	2008-08-06
函数说明：	从时钟芯片中读出时间。
参数说明：	
	p_tm(out):	读出的时间存放在这个指针所指的结构中。
返回值：  	
	0：	成功返回。
	1：	成功返回，但是由于时钟芯片振荡器曾经停止过，所以时间可能不准。
	<0：读取失败。
	
修改记录：	
*/
int rtc_time_read(struct rtc_time_t *p_tm)
{
	int ret;
	rtc_t rtc;
	u8 flag=0;
	
	//检查参数
	if(p_tm == 0)
	{
		return -1;
	}
	
	memset(&rtc, 0x00, sizeof(rtc_t));
	//读出数据
	ret = _rtc_read(0,(unsigned char *)&rtc,sizeof(rtc));
	if(ret != 0)
	{
		return -2;
	}
	ret = _rtc_read(RTC_ADDR_FLAG,&flag,1);
	if(ret != 0)
	{
		return -3;
	}
	
	//转换格式
	//清除不需要的标志
	rtc.second 		&= ~0x80;
	rtc.hour 		&= ~0xc0;
	
	//BCD转化为二进制
	p_tm->ms 	= (unsigned short)((rtc.second >> 4)*10 	+ (rtc.second & 0xf));	//秒
	p_tm->min 	= (unsigned char )((rtc.minute >> 4)*10 	+ (rtc.minute & 0xf));	//分
	p_tm->hour 	= (unsigned char )((rtc.hour   >> 4)*10 	+ (rtc.hour   & 0xf));	//时
	p_tm->day 	= (unsigned char )((rtc.date   >> 4)*10 	+ (rtc.date   & 0xf));	//日
	p_tm->month = (unsigned char )((rtc.month  >> 4)*10 	+ (rtc.month  & 0xf));	//月
	p_tm->year	= (unsigned char )((rtc.year   >> 4)*10 	+ (rtc.year   & 0xf));	//年

	//s->ms
	p_tm->ms *= 1000;
	
	//如果时钟振荡器曾经停止过，时间有可能不准，返回1。
	if(flag&RTC_FLAG_VLF)
	{
		return 1;
	}
	
	return 0;
}


/******************************************************************************
函数名称：	rtc_time_write
函数版本：	01.01
创建作者:	sunxi
创建日期：	2008-08-06
函数说明：	将时间写入时钟芯片。
参数说明：	
	p_tm(in):	要写入的时间存放在这个指针所指的结构中。
返回值：  	
	0：	成功返回。
	<0：写入失败。
	
修改记录：	
*/
extern void sys_time_change(struct rtc_time_t *p_ct);
int rtc_time_write(struct rtc_time_t *p_tm)
{
	//uint32_t flags;
	int seccond,ms5;
	rtc_t rtc;

//	rt_printf("rtc_time_write.\r\n");
	
	//检查参数
	if(p_tm == 0)
	{
		return -11;
	}
	
	if(p_tm->ms > 59999)	
	{
		return -12;
	}
	
	if(p_tm->min > 59)	
	{
		return -13;
	}
	
	if(p_tm->hour > 23)	
	{
		return -14;
	}
	
	if(p_tm->day <1 	|| p_tm->day > 31)	
	{
		return -15;
	}
	
	if(p_tm->month <1	|| p_tm->month > 12)	
	{
		return -16;
	}
	
	if(p_tm->year > 99)	
	{
		return -17;
	}

	//ms->s,使用下一秒作为要写入的秒值
	seccond = p_tm->ms/1000 + 1;
	ms5 = (seccond*1000 - p_tm->ms)/5;
	if(ms5 <1)
	{
		ms5 = 1;
	}
	
	if(seccond>=60)
	{
		seccond-=60;
		sys_time_change(p_tm);
	}
	
	//二进制转换为BCD
	rtc.second 	= (unsigned char)(((seccond    /10)<<4) | (seccond  %10));
	rtc.minute 	= (unsigned char)(((p_tm->min  /10)<<4) | (p_tm->min  %10));
	rtc.hour 	= (unsigned char)(((p_tm->hour /10)<<4) | (p_tm->hour %10));
	rtc.date 	= (unsigned char)(((p_tm->day  /10)<<4) | (p_tm->day %10));
	rtc.month 	= (unsigned char)(((p_tm->month/10)<<4) | (p_tm->month  %10));
	rtc.year 	= (unsigned char)(((p_tm->year /10)<<4) | (p_tm->year %10));
	
	//星期不使用直接赋值1
	rtc.week 	= 1;
	
	//写入数据
	rt_irq_save(flags);
	g_rtc_write_data = rtc;
	g_rtc_write_5ms = ms5;
	rt_irq_restore(flags);

	return 0;
}


void rtc_time_write_5ms(void)
{
	int ret = 0;
	rtc_ctrl_t rtc_ctrl;
	
	if(g_rtc_write_5ms == 0)
	{
		return;
	}
		
	if(g_rtc_write_5ms == 1)
	{
		// 写入时间数据
		//ret = _rtc_write(0,(unsigned char *)&g_rtc_write_data,sizeof(g_rtc_write_data));  //jack.liu 20200921 读写RTC失能
		if(ret)
		{
			rt_printf("rtc_write_5ms0:ret=%d)\r\n",ret);
		}

		// 写入控制数据
		rtc_ctrl.ext_reg = 0x00;
		rtc_ctrl.flg_reg = 0x00;
		rtc_ctrl.ctl_reg = 0x40;// 默认2.0s温度补偿
		//ret = _rtc_write(RTC_ADDR_EXT,(unsigned char *)&rtc_ctrl,sizeof(rtc_ctrl));  //jack.liu 20200921 读写RTC失能
		if(ret)
		{
			rt_printf("rtc_write_5ms1:ret=%d)\r\n",ret);
		}
	}
	
	g_rtc_write_5ms--;
}




/******************************************************************************
函数名称：	rtc_nvram_read
函数版本：	01.01
创建作者:	sunxi
创建日期：	2008-08-07
函数说明：	从时钟芯片的NVRAM中读出数据。
参数说明：	
	offset(in)：需要读的数据在NVRAM中的偏移量，和length之和必须小于CFG_RTC_NVRAM_SIZE.
	buffer(out):读出数据的buffer。
	length(in):	需要读出数据的长度，和offset之和必须小于CFG_RTC_NVRAM_SIZE。
返回值：  	成功返回0.
修改记录：	
*/
int rtc_nvram_read(uint32_t offset, unsigned char *buffer, uint32_t length)
{
	//检查参数
	if(buffer == 0 || (offset + length) > CFG_RTC_NVRAM_SIZE)
	{
		return -1;
	}
	if(length == 0)
	{
		return 0;
	}
	
	return _rtc_read(offset+7,buffer,length);
}


/******************************************************************************
函数名称：	rtc_nvram_write
函数版本：	01.01
创建作者:	sunxi
创建日期：	2008-08-07
函数说明：	将数据写入时钟芯片的NVRAM中。
参数说明：	
	offset(in)：需要写入的数据在NVRAM中的偏移量，和length之和必须小于CFG_RTC_NVRAM_SIZE.
	buffer(in):写入数据的buffer。
	length(in):	需要写入数据的长度，和offset之和必须小于CFG_RTC_NVRAM_SIZE。
返回值：  	成功返回0.
修改记录：	
*/
int rtc_nvram_write(uint32_t offset, unsigned char *buffer, uint32_t length)
{
	//检查参数
	if(buffer == 0 || (offset + length) > CFG_RTC_NVRAM_SIZE)
	{
		return -1;
	}
	if(length == 0)
	{
		return 0;
	}
	
	return _rtc_write(offset+7, buffer, length);
}



/*------------------------------ 内部函数 -------------------------------------
*/
/******************************************************************************
函数名称：	_rtc_read
函数版本：	01.01
创建作者:	sunxi
创建日期：	2008-08-07
函数说明：	从时钟芯片中读出数据。
参数说明：	
	offset(in)：需要读的数据在时钟芯片的偏移量.
	buffer(out):读出数据的buffer。
	length(in):	需要读出数据的长度。
返回值：  	成功返回0.
修改记录：	
*/
int _rtc_read(uint32_t offset, unsigned char *buffer, uint32_t length)
{
#if 0
	unsigned char c;
	int h_i2c,ret;
	h_i2c = i2c_open(I2C_ID_RTC);
	if(h_i2c < 0)
	{
		return -1;
	}
	
	ret = -2;
	
	//等待总线释放
	I2C_WAIT_BUS_IDLE();
	
	//发送模式
	MCF_I2CR |= MCF_I2C_I2CR_MTX;			
	
	//发送START信号
	MCF_I2CR |= MCF_I2C_I2CR_MSTA;
	
	//发送控制字节
	MCF_I2DR = RTC_I2C_ID;							
	I2C_WAIT_COMPLETION();
	I2C_CHECK_ACK();
	
	//发送地址
	MCF_I2DR = (unsigned char)(offset);						
	I2C_WAIT_COMPLETION();	 	
	I2C_CHECK_ACK();
	
	//发送重开始信号，方向改为读
	MCF_I2CR |= MCF_I2C_I2CR_RSTA;	
	MCF_I2DR = RTC_I2C_ID | 0x01;				
	I2C_WAIT_COMPLETION();
	I2C_CHECK_ACK();
	
	
	//接收模式
	MCF_I2CR &= ~MCF_I2C_I2CR_MTX;			
	
	//接收数据，最后一个字节不发ACK.
	if(length == 1)
	{
		MCF_I2CR |= MCF_I2C_I2CR_TXAK;	//不发ACK
		c = MCF_I2DR;				//空读，启动下一个字节的读
		I2C_WAIT_COMPLETION();
		*buffer++ = MCF_I2DR;
		I2C_WAIT_COMPLETION();
		
	}
	else
	{
		//循环接收length个字节。
		MCF_I2CR &= ~MCF_I2C_I2CR_TXAK;	//发送ACK
		c = MCF_I2DR;				//空读，启动下一个字节的读
		I2C_WAIT_COMPLETION();
		
		while(length--)
		{
			if(length == 1)
			{
				
				MCF_I2CR |= MCF_I2C_I2CR_TXAK;	//不发ACK
			}
			*buffer++ = MCF_I2DR;			//读数据，并启动下一个字节的读。
			I2C_WAIT_COMPLETION();
		}
	}
	
	ret = 0;
	
LABEL_END:

	//发送STOP信号
	MCF_I2CR &= ~MCF_I2C_I2CR_MSTA;
	
	i2c_close(h_i2c);
	
	return ret;
#endif
	return 0;
}


/******************************************************************************
函数名称：	_rtc_write
函数版本：	01.01
创建作者:	sunxi
创建日期：	2008-08-07
函数说明：	将数据写入时钟芯片中。
参数说明：	
	offset(in)：需要写入的数据在时钟芯片中的偏移量.
	buffer(in):写入数据的buffer。
	length(in):	需要写入数据的长度。
返回值：  	成功返回0.
修改记录：	
*/
int _rtc_write(uint32_t offset, unsigned char *buffer, uint32_t length)
{
#if 0
	int h_i2c,ret;
	
	h_i2c = i2c_open(I2C_ID_RTC);
	if(h_i2c < 0)
	{
		
		return -1;
	}
	
	ret = -2;
	
	//等待总线释放
	I2C_WAIT_BUS_IDLE();
	
	//发送模式
//	MCF_I2CR |= MCF_I2C_I2CR_MTX;			
	
	//发送START信号
//	MCF_I2CR |= MCF_I2C_I2CR_MSTA;
	
	//发送控制字节
	MCF_I2DR = RTC_I2C_ID;							
	I2C_WAIT_COMPLETION();
	I2C_CHECK_ACK();
	
	//发送地址
	MCF_I2DR = (unsigned char)(offset);						
	I2C_WAIT_COMPLETION();	 	
	I2C_CHECK_ACK();
	
	//发送数据
	while(length--)
	{
		MCF_I2DR = 	*buffer++;
		I2C_WAIT_COMPLETION();
		I2C_CHECK_ACK();
	}
	
	ret = 0;
	
LABEL_END:

	//发送STOP信号
//	MCF_I2CR &= ~MCF_I2C_I2CR_MSTA;

	i2c_close(h_i2c);
	
	return ret;
#endif
	return 0;
}


/*------------------------------ 测试函数 -------------------------------------
*/
#ifdef _DEBUG_RTC

#include "ustimer.h"
#include "rt.h"

#define RTC_TEST_SIZE	1

int rtc_test(void)
{
	int ret;
	uint32_t us0,us;
	uint32_t i,offset,length;
	unsigned char buffer[CFG_RTC_BUFFER_SIZE];
	struct rtc_time_t tm0;
	
	rt_printf("rtc_test start...\r\n");
	
	offset = 0;
	while(offset < CFG_RTC_NVRAM_SIZE)
	{
		//写
		length = (CFG_RTC_NVRAM_SIZE - offset) > RTC_TEST_SIZE ? RTC_TEST_SIZE : (CFG_RTC_NVRAM_SIZE - offset);
		for(i=0;i<length;i++)
		{
			
			buffer[i] = (unsigned char)(offset + i + 0x37);
		}
		us0 = ustimer_get_origin();
		ret = rtc_nvram_write(offset,(unsigned char *)buffer,length);
		us = ustimer_get_duration(us0);
	//	rt_printf("rtc_nvram_write:(offset=%d,ret=%d,us=%d)\r\n",offset,ret,us);
		if(ret != 0)
		{
			
			break;
		}
		
		offset += length;
	}
	
	offset = 0;
	while(offset < CFG_RTC_NVRAM_SIZE)
	{
		//读
		length = (CFG_RTC_NVRAM_SIZE - offset) > RTC_TEST_SIZE ? RTC_TEST_SIZE : (CFG_RTC_NVRAM_SIZE - offset);
		memset(buffer,0,RTC_TEST_SIZE);
		us0 = ustimer_get_origin();
		ret = rtc_nvram_read(offset,(unsigned char *)buffer,length);
		us = ustimer_get_duration(us0);
	//	rt_printf("rtc_nvram_read:(offset=%d,ret=%d,us=%d)\r\n",offset,ret,us);
		
		if(ret != 0)
		{
			
			break;
		}
		
		//比较
		for(i=0;i<length;i++)
		{
			if(buffer[i] != (unsigned char)(offset + i + 0x37))
			{
				
				break;
			}
		}
		if(i!= length)
		{
			rt_printf("compare error(offset=%lu,i=%lu)!\r\n",offset,i);
			break;
		}
		
		offset += length;
		
	}
	
	if(offset == CFG_RTC_NVRAM_SIZE)
	{
		
		rt_printf("rtc_nvram ok!\r\n\n");
	}
	else
	{
		
		rt_printf("rtc_nvram  fail!\r\n\n");
	}
	
	//开始测试时间
	ret = rtc_time_read(&tm0);
	if(ret == 1)
	{
		
		rt_printf("rtc was stoped!\r\n");
	}
	else if(ret == 0)
	{
		
		rt_printf("rtc is running!\r\n");
	}
	else
	{
		
		rt_printf("rtc_time_read test fail!");
	}

	tm0.ms  = 8000;
	tm0.min  = 28;
	tm0.hour = 14;
	tm0.day = 19;
	tm0.month	= 10;
	tm0.year = 2010 - 2000;

#if 0
	ret = rtc_time_write(&tm0);
	if(ret == 0)
	{
		
		rt_printf("rtc_time_write ok!\r\n\n");
	}
	else
	{
		
		rt_printf("rtc_time_write  fail(ret=%d)!\r\n\n",ret);
	}
#endif
	
	us0 = ustimer_get_origin();
	while(1)
	{
		ret = rtc_time_read(&tm0);
		if(ret == 0 || ret == 1)
		{
			
			rt_printf("date(ret=%d): %04d-%02d-%02d time: %02d:%02d:%05d!\r\n",
					ret,
					tm0.year + 2000,
					tm0.month,
					tm0.day,
					tm0.hour,
					tm0.min,
					tm0.ms
					);
		}
		else
		{
			
			rt_printf("rtc_time_read  fail(ret=%d)!\r\n\n",ret);
		}

		watchdog_feed_mainloop();
		
		//精确间隔1S循环。
		ustimer_delay_origin(us0,USTIMER_SEC);
		us0 += USTIMER_SEC;
	}
	
	return 0;
}
#endif

