/***********************************************************************
 * Copyright (c)  2017 - 2022, Unicmicro Co.,Ltd .
 * All rights reserved.
 * Filename    : i2c1_master.c
 * Description : i2c1_master source file
 * Author(s)   : Neil
 * version     : V1.0
 * Modify date : 2019-05-22
 ***********************************************************************/
#include "i2c1_master.h"

/************************************************************************
 * function   : i2c_get_int_status
 * Description: i2c_get_int_status
 * input : none
 * return: 1: IFLG status is 1, 0: IFLG status is 0
 ************************************************************************/
uint8_t i2c_get_int_status(void)
{
	if(REG_I2C1_CR & I2C_CR_IFLG) 	
		return 1;
	else 							
		return 0;	
}

/************************************************************************
 * function   : i2c_get_status
 * Description: i2c_get_status
 * input : none
 * return: status_flag
 ************************************************************************/
uint8_t i2c_get_status(void)
{
	return REG_I2C1_STAT;
}


/************************************************************************
 * function   : i2c_write_byte
 * Description: i2c_write_byte
 * input : 发送的数据（1 Byte）
 * return: rdata: send_result
 ************************************************************************/
static void i2c_write_byte(uint8_t data)
{
	REG_I2C1_DATA = data;
}

/************************************************************************
 * function   : i2c_read_byte
 * Description: i2c_read_byte
 * input : none
 * return: rdata: 接收数据（1 Byte）
 ************************************************************************/
static uint8_t i2c_read_byte(void)
{
	return REG_I2C1_DATA;
}

/************************************************************************
 * function   : i2c_wait_state
 * Description: i2c_wait_state
 * input : 
			uint8_t status: expected status
			uint32_t wait_time: wait time
 * return: 0:TIMEOUT  1: expected status OK ，2：unexpected status OK   
 ************************************************************************/
static uint8_t i2c_wait_state(uint8_t status,uint32_t wait_time)
{
	uint8_t value;
	
	while(wait_time--)	
	{
		value = REG_I2C1_STAT;
		if(value == status)   //状态匹配
			{	
				return 1;
      }	
        
		if((value == I2C_STA_TX_ADDR_W_NAK)||
				(value == I2C_STA_TX_DATA_NAK)  ||
		    (value == I2C_STA_TX_ADDR_R_NAK)||
		    (value == I2C_STA_SLAVE_TX_DATA_NAK))			
			{
				return 2;
      }	
		if(value == I2C_STA_SLAVE_RX_STP_RSTA )
			{
				return 3;
			}			
	}
	
	return 0; 
}

/************************************************************************
 * function   : i2c_write_byte_wait_status
 * Description: i2c_write_byte_wait_status
 * input : byte
 * return:  result ，0 error， other：OK  
 ************************************************************************/
static uint8_t i2c_write_byte_wait_status(uint8_t byte,uint8_t status)
{
    uint8_t result = 0;
	
    i2c_write_byte(byte);
	  //清中断标志 ，数据填好后清标志（slave下 释放时钟，master下开始发时钟）   
		REG_I2C1_CLR |= I2C_CR_IFLG;	
	
    result = i2c_wait_state(status,I2C_TIMEOUT);
    return result;
}

/************************************************************************
 * function   : i2c_read_byte_wait_status
 * Description: i2c_read_byte_wait_status
 * input : byte
 * return: result, 0: error， other: OK  
 ************************************************************************/
static uint8_t i2c_read_byte_wait_status(uint8_t *byte, uint8_t status)
{
    uint8_t result = 0;
	
    result = i2c_wait_state(status,I2C_TIMEOUT);
    if(result)
    {
			*byte = i2c_read_byte();
			//清中断标志 ，数据读走后清标志（slave下 释放时钟，master下开始发时钟）		
			REG_I2C1_CLR |= I2C_CR_IFLG;		
    }
    return result;
}

/************************************************************************
 * function   : i2c_speed
 * Description: i2c speed config，FOSCL = FSCL = PCLK / (2M×(N+1)×10)；其中，FOSCL是I2C接口输出的SCL得频率。
 * input : 
		uint8_t speed：1M、400K、100K
 * return: rdata: none
 ************************************************************************/
