#include "SWM221.h"

#include "IOI2C_SLV.h"

static char TxBuff[128] = {0};
static uint32_t TxSize = 0;
static volatile uint32_t TxIndx = 0;

static char RxBuff[128] = {0};
static uint32_t RxSize = 128;			//最多能接收128个字节
static volatile uint32_t RxIndx = 0;

static volatile uint32_t BitTim = 0;

static volatile uint32_t IsBusy = 0;	//检测到Start置位，检测到Stop清零

static volatile uint32_t IsNACK = 0;	//发送数据时收到NACK


static uint32_t RxBuff2[128] = {0};		//接收到Stop时将RxBuff中数据搬运到这里，然后驱动可以继续用RxBuff接收数据位，用户可通过从RxBuff2中读走数据
static volatile uint32_t RxSize2 = 0;	//RxBuff2中有多少个数据可以被读出
static volatile uint32_t RxIndx2 = 0;

static void (*IOI2C_SLV_Func)(void);

static void IOI2C_SLV_SendByte(void);
static void IOI2C_SLV_ReadByte(void);

extern uint32_t IOI2C_SLV_EmptyCallback(char *TxBuff);

/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_SLV_Init()
* 功能说明: IOI2C_SLV初始化
* 输    入: 无
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void IOI2C_SLV_Init(void)
{
#define IOI2C_PORT	GPIOA
#define IOI2C_SCL	PIN2
#define IOI2C_SDA	PIN3
#define IOI2C_IRQ	GPIOA_IRQn
#define IOI2C_ISR	GPIOA_Handler
	
	GPIO_Init(IOI2C_PORT, IOI2C_SCL, 0, 1, 0, 1);
#define I2C_SCL_Value()		GPIO_GetBit(IOI2C_PORT, IOI2C_SCL)

	GPIO_Init(IOI2C_PORT, IOI2C_SDA, 0, 1, 0, 1);
#define I2C_SDA_OUTPUT()	IOI2C_PORT->DIR |= (1 << IOI2C_SDA);
#define I2C_SDA_LOW()		GPIO_ClrBit(IOI2C_PORT, IOI2C_SDA)
#define I2C_SDA_HIGH()		GPIO_SetBit(IOI2C_PORT, IOI2C_SDA)

#define I2C_SDA_INPUT()		IOI2C_PORT->DIR &=~(1 << IOI2C_SDA)
#define I2C_SDA_Value()		GPIO_GetBit(IOI2C_PORT, IOI2C_SDA)	
	
	
	EXTI_Init(IOI2C_PORT, IOI2C_SCL, EXTI_BOTH_EDGE);
	EXTI_Init(IOI2C_PORT, IOI2C_SDA, EXTI_BOTH_EDGE);
	
	NVIC_EnableIRQ(IOI2C_IRQ);
	
	EXTI_Open(IOI2C_PORT, IOI2C_SCL);
	EXTI_Open(IOI2C_PORT, IOI2C_SDA);
}

/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_SLV_RxNotEmpty()
* 功能说明: IOI2C从机接收缓冲区中是否有数据可取出
* 输    入: 无
* 输    出: uint32_t		0 没有可取出数据    >0 可取出的数据个数
* 注意事项: 无
******************************************************************************************************************************************/
uint32_t IOI2C_SLV_RxNotEmpty(void)
{
	return RxSize2;
}

/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_SLV_FetchByte()
* 功能说明: 从IOI2C从机接收缓冲区中取出一个字节
* 输    入: 无
* 输    出: char		取出的字节
* 注意事项: 无
******************************************************************************************************************************************/
char IOI2C_SLV_FetchByte(void)
{
	RxSize2 -= 1;
	
	return RxBuff2[RxIndx2++];
}

