
/******************************************************************************
版权所有：	
文件名称：	e2prom_24lc.c
文件版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-06
功能说明：	MICROCHIP 24LC系列的E2PROM驱动程序。
其它说明：	在I2C总线频率357k(周期2.8us)的情况下，写256bytes需要22206us，读256bytes
			需要6836us。
修改记录：
*/

/*------------------------------- 头文件 --------------------------------------
*/
#include "bspconfig.h"
#include "i2c.h"
#include "ustimer.h"
#include "gpio.h"
#include "e2prom.h"
#if 0

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

#define _DEBUG_E2PROM

#endif


#define E2PROM_I2C_ID		0XA0



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


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


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


/******************************************************************************
函数名称：	ac_e2prom_read
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-06
函数说明：	从E2PROM中读出数据。
参数说明：	
	offset(in)：需要读的数据在E2PROM中的偏移量，和length之和不能大于
				CFG_E2PROM_SIZE.
	buffer(out):读出数据的buffer。
	length(in):	需要读出数据的长度，和offset之和不能大于CFG_E2PROM_SIZE。
返回值：  	成功返回0.
修改记录：	
*/
int ac_e2prom_read(unsigned char addr,uint32_t offset,unsigned char * buffer,uint32_t length)
{
	unsigned char c;
	int h_i2c,ret;
	
	//检查参数
	if(addr >= 8)
	{
		return -11;
	}
	
	if(buffer == 0 || (offset + length) > CFG_E2PROM_SIZE)
	{
		
		return -1;
	}
	
	if(length == 0)
	{
		
		return 0;
	}
	
	h_i2c = i2c_open(I2C_ID_E2PROM);
	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 = E2PROM_I2C_ID | (addr<<1);							
	I2C_WAIT_COMPLETION();
	I2C_CHECK_ACK();
	
	//发送地址0
	MCF_I2DR = (unsigned char)(offset>>8);						
	I2C_WAIT_COMPLETION();	
	I2C_CHECK_ACK();

	//发送地址1
	MCF_I2DR = (unsigned char)(offset);						
	I2C_WAIT_COMPLETION();	 	
	I2C_CHECK_ACK();
	
	//发送重开始信号，方向改为读
	MCF_I2CR |= MCF_I2C_I2CR_RSTA;	
	MCF_I2DR = E2PROM_I2C_ID | (addr<<1) | 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;
}


/******************************************************************************
函数名称：	ac_e2prom_write
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-06
函数说明：	将数据写入E2PROM中。注意：E2PROM按页写的速度比按字节写的速度快，
			所以如果应用程序将要写入的数据组织成
参数说明：	
	offset(in)：需要写入的数据在E2PROM中的偏移量，和length之和不能大于
				CFG_E2PROM_SIZE.
	buffer(in):写入数据的buffer。
	length(in):	需要写入数据的长度，和offset之和不能大于CFG_E2PROM_SIZE。
返回值：  	成功返回0.
修改记录：	
*/
int ac_e2prom_write(unsigned char addr,uint32_t offset,unsigned char * buffer,uint32_t length)
{
	int ret;
	uint32_t length_once,length_spare;
	
	//检查参数
	if(addr >= 8)
	{
		return -11;
	}
	
	if(buffer == 0 || (offset + length) > CFG_E2PROM_SIZE)
	{		
		return -1;
	}
	
	if(length == 0)
	{
		
		return 0;
	}
	
	//初始化length_spare	
	length_spare = length;
	
	//写起始不完整页
	length_once = CFG_E2PROM_PAGE_SIZE - (offset&(CFG_E2PROM_PAGE_SIZE - 1));
	if(length_once > length_spare)
	{
		
		length_once = length_spare;
	}
	
	if(length_once)
	{
		ret = _ac_e2prom_write(addr,offset,buffer,length_once);
		if(ret != 0)
		{
			
			return ret;
		}
		
		offset += length_once;
		buffer += length_once;
		length_spare -= length_once;
	}
	
	//循环写完整页。
	length_once = CFG_E2PROM_PAGE_SIZE;
	while(length_spare > CFG_E2PROM_PAGE_SIZE)
	{
		ret = _ac_e2prom_write(addr,offset,buffer,length_once);
		if(ret != 0)
		{
			
			return ret;
		}
		
		offset += length_once;
		buffer += length_once;
		length_spare -= length_once;
	}
	
	//写末尾不完整页。
	length_once = length_spare;
	if(length_once)
	{
		ret = _ac_e2prom_write(addr,offset,buffer,length_once);
		if(ret != 0)
		{
			
			return ret;
		}
	}
		
	return 0;
}