static void i2c_speed(uint8_t speed)
{ 
	REG_I2C1_CCR=0;
	switch(speed)            
	{ 
		// 100Kbps
		case I2C_SPEED_STAD:          
			REG_I2C1_CCR|=(3<<4)|(3<<0); 	
			break;
		
		//400K
		case I2C_SPEED_FAST:       
			REG_I2C1_CCR|=(1<<4)|(3<<0);	 	
			break;
		
		// 1M
		case I2C_SPEED_HIGH:  
			REG_I2C1_CCR|=(2<<0);    		
			break;	

		default: 
			break;
	}
}

/************************************************************************
 * function   : i2c_master_init
 * Description: i2c pin init, config interruption
 * input : 
			uint8_t speed:  i2c SCL clk
 * return: none
 ************************************************************************/
void i2c_master_init( uint8_t speed )
{	
	REG_SCU_IOCTRLPROTECT = (uint32_t)0xA5A55A5A;       	// 关闭IO写保护
		
    REG_SCU_PFSEL = (REG_SCU_PFSEL&(~(0xF<<12)))|(0x4<<12); //PF3复用为I2C1_SCL
    REG_SCU_PESEL = (REG_SCU_PESEL&(~(0xF<<20)))|(0x8<<20); //PE5复用为I2C1_SDA

	REG_SCU_IOCTRLPROTECT = (uint32_t)0xFFFFFFFF;       	// 打开IO写保护 

	REG_SCU_PERICLKEN |= I2C1_CLKEN; 											// 开启I2C时钟 
	REG_SCU_PERIRESET |= I2C1_RESET;			 									// SCU复位控制寄存器 	
	REG_I2C1_SRST = 1;							        								// i2c 复位 
	
	i2c_speed(speed);                                   	// 设置I2C SCL /
}

/************************************************************************
 * function   : i2c_start
 * Description: i2c_start
 * input : 
 * return: status not 0 is ok 
 ************************************************************************/
static uint8_t i2c_start(void)
{
	uint8_t status = 0;

	REG_I2C1_CR |= I2C_CR_STA;
	status = i2c_wait_state(I2C_STA_START_BIT_SENT,I2C_TIMEOUT);

	return status;
}

/************************************************************************
 * function   : i2c_restart
 * Description: i2c_restart
 * input : 
 * return: status not 0 is ok 
 ************************************************************************/
static uint8_t i2c_restart(void)
{
	uint8_t status = 0;

	REG_I2C1_CR |= I2C_CR_STA;
	REG_I2C1_CLR |= I2C_CR_IFLG;				//清中断标志 ，master下开始发时钟
	status = i2c_wait_state(I2C_STA_RESTART_BIT_SENT,I2C_TIMEOUT);

	return status;
}

/************************************************************************
 * function   : i2c_stop
 * Description: i2c_stop
 * input : none
 * return:  none
 ************************************************************************/
static void i2c_stop(void)
{
	REG_I2C1_CR |= I2C_CR_STP;
}


/************************************************************************
 * function   : i2c_master_send
 * Description: i2c master send data by commond mode
 * input      : 
			          uint16_t slave_addr: slave device address 
			          uint8_t *buff      : data buff
			          uint32_t length    : data length
 * return     :  
                0: error， 1: OK  
 ************************************************************************/
uint8_t i2c_master_send(uint16_t slave_addr, uint8_t *buff, uint32_t length)
{
	uint8_t  status = 0;
	uint32_t i;

	status = i2c_start();                                   		//起始信号 
	if(status !=1)	goto __ERROR;	

#ifdef I2C_7BIT_ADDR
	
	status = i2c_write_byte_wait_status((slave_addr<<1)|I2C_WRITE, I2C_STA_TX_ADDR_W_ACK);  //设备地址+W标志 
	if(status !=1)	 goto __ERROR;	

#else		
	
	uint8_t addr1, addr2;
	addr1  = 0xF0;
	addr1 |= (uint8_t)((slave_addr >> 7) & 0x06);
	addr2  = (uint8_t)slave_addr;

	status = i2c_write_byte_wait_status(addr1|I2C_WRITE, I2C_STA_TX_ADDR_W_ACK);	
	if(status !=1)	goto __ERROR;	

	status = i2c_write_byte_wait_status(addr2, I2C_STA_SECOND_ADD_ACK);		
	if(status !=1)	goto __ERROR;	
	
#endif           

	for(i=0; i<length; i++)   					//传输要写数据                      
	{
		status = i2c_write_byte_wait_status(buff[i], I2C_STA_TX_DATA_ACK);  
		if(status==0)		goto __ERROR;	
		if(status==2) 	break;						//收到NACK
	}	

	i2c_stop();	
	
	return 1;	

__ERROR:
	i2c_stop();
	
	return 0;		
}

