#include "SWM221.h"

#include "IOI2C_MST.h"

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

static char *RxBuff = 0;
static uint32_t RxSize = 0;
static volatile uint32_t RxIndx = 0;

static volatile uint32_t BitTim = 0;

static volatile uint32_t IsRead = 0;

static volatile uint32_t IsBusy = 0;


static void (*IOI2C_MST_Func)(void);

static void IOI2C_MST_Start(void);
static void IOI2C_MST_SendByte(void);
static void IOI2C_MST_ReadByte(void);
static void IOI2C_MST_Stop(void);

static void delayuS(uint32_t dly) { uint32_t i; for(i = 0; i < SystemCoreClock/1000000 * dly; i++) __NOP(); }

/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_MST_Init()
* 功能说明: IOI2C主机初始化
* 输    入: uint32_t baud		IOI2C主机波特率，即位传输速率，单位bps
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void IOI2C_MST_Init(uint32_t baud)
{
#define IOI2C_PORT	GPIOA
#define IOI2C_SCL	PIN2
#define IOI2C_SDA	PIN3
#define IOI2C_TMR	BTIMR0
#define IOI2C_ISR	BTIMR0_Handler
	
	GPIO_Init(IOI2C_PORT, IOI2C_SCL, 1, 0, 0, 0);
#define I2C_SCL_LOW()		{ GPIO_ClrBit(IOI2C_PORT, IOI2C_SCL); delayuS(1); }
#define I2C_SCL_HIGH()		{ GPIO_SetBit(IOI2C_PORT, IOI2C_SCL); delayuS(1); }

	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)

	I2C_SDA_HIGH();
	I2C_SCL_HIGH();
	
	TIMR_Init(IOI2C_TMR, TIMR_MODE_TIMER, 10, SystemCoreClock/10/(baud*2), 1);	//波特率为baud，则时钟翻转速率为baud*2
	
	NVIC_SetPriority(BTIMR0_IRQn, 3);	// 将I2C主机的中断优先级设置的比从机低，因为从机来不及处理的话会返回NAK
}

/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_MST_Send()
* 功能说明: IOI2C主机发送数据
* 输    入: char slvaddr	从机地址
*			char buff[]		要发送的数据
*			uint32_t size		要发送的数据的个数，最多128个
* 输    出: 无
* 注意事项: 只能在 IOI2C_MST_IsBusy() == 0 时发送
******************************************************************************************************************************************/
void IOI2C_MST_Send(char slvaddr, char buff[], uint32_t size)
{	
	uint32_t i;
	
	TxBuff[0] = (slvaddr << 1) | 0;
	for(i = 1; i <= size; i++)
		TxBuff[i] = buff[i-1];
	TxSize = i;
	TxIndx = 0;
	
	BitTim = 0;
	
	IsRead = 0;
	
	IsBusy = 1;
	
	IOI2C_MST_Func = IOI2C_MST_Start;
	TIMR_Start(IOI2C_TMR);
}

/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_MST_Read()
* 功能说明: IOI2C主机读取数据
* 输    入: char slvaddr	从机地址
*			char buff[]		读取到的数据存入此数组
*			uint32_t size		要读取的数据的个数，最多128个
* 输    出: 无
* 注意事项: 只能在 IOI2C_MST_IsBusy() == 0 时读取，当再次 IOI2C_MST_IsBusy() == 0 时读取到的数据已经存储在buff[]中
******************************************************************************************************************************************/
void IOI2C_MST_Read(char slvaddr, char buff[], uint32_t size)
{	
	uint32_t i;
	
	TxBuff[0] = (slvaddr << 1) | 1;
	for(i = 1; i <= size; i++)
		TxBuff[i] = buff[i-1];
	TxSize = i;
	TxIndx = 0;
	
	RxBuff = buff;
	for(i = 0; i < size; i++)
		RxBuff[i] = 0;
	RxSize = i;
	RxIndx = 0;
	
	BitTim = 0;
	
	IsRead = 1;
	
	IsBusy = 1;
	
	IOI2C_MST_Func = IOI2C_MST_Start;
	TIMR_Start(IOI2C_TMR);
}

/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_MST_IsBusy()
* 功能说明: IOI2C主机否正在发送/接收
* 输    入: 无
* 输    出: uint32_t		1 IOI2C正忙于发送/接收    0 IOI2C当前空闲，可以发起新的发送
* 注意事项: 无
******************************************************************************************************************************************/
uint32_t IOI2C_MST_IsBusy(void)
{
	return IsBusy;
}

static void IOI2C_MST_Start(void)
{
	I2C_SDA_LOW();
	
	IOI2C_MST_Func = IOI2C_MST_SendByte;
}

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

noinc:
	;
}

static void IOI2C_MST_ReadByte(void)
{
    if(BitTim < 16)
    {
        if((BitTim % 2) == 0)
        {
			I2C_SCL_LOW();
			
			if(BitTim == 0) I2C_SDA_INPUT();	//接收上一个字符时会将SDA切成输出脚以发送ACK
        }
        else
        {
			I2C_SCL_HIGH();
			
			RxBuff[RxIndx] |=  I2C_SDA_Value() << (7 - BitTim/2);				
        }
    }
    else if(BitTim == 16)
	{
		I2C_SCL_LOW();
		
		I2C_SDA_OUTPUT();
		
		if(++RxIndx < RxSize)
		{
			I2C_SDA_LOW();
		}
		else
		{
			I2C_SDA_HIGH();
		}
	}
	else if(BitTim == 17)
	{
		I2C_SCL_HIGH();
	}
	else
	{
		if(RxIndx == RxSize)
		{
			IOI2C_MST_Func = IOI2C_MST_Stop;
		}
		
		BitTim = 0;
		goto noinc;
	}
	
	BitTim++;

noinc:
	;
}

static void IOI2C_MST_Stop(void)
{
    if(BitTim == 0)
    {
		I2C_SCL_LOW();
		
		I2C_SDA_LOW();
		I2C_SDA_OUTPUT();	//发送上一个字符时会将SDA切成输入脚以接收ACK
    }
    else
    {
		I2C_SCL_HIGH();
		
		I2C_SDA_HIGH();
		
		IsBusy = 0;
		
		TIMR_Stop(IOI2C_TMR);
    }
	
	BitTim++;
}

/****************************************************************************************************************************************** 
* 函数名称:	IOI2C_ISR()
* 功能说明: TIMER中断服务程序，以2倍SCL时钟频率执行
* 输    入: 无
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void IOI2C_ISR(void)
{	
	TIMR_INTClr(IOI2C_TMR);
	
	if(IsBusy) IOI2C_MST_Func();
}