/*------------------------------ 内部函数 -------------------------------------
*/
/******************************************************************************
函数名称：	 _ac_e2prom_write
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-06
函数说明：	内部函数，将数据写入E2PROM中。
参数说明：	
	offset(in)：需要写入的数据在E2PROM中的偏移量，和length之和不能大于CFG_E2PROM_SIZE.
	buffer(in):写入数据的buffer。
	length(in):	需要写入数据的长度，和offset之和不能大于CFG_E2PROM_PAGE_SIZE。
返回值：  	成功返回0.
修改记录：	
*/
int _ac_e2prom_write(unsigned char addr,uint32_t offset,unsigned char * buffer,uint32_t length)
{
	int h_i2c,ret;
	unsigned long us0,us;
	
	//打开总线
	h_i2c = i2c_open(I2C_ID_E2PROM);
	if(h_i2c < 0)
	{
		
		return -1;
	}
	
	//打开写保护.
	//GPIO_WP_E2PROM_LOW();
	
	ret = -2;
	
	//等待总线释放
	I2C_WAIT_BUS_IDLE();
	
	//发送模式
	MCF_I2CR |= MCF_I2C_I2CR_MTX;			
	
	//发送START信号
	MCF_I2CR |= MCF_I2C_I2CR_MSTA;
	
	//发送控制字节
	MCF_I2DR = E2PROM_I2C_ID | (addr<<1);							
	I2C_WAIT_COMPLETION();
	I2C_CHECK_ACK();
	
	//发送地址0
	MCF_I2DR = (unsigned char)(offset>>8);						
	I2C_WAIT_COMPLETION();	
	I2C_CHECK_ACK();

	//发送地址1
	MCF_I2DR = (unsigned char)(offset);						
	I2C_WAIT_COMPLETION();	 	
	I2C_CHECK_ACK();
	
	
	//发送数据
	while(length--)
	{
		MCF_I2DR = 	*buffer++;
		I2C_WAIT_COMPLETION();
		I2C_CHECK_ACK();
	}
	
	//发送STOP信号，启动E2PROM内部写
	MCF_I2CR &= ~MCF_I2C_I2CR_MSTA;
	
	//等待总线释放
	I2C_WAIT_BUS_IDLE();

	//等待完成
	//发送START信号
	MCF_I2CR |= MCF_I2C_I2CR_MSTA;
	
	us0= ustimer_get_origin();
	while(1)
	{
		// 得到超时值
		us = ustimer_get_duration(us0);
		
		//发送控制字节
		MCF_I2DR = E2PROM_I2C_ID | (addr<<1);							
		I2C_WAIT_COMPLETION();
		
		//如果有ACK,内部写已经完成，退出。
		if((MCF_I2SR & MCF_I2C_I2SR_RXAK) == 0)
		{
			ret = 0;
			break;
		}
		
		//最大写时间为5ms.
		if(us > 5*USTIMER_MS)
		{
			ret = -3;
			break;
		}
		
		//发送重开始信号
		MCF_I2CR |= MCF_I2C_I2CR_RSTA;
	}
	
LABEL_END:

	//发送STOP信号
	MCF_I2CR &= ~MCF_I2C_I2CR_MSTA;
	
	//关闭写保护
	//GPIO_WP_E2PROM_HIGH();
	
	//关闭总线
	i2c_close(h_i2c);
	
	return ret;
}


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

#include "rt.h"
#define E2PROM_TEST_SIZE	1002 //必须是偶数
//#define E2PROM_TEST_SUM		CFG_E2PROM_SIZE
#define E2PROM_TEST_SUM		4096

int ac_e2prom_test(void)
{
	int ret;
	unsigned long us0,us;
	unsigned long i,offset,length;
	unsigned short buffer[E2PROM_TEST_SIZE/2];
	unsigned char addr;
	
	rt_printf("ac_e2prom_test start...\r\n");

	addr = 0;
	for(addr=0; addr<4; addr++)
	{
		rt_printf("addr=%d.\r\n",addr);
		
		offset = 0;
		while(offset < E2PROM_TEST_SUM)
		{
			//写
			length = (E2PROM_TEST_SUM - offset) > E2PROM_TEST_SIZE ? E2PROM_TEST_SIZE : (E2PROM_TEST_SUM - offset);
			for(i=0;i<length/2;i++)
			{
				
				buffer[i] = (unsigned short)(offset + i + 0x5555);
			}
			us0 = ustimer_get_origin();
			ret = ac_e2prom_write(addr,offset,(unsigned char *)buffer,length);
			us = ustimer_get_duration(us0);
			rt_printf("ac_e2prom_write:(offset=%d,ret=%d,us=%d)\r\n",offset,ret,us);
			if(ret != 0)
			{
				
				break;
			}
			
			offset += length;
		}
		
		offset = 0;
		memset(buffer,0,sizeof(buffer));
		while(offset < E2PROM_TEST_SUM)
		{
			//读
			length = (E2PROM_TEST_SUM - offset) > E2PROM_TEST_SIZE ? E2PROM_TEST_SIZE : (E2PROM_TEST_SUM - offset);
			memset(buffer,0,E2PROM_TEST_SIZE);
			us0 = ustimer_get_origin();
			ret = ac_e2prom_read(addr,offset,(unsigned char *)buffer,length);
			us = ustimer_get_duration(us0);
			rt_printf("ac_e2prom_read:(offset=%d,ret=%d,us=%d)\r\n",offset,ret,us);
			
			if(ret != 0)
			{
				
				break;
			}
			
			//比较
			for(i=0;i<length/2;i++)
			{
				if(buffer[i] != (unsigned short)(offset + i + 0x5555))
				{
					
					break;
				}
			}
			if(i== length/2)
			{
				
				rt_printf("compare ok(offset=%d,i=%d)!\r\n",offset,i);
			}
			else
			{
				rt_printf("compare error(offset=%d,i=%d)!\r\n",offset,i);
				break;
			}
			
			offset += length;
			
		}
		
		if(offset == E2PROM_TEST_SUM)
		{
			
			rt_printf("ac_e2prom_test ok!\r\n\n");
		}
		else
		{
			
			rt_printf("ac_e2prom_test fail!\r\n\n");
		}
	}
	return 0;
}

#endif

#endif
/*------------------------------ 文件结束 -------------------------------------
*/