/************************************************************************
 * function   :  i2c_master_recv
 * Description:  i2c_master receive data
 * input      : 
			           uint16_t slave_addr: slave device address 
			           uint8_t       *buff: data buff
			           uint32_t     length: data length
 * return     :  
                 0: error， 1: OK  
 ************************************************************************/
uint8_t i2c_master_recv(uint16_t slave_addr, uint8_t *buff, uint32_t length)
{
	uint8_t  status = 0;
	uint32_t i;

	status = i2c_start();        //START  ，start 3us后，会自动发1个字节的时钟，
	if(status !=1)	goto __ERROR;	

#ifdef I2C_7BIT_ADDR	
	status = i2c_write_byte_wait_status((slave_addr<<1)|I2C_READ, I2C_STA_TX_ADDR_R_ACK); //设备地址+R标志
	if(status !=1)	goto __ERROR;		
#else			
	uint8_t addr1, addr2;
	
	addr1  = 0xF0;
	addr1 |= (uint8_t)((slave_addr >> 7) & 0x06);
	addr2  = (uint8_t)slave_addr;

	status = i2c_write_byte_wait_status(addr1|I2C_WRITE, I2C_STA_TX_ADDR_W_ACK);    //设备地址+W标志
	if(status !=1)	goto __ERROR;	

	status = i2c_write_byte_wait_status(addr2, I2C_STA_SECOND_ADD_ACK);    
	if(status !=1)	goto __ERROR;	

	status = i2c_restart();								     //RESTART
	if(status !=1)	goto __ERROR;	
	
	status = i2c_write_byte_wait_status(addr1|I2C_READ, I2C_STA_TX_ADDR_R_ACK); //设备地址+R标志 
	if(status !=1)	goto __ERROR;		
#endif	

	REG_I2C1_CR  |= I2C_CR_AAK;	
	REG_I2C1_CLR |= I2C_CR_IFLG;		//清中断标志 ，master下开始发时钟
	
	for(i=0;i<length;i++)
	{
#ifdef I2C_NACK			
		if(i==(length-1))						//最后一个字节回NAK
		{
			REG_I2C1_CLR |= (1<<2);   	//NAK  只有设置了该位才能设置NACK
			status = i2c_read_byte_wait_status(buff++, I2C_STA_RX_DATA_NAK);
			if(status !=1)	goto __ERROR;	
			break;
		}		
#endif	

		status = i2c_read_byte_wait_status(buff++, I2C_STA_RX_DATA_ACK);
		if(status !=1)	goto __ERROR;	
	}

	i2c_stop();

	return 1;	

__ERROR:
	i2c_stop();

	return 0;	
}

/************************************************************************
 * function   : i2c_master_send_mix
 * Description: i2c master send data by mix mode （针对需要单独发memory地址的设备，如I2C EEPROM设备）
 * input      : 
								uint16_t slave_addr: slave device address 
								uint32_t write_addr：write address
								uint8_t    addr_num：addre size（eg：1,2,3,4）
			          uint8_t        buff: data buff
			          uint32_t     length: data length
 * return     :  
                0: error， 1: OK  
 ************************************************************************/
uint8_t i2c_master_send_mix(uint16_t slave_addr, uint32_t* write_addr, uint8_t addr_num, uint8_t *buff, uint32_t length)
{
	uint8_t  status = 0;
	uint32_t i;
	uint8_t  *dest_addr = (uint8_t *)write_addr;
	uint8_t  addr1;

	status = i2c_start();                                   //起始信号 
	if(status !=1)	goto __ERROR;	

#ifdef I2C_7BIT_ADDR	
	
	addr1 = slave_addr<<1;	
	status = i2c_write_byte_wait_status(addr1|I2C_WRITE, I2C_STA_TX_ADDR_W_ACK); //设备地址+W标志 
	if(status !=1)	goto __ERROR;	
		
#else		
	
	uint8_t  addr2;
	
	addr1  = 0xF0;
	addr1 |= (uint8_t)((slave_addr >> 7) & 0x06);
	addr2  = (uint8_t)slave_addr;

	status = i2c_write_byte_wait_status(addr1|I2C_WRITE, I2C_STA_TX_ADDR_W_ACK); //设备地址+W标志 
	if(status !=1)	goto __ERROR;	
			
	status = i2c_write_byte_wait_status(addr2, I2C_STA_SECOND_ADD_ACK);				
	if(status !=1)	goto __ERROR;	
	
#endif

	for(i=0; i<addr_num; i++)       			//传输memory 地址                    
	{
		status = i2c_write_byte_wait_status(dest_addr[i], I2C_STA_TX_DATA_ACK);				
		if(status !=1)	goto __ERROR;	
	}	

	status = i2c_restart();								//RESTART
	if(status !=1)	goto __ERROR;	
	
	status = i2c_write_byte_wait_status(addr1|I2C_WRITE, I2C_STA_TX_ADDR_W_ACK); //设备地址+W标志 	
	if(status !=1)	goto __ERROR;	

#ifndef I2C_7BIT_ADDR	
	status = i2c_write_byte_wait_status(addr2, I2C_STA_SECOND_ADD_ACK);				
	if(status !=1)	goto __ERROR;	
#endif
	
	for(i=0; i<length; i++)   						//传输要写数据                      
	{
		status = i2c_write_byte_wait_status(buff[i], I2C_STA_TX_DATA_ACK);  
		if(status==0)	 goto __ERROR;	
		if(status==2)  break;								//收到NACK
	}	

	i2c_stop();	
	
	return 1;	

__ERROR:
	i2c_stop();
	
	return 0;			
}