/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_SLV_FetchBytes()
* 功能说明: 从IOI2C从机接收缓冲区中取出n个字节
* 输    入: char buff[]		取出的数据存入此数组
*			uint32_t len		要取出的数据的个数，不得大于IOI2C_SLV_RxNotEmpty()返回值
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void IOI2C_SLV_FetchBytes(char buff[], uint32_t len)
{
	uint32_t i;
	
	for(i = 0; i < len; i++)
		buff[i] = RxBuff2[RxIndx2+i];
	RxIndx2 += len;
	RxSize2 -= len;
}


static void IOI2C_SLV_SendByte(void)
{
	if(IsNACK == 1)
	{
		I2C_SDA_INPUT();
		
		goto noinc;
	}
	
	if(TxSize == 0)
	{
		TxSize = IOI2C_SLV_EmptyCallback(TxBuff);
		
		TxIndx = 0;
	}
	
	if(BitTim == 0)	I2C_SDA_OUTPUT();	//发送上一个字符时会将SDA切成输入脚以接收ACK
	
	if(BitTim < 16)
	{
		if((BitTim % 2) == 0)
        {
			if(TxBuff[TxIndx] & (1 << (7 - BitTim/2)))
				I2C_SDA_HIGH();
			else
				I2C_SDA_LOW();
        }
	}
	else if(BitTim == 16)
	{
		I2C_SDA_INPUT();
	}
	else if(BitTim == 17)
	{
		if(I2C_SDA_Value())	//NACK
        {
            IsNACK = 1;
        }
        else
        {
            if(++TxIndx == TxSize)
            {
                TxIndx = TxSize - 1;
            }
        }
		
		BitTim = 0;
		goto noinc;
    }
	
	BitTim++;

noinc:
	;
}

static void IOI2C_SLV_ReadByte(void)
{
	if(BitTim == 0) I2C_SDA_INPUT();	//接收上一个字符时会将SDA切成输出脚以发送ACK
	
	if(BitTim < 16)
    {
        if((BitTim % 2) == 1)
        {
			if(I2C_SDA_Value() == 1)
				RxBuff[RxIndx] |=  (1 << (7 - BitTim/2));
			else
				RxBuff[RxIndx] &= ~(1 << (7 - BitTim/2));
        }
    }
    else if(BitTim == 16)
	{
		I2C_SDA_OUTPUT();
		
		if(++RxIndx < RxSize)
		{
			I2C_SDA_LOW();
		}
		else
		{
			I2C_SDA_HIGH();
		}
	}
	else if(BitTim == 17)
	{
		if((RxIndx == 1) && (RxBuff[0] & 0x01))
		{
			TxIndx = 0;
			
			IsNACK = 0;
			
			IOI2C_SLV_Func = IOI2C_SLV_SendByte;
		}
		
		BitTim = 0;
		goto noinc;
	}
	
	BitTim++;
	
noinc:
	;
}


/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_ISR()
* 功能说明: IOI2C SCL、SDA引脚双边沿中断服务函数
* 输    入: 无
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void IOI2C_ISR(void)
{		
	uint32_t i;
	
	if(EXTI_State(IOI2C_PORT, IOI2C_SDA))
	{
		EXTI_Clear(IOI2C_PORT, IOI2C_SDA);
		
		if(I2C_SCL_Value() == 1)			//只有在SCL高电平时SDA的跳变才是Start和Stop
		{
			if(I2C_SDA_Value() == 0)		//Start
			{
				RxIndx = 0;
				
				BitTim = 0;
				
				IsBusy = 1;
				
				IOI2C_SLV_Func = IOI2C_SLV_ReadByte;
			}
			else							//Stop
			{
				IsBusy = 0;
				
				for(i = 0; i < RxIndx-1; i++)
				{
					RxBuff2[i] = RxBuff[1+i];	//RxBuff[]中第一个字节是从机地址
				}
				RxSize2 = i;
				RxIndx2 = 0;
			}
		}
	}
	else if(EXTI_State(IOI2C_PORT, IOI2C_SCL))
	{
		EXTI_Clear(IOI2C_PORT, IOI2C_SCL);
		
		if(IsBusy) IOI2C_SLV_Func();
	}
}