/************************************************************************
 * function   : i2c_master_receive_mix
 * Description: i2c_master receive data （针对需要单独发memory地址的设备，如I2C EEPROM设备）
 * input 			: 
								uint16_t slave_addr: slave device address 
								uint32_t read_addr ：read address
								uint8_t addr_num   ：addre size（eg：0,1,2,3,4）
								uint8_t *buff      : data buff
								uint32_t length    : data length
 * return			:  
								 0: error，
								 1: OK  
 ************************************************************************/
uint8_t i2c_master_recv_mix(uint16_t slave_addr, uint32_t *read_addr, uint8_t addr_num, uint8_t *buff, uint32_t length)
{
	uint8_t  status = 0;
	uint32_t i;
	uint8_t  *dest_addr = (uint8_t *)read_addr;
	uint8_t  addr1;

	status = i2c_start();                                               //START
	if(status != 1)	goto __ERROR;

#ifdef I2C_7BIT_ADDR		
	addr1 = slave_addr<<1;	
	status = i2c_write_byte_wait_status(addr1|I2C_WRITE, I2C_STA_TX_ADDR_W_ACK); //设备地址+W标志 
	if(status != 1)	goto __ERROR;	
#else		
	uint8_t addr2;
	
	addr1  = 0xF0;
	addr1 |= (uint8_t)((slave_addr >> 7) & 0x06);
	addr2  = (uint8_t)slave_addr;

	status = i2c_write_byte_wait_status(addr1|I2C_WRITE, I2C_STA_TX_ADDR_W_ACK);    //设备地址+W标志
	if(status !=1)	goto __ERROR;	

	status = i2c_write_byte_wait_status(addr2, I2C_STA_SECOND_ADD_ACK);    
	if(status !=1)	goto __ERROR;		
#endif	
	
	for(i=0; i<addr_num; i++)        		//传输memory 地址                  
	{
		status = i2c_write_byte_wait_status(dest_addr[i], I2C_STA_TX_DATA_ACK);				
		if(status != 1)	goto __ERROR;
	}	

	status = i2c_restart();							//RESTART
	if(status != 1)	goto __ERROR;
	
	status = i2c_write_byte_wait_status(addr1|I2C_READ, I2C_STA_TX_ADDR_R_ACK); //设备地址+R标志 
	if(status != 1)	goto __ERROR;

	REG_I2C1_CR |= I2C_CR_AAK;	
	REG_I2C1_CLR |= I2C_CR_IFLG;					//清中断标志 ，master下开始发时钟
	
	for(i=0;i<length;i++)
	{
#ifdef I2C_NACK				
		if(i==(length-1))
		{
			REG_I2C1_CLR |= (1<<2);    			//NAK  只有设置了该位才能设置NACK
			status = i2c_read_byte_wait_status(buff++, I2C_STA_RX_DATA_NAK);
			if(status != 1)	goto __ERROR;
			break;
		}		
#endif
		status = i2c_read_byte_wait_status(buff++, I2C_STA_RX_DATA_ACK);
		if(status==0)		goto __ERROR;	
		if(status==2)   break;						//收到NACK
	}	

	i2c_stop();
	
	return 1;	

__ERROR:
	i2c_stop();
	
	return 0;				
}






