#include "bsp_i2c.h"


/*********************************************************************************
* Function    : I2C_WaitOnFlagUntilTimeout
* Description : This function handles I2C Communication Timeout.
* Input       : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
* Input       : Flag: Flag specifies the I2C flag to check.
* Input       : Status: The new Flag status (SET or RESET).
* Input       : Timeout: Timeout duration.
* Output      : None
**********************************************************************************/
static uint8_t I2C_WaitOnFlagUntilTimeout(I2C_TypeDef* I2Cx, uint32_t Flag, FlagStatus Status, uint32_t Timeout)
{
    __IO uint32_t lu32_Timeout;
    /* have no timeout */
    if (Timeout == 0) 
    {
        while(_I2C_GET_FLAG(I2Cx, Flag)==Status);
    }
    else 
    {
        lu32_Timeout = Timeout * 256;
            
        while(_I2C_GET_FLAG(I2Cx, Flag)==Status)
        {
            if (lu32_Timeout-- == 0) 
            {
                return I2C_ERROR;
            }
        }
    }
    return I2C_OK;
}

/************************************************************************
 * function   : I2C_Master_Request_Write
 * Description: I2C Write Access Request
 * input      : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
 *              DevAddress: Device address
 *              Timeout: Timeout value
 ************************************************************************/
static uint8_t I2C_Master_Request_Write(I2C_TypeDef* I2Cx, uint8_t DevAddress, uint32_t Timeout)
{
    /* Generate Start */
	I2C_GenerateSTART(I2Cx,ENABLE);

    /* Clear MTF, To Prevent Errors */
	I2C_ClearFlag(I2Cx,I2C_SR_MTF);

    /* Send Device Address */
	I2C_Send7bitAddress(I2Cx,DevAddress,I2C_Direction_Transmitter);

    /* Wait for transmission End*/
    if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK) return I2C_ERROR;
    /* Clear MTF */
	I2C_ClearFlag(I2Cx,I2C_SR_MTF);

    /* Get NACK */
    if (I2C_GetFlagStatus(I2Cx, I2C_SR_RACK))
    {
        /* Generate Stop */
		I2C_GenerateSTOP(I2Cx,ENABLE);
        
        /* Wait for the bus to idle */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK) return I2C_ERROR;
        
        return I2C_ERROR;
    }
    /* Get ACK */
    else 
    {
        return I2C_OK;
    }
}

/************************************************************************
 * function   : I2C_Master_Request_Read
 * Description: I2C Read Access Request
 * input      : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
 *              DevAddress: Device address
 *              Timeout: Timeout value
 ************************************************************************/
static uint8_t I2C_Master_Request_Read(I2C_TypeDef* I2Cx, uint8_t DevAddress, uint32_t Timeout)
{
    /* Generate Start */
    I2C_GenerateSTART(I2Cx,ENABLE);

    /* Clear MTF, To Prevent Errors */
    I2C_ClearFlag(I2Cx,I2C_SR_MTF);
    
    /* Send Device Address */
	I2C_Send7bitAddress(I2Cx,DevAddress,I2C_Direction_Receiver);
    
    /* Wait for transmission End */
    if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK) return I2C_ERROR;
    /* Clear MTF */
    I2C_ClearFlag(I2Cx,I2C_SR_MTF);
    
    /* Get NACK */
    if (I2C_GetFlagStatus(I2Cx, I2C_SR_RACK))
    {
        /* Generate Stop */
        I2C_GenerateSTOP(I2Cx,ENABLE);
        
        /* Wait for the bus to idle */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK) return I2C_ERROR;
        
        return I2C_ERROR;
    }
    /* Get ACK */
    else 
    {
        return I2C_OK;
    }
}

/************************************************************************
 * function   : I2C_Check_Device_Ready
 * Description: Check Device Ready
 * input      : I2Cx: where x can be 1 or 2 to select the I2C peripheral. 
 *              DevAddress: Device address
 *              Timeout: Timeout value
 ************************************************************************/
static uint8_t I2C_Check_Device_Ready(I2C_TypeDef* I2Cx, uint8_t DevAddress, uint32_t Timeout)
{
    /* Bus Busy */
    if (I2C_GetFlagStatus(I2Cx, I2C_SR_BUS_BUSY)) 
        return I2C_ERROR;

    /* Generate Start */
    I2C_GenerateSTART(I2Cx,ENABLE);
    
    /* Send Device Address */
    I2Cx->DR = DevAddress;
    
    /* Wait for transmission End */
    if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK) return I2C_ERROR;
    /* Clear MTF */
    I2C_ClearFlag(I2Cx,I2C_SR_MTF);
    
    /* Get NACK */
    if (I2C_GetFlagStatus(I2Cx, I2C_SR_RACK))
    {
        /* Generate Stop */
		I2C_GenerateSTOP(I2Cx,ENABLE);
        
        /* Wait for the bus to idle */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK) return I2C_ERROR;
        
        return I2C_ERROR;
    }
    /* Get ACK */
    else 
    {
        /* Generate Stop */
        I2C_GenerateSTOP(I2Cx,ENABLE);
        
        /* Wait for the bus to idle */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK) return I2C_ERROR;
        
        return I2C_OK;
    }
}
/************************************************************************
 * function   : I2C_Mem_Write
 * Description: Write an amount of data in blocking mode to a specific memory address
 * input      : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
 *              DevAddress : Target device address
 *              MemAddress : MemAddress Internal memory address
 *              MemAddSize : MemAddSize Size of internal memory address
 *              pData      : Pointer to data buffer
 *              Size       : Amount of data to be sent
 *              Timeout    : Timeout value
 ************************************************************************/
uint8_t I2C_Mem_Write(I2C_TypeDef* I2Cx, uint8_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint32_t Size, uint32_t Timeout)
{
    uint32_t i;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
   
    /* Bus Busy */
    if (I2C_GetFlagStatus(I2Cx, I2C_SR_BUS_BUSY)) 
        return I2C_BUSY;
    
    /* Send Write Access Request */
    if (I2C_Master_Request_Write(I2Cx, DevAddress,0) == I2C_OK)
    {
        /* If Memory address size is 8Bit */
        if (MemAddSize == I2C_MEMADD_SIZE_8BIT)
        {
            /* Send Memory Address */
			I2C_SendData(I2Cx,I2C_MEM_ADD_LSB(MemAddress));
        }
        /* If Memory address size is 16Bit */ 
        else
        {
            /* Send Memory Address MSB*/
            I2C_SendData(I2Cx,I2C_MEM_ADD_MSB(MemAddress));
            /* Wait for transmission End*/
            if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
            /* Clear MTF */
            I2C_ClearFlag(I2Cx,I2C_SR_MTF);
            /* Send Memory Address LSB*/
			I2C_SendData(I2Cx,I2C_MEM_ADD_LSB(MemAddress));
        }
        
        /* Wait for transmission End*/
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
        /* Clear MTF */
        I2C_ClearFlag(I2Cx,I2C_SR_MTF);
        
        /* Get NACK */
        if (I2C_GetFlagStatus(I2Cx, I2C_SR_RACK))
        {
            /* Generate Stop */
			I2C_GenerateSTOP(I2Cx,ENABLE);
            
            /* Wait for the bus to idle */
            if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK)  return I2C_ERROR;
            
            return I2C_ERROR;
        }
        /* Get ACK */
        else 
        {
            for (i = 0; i < Size; i++)
            {
                /* Wait TXE Flag */
                if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_TXE, RESET, Timeout)!= I2C_OK)  return I2C_ERROR;
                
                /* Send Data */
				I2C_SendData(I2Cx,pData[i]);
                
                /* Wait for transmission End*/
                 if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
                /* Clear MTF */
                I2C_ClearFlag(I2Cx,I2C_SR_MTF);
                
                /* Get NACK */
                if (I2C_GetFlagStatus(I2Cx, I2C_SR_RACK))
                {
                    /* Generate Stop */
                    I2C_GenerateSTOP(I2Cx,ENABLE);
                    
                    /* Wait for the bus to idle */
                    if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK)  return I2C_ERROR;
                    return I2C_ERROR;
                }
            }
            
            /* Generate Stop */
            I2C_GenerateSTOP(I2Cx,ENABLE);
            
            /* Wait for the bus to idle */
            if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK)  return I2C_ERROR;
            /* Check Device Ready */
            while(I2C_Check_Device_Ready(I2Cx, DevAddress, Timeout) != I2C_OK);
        }
    }
    else
    {
        return I2C_ERROR;
    }

    return I2C_OK;
}

/************************************************************************
 * function   : I2C_Mem_Read
 * Description: Read an amount of data in blocking mode to a specific memory address
 * input      : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
 *              DevAddress : Target device address
 *              MemAddress : MemAddress Internal memory address
 *              MemAddSize : MemAddSize Size of internal memory address
 *              pData      : Pointer to data buffer
 *              Size       : Amount of data to be sent
 *              Timeout    : Timeout value
 ************************************************************************/
uint8_t I2C_Mem_Read(I2C_TypeDef* I2Cx, uint8_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
    uint32_t i;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
   
    /* Bus Busy */
    if (READ_BIT(I2Cx->SR, I2C_SR_BUS_BUSY)) 
        return I2C_ERROR;
    
    /* Send Write Access Request */
    if (I2C_Master_Request_Write(I2Cx, DevAddress,0) == I2C_OK)
    {
        /* If Memory address size is 8Bit */
        if (MemAddSize == I2C_MEMADD_SIZE_8BIT)
        {
            /* Send Memory Address */
            I2Cx->DR = I2C_MEM_ADD_LSB(MemAddress);
        }
        /* If Memory address size is 16Bit */ 
        else
        {
            /* Send Memory Address MSB*/
            I2Cx->DR = I2C_MEM_ADD_MSB(MemAddress);
            /* Wait for transmission End*/
            if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
            /* Clear MTF */
            I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_MTF);
            /* Send Memory Address LSB*/
            I2Cx->DR = I2C_MEM_ADD_LSB(MemAddress);
        }
        
        /* Wait for transmission End*/
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
        /* Clear MTF */
        I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_MTF);
        
        /* Get NACK */
        if (READ_BIT(I2Cx->SR, I2C_SR_RACK))
        {
            /* Generate Stop */
            SET_BIT(I2Cx->CR, I2C_CR_STOP);
            
            /* Wait for the bus to idle */
            if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK)  return I2C_ERROR;
            
            return I2C_ERROR;
        }
        /* Get ACK */
        else
        {
            /* Send Write Read Request */
            if (I2C_Master_Request_Read(I2Cx, DevAddress, Timeout) == I2C_OK)
            {
                /* Wait Master Transition receiving state */
                if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_TX_RX_FLAG, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
                /* Clear TX_RX_FLAG */
                I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_TX_RX_FLAG);

                /* Generate ACK */
                CLEAR_BIT(I2Cx->CR, I2C_CR_TACK);

                for (i = 0; i <Size - 1; i++)
                {
                    /* Wait RXNE Flag */
                    if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_RXNE, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
                    /* Read Data */
                    pData[i] = I2Cx->DR;
                    /* Wait for transmission End*/
                    if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
                    /* Clear MTF */
                    I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_MTF);
                }

                /* Prepare for Generate NACK */
                SET_BIT(I2Cx->CR, I2C_CR_TACK);
                /* Prepare for Generate STOP */
                SET_BIT(I2Cx->CR, I2C_CR_STOP);

                /* Wait RXNE Flag */
                if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_RXNE, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
                /* Read Data */
                pData[i] = I2Cx->DR;
                /* Wait for transmission End*/
                if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
                /* Clear MTF */
                I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_MTF);

                /* Wait for the bus to idle */
                if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK)  return I2C_ERROR;
                /* Generate ACK */
                CLEAR_BIT(I2Cx->CR, I2C_CR_TACK);
            }
            else 
            {
				/* Get NACK */
				return I2C_ERROR;
            }
        }
    }
    else
    {
		return I2C_ERROR;
    }

    return I2C_OK;
}


/************************************************************************
 * function   : I2C_Master_Transmit
 * Description: Transmits in master mode an amount of data in blocking mode.
 * input      : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
 *              DevAddress : Target device address
 *              pData      : Pointer to data buffer
 *              Size       : Amount of data to be sent
 *              Timeout    : Timeout value
************************************************************************/
uint8_t I2C_Master_Transmit(I2C_TypeDef* I2Cx, uint16_t DevAddress, uint8_t *pData, uint32_t Size, uint32_t Timeout)
{
    uint32_t i;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
       
    /* Send Write Access Request */
    if (I2C_Master_Request_Write(I2Cx, DevAddress, Timeout) == I2C_OK)
    {
        for (i = 0; i < Size; i++)
        {
            /* Wait TXE Flag */
            if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_TXE, RESET, Timeout) != I2C_OK) return I2C_ERROR;
            
            /* Send Data */
			I2C_SendData(I2Cx,pData[i]);
            
            /* Wait for transmission End*/
            if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK) return I2C_ERROR;
            
            /* Clear MTF */
            I2C_ClearFlag(I2Cx,I2C_SR_MTF);
            /* Get NACK */
            if (I2C_GetFlagStatus(I2Cx, I2C_SR_RACK))
            {
                /* Generate Stop */
				I2C_GenerateSTOP(I2Cx,ENABLE);
                
                /* Wait for the bus to idle */
                if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK) return I2C_ERROR;
                
                return I2C_ERROR;
            }
        }
        
        /* Generate Stop */
        I2C_GenerateSTOP(I2Cx,ENABLE);
        
        /* Wait for the bus to idle */
         if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK) return I2C_ERROR;
    }
    else
    {
        return I2C_ERROR;
    }

    return I2C_OK;
}

/************************************************************************
 * function   : I2C_Master_Receive
 * Description: Transmits in master mode an amount of data in blocking mode.
 * input      : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
 *              DevAddress : Target device address
 *              pData      : Pointer to data buffer
 *              Size       : Amount of data to be Receive
 *              Timeout    : Timeout value
 ************************************************************************/
uint8_t I2C_Master_Receive(I2C_TypeDef* I2Cx, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
    uint32_t i;
    
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
       
    /* Send Read Access Request */
    if (I2C_Master_Request_Read(I2Cx, DevAddress, Timeout) == I2C_OK)
    {
        /* Wait Master Transition receiving state */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_TX_RX_FLAG, RESET, Timeout) != I2C_OK) return I2C_ERROR;
        /* Clear TX_RX_FLAG */
		I2C_ClearFlag(I2Cx,I2C_SR_TX_RX_FLAG);
        /* Generate ACK */
        CLEAR_BIT(I2Cx->CR, I2C_CR_TACK);

        for (i = 0; i < Size - 1; i++)
        {
            /* Wait RXNE Flag */
            if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_RXNE, RESET, Timeout) != I2C_OK) return I2C_ERROR;
            /* Read Data */
            pData[i] = I2C_ReceiveData(I2Cx);
            /* Wait for transmission End*/
            if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK) return I2C_ERROR;
            /* Clear MTF */
            I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_MTF);
        }

        /* Prepare for Generate NACK */
        SET_BIT(I2Cx->CR, I2C_CR_TACK);
        /* Prepare for Generate STOP */
        SET_BIT(I2Cx->CR, I2C_CR_STOP);

        /* Wait RXNE Flag */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_RXNE, RESET, Timeout) != I2C_OK) return I2C_ERROR;
        /* Read Data */
        pData[i] = I2C_ReceiveData(I2Cx);
        /* Wait for transmission End*/
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK) return I2C_ERROR;
        /* Clear MTF */
        I2C_ClearFlag(I2Cx,I2C_SR_MTF);
        
        /* Wait for the bus to idle */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_BUS_BUSY, SET, Timeout) != I2C_OK) return I2C_ERROR;

        /* Generate ACK */
        CLEAR_BIT(I2Cx->CR, I2C_CR_TACK);
    }
    else
    {
        return I2C_ERROR;
    }

    return I2C_OK;
}

/************************************************************************
 * function   : I2C_Slave_Transmit
 * Description: Transmits in Slave mode an amount of data in blocking mode.
 * input      : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
 *              pData      : Pointer to data buffer
 *              Size       : Amount of data to be sent
 *              Timeout    : Timeout value
 ************************************************************************/
uint8_t I2C_Slave_Transmit(I2C_TypeDef* I2Cx, uint8_t *pData, uint32_t Size, uint32_t Timeout)
{
    uint32_t i = 0;    
 
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
 
    
    /* Clear RX_ADDR1 Flag */ 
    I2C_ClearFlag(I2Cx,I2C_SR_RX_ADDR1);	  
    /* Match the Address 1 */
    if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_RX_ADDR1, RESET, Timeout) != I2C_OK) return I2C_ERROR;
    /* Clear RX_ADDR1 Flag */
	I2C_ClearFlag(I2Cx,I2C_SR_RX_ADDR1);

    /* Slave Transmit */
    if (I2C_GetFlagStatus(I2Cx, I2C_SR_SRW))
    {
        /* BUS BUSY */
        while(I2C_GetFlagStatus(I2Cx, I2C_SR_BUS_BUSY))
        {
            if (I2C_GetFlagStatus(I2Cx, I2C_SR_MTF)) 
            {
                /* Clear MTF */
				I2C_ClearFlag(I2Cx,I2C_SR_MTF);				
            }

            if (I2C_GetFlagStatus(I2Cx, I2C_SR_TXE))
            {
                if (i < Size || Size == 0) 
                {
					I2C_SendData(I2Cx,pData[i]);
					i++;
                }
            }
        }
		//Clear Flags
        I2Cx->SR = READ_REG(I2Cx->SR);
    }
    else
    {
        return I2C_ERROR;
    }
   
    return I2C_OK;
}

/************************************************************************
 * function   : I2C_Slave_Receive
 * Description: Receive in Slave mode an amount of data in blocking mode.
 * input      : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
 *              pData : Pointer to data buffer
 *              Size  : Amount of data to be sent
 *              Timeout    : Timeout value
 * retval     : I2C_OK or I2C_ERROR
 ************************************************************************/
uint8_t I2C_Slave_Receive(I2C_TypeDef* I2Cx, uint8_t *pData, uint32_t Size, uint32_t Timeout)
{
	uint32_t Rx_Count =0;
     
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));


 
    /* Match the Address 1 */
    if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_RX_ADDR1, RESET, Timeout) != I2C_OK) return I2C_ERROR;
    /* Clear RX_ADDR1 Flag */
	I2C_ClearFlag(I2Cx,I2C_SR_RX_ADDR1);
    
    /* Slave Receive */
    if (!I2C_GetFlagStatus(I2Cx, I2C_SR_SRW))
    {
        /* Wait for transmission End*/
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
        /* Clear MTF */
		I2C_ClearFlag(I2Cx,I2C_SR_MTF);

        /* BUS BUSY */
        while(I2C_GetFlagStatus(I2Cx, I2C_SR_BUS_BUSY))
        {
            /* Receive Data */
            if (I2C_GetFlagStatus(I2Cx, I2C_SR_RXNE))
            {
                pData[Rx_Count++] = I2C_ReceiveData(I2Cx);
              
                /* Wait for transmission End*/
                if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_SR_MTF, RESET, Timeout) != I2C_OK)  return I2C_ERROR;
                /* Clear MTF */
                I2C_ClearFlag(I2Cx,I2C_SR_MTF);

                if (Size != 0) 
                {
                    if (Rx_Count >= Size) 
                    {
                        break;
                    }
                }
            }
        }       
        /* Clear ACK */
        CLEAR_BIT(I2Cx->CR, I2C_CR_TACK);
        
        I2Cx->SR = READ_REG(I2Cx->SR);
    }
    /* Slave Transmit */
    else 
    {
       return I2C_ERROR;
    }

    return I2C_OK;
}

/******************************************************************************
*@brief : send an amount of data in Master mode using IT mode.
*         
*@param : hi2c: a pointer of I2C_HandleTypeDef structure which contains 
*         the configuration information for the specified I2C.
*@param : pData: Pointer to a buffer to send data.
*@param : Size: Total size of data to send .
*@return: HAL_StatusTypeDef
******************************************************************************/
uint8_t I2C_Master_Transmit_IT(I2C_TypeDef* I2Cx, uint16_t DevAddress, I2C_Handler_t* hi2c, uint8_t *pData, uint16_t Size)
{
    __IO uint32_t count = 0U;
    
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    
    /* Tx machine is running */
    if (hi2c->state != I2C_STATE_IDLE)
        return I2C_ERROR;
    
    /* Set I2C machine is sending */
    hi2c->state = I2C_STATE_TX_DATA;

    hi2c->Tx_Buffer = pData;
    hi2c->Tx_Size   = Size;
    hi2c->Tx_Count = 0;
    
    /* Clear MTF, To Prevent Errors */
    I2C_ClearFlag(I2Cx, I2C_SR_MTF);    
    
    I2C_GenerateSTART(I2Cx, ENABLE);

    I2C_ITConfig(I2Cx, I2C_CR_TXE_INT_EN | I2C_CR_MTF_INT_EN | I2C_CR_STOPF_INTEN, ENABLE);
    
    /* Send Device Address */
    I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Transmitter);
    
    
    return I2C_OK;
}

/******************************************************************************
*@brief : receivd an amount of data in Master mode using IT mode.
*         
*@param : hi2c: a pointer of I2C_HandleTypeDef structure which contains 
*         the configuration information for the specified I2C.
*@param : pData: Pointer to a buffer to store data.
*@param : Size: Total size of data to receive .
*@return: HAL_StatusTypeDef
******************************************************************************/
uint8_t I2C_Master_Receive_IT(I2C_TypeDef* I2Cx, uint16_t DevAddress, I2C_Handler_t* hi2c, uint8_t *pData, uint16_t Size)
{
    uint32_t i;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    
    hi2c->Rx_Buffer = pData;
    hi2c->Rx_Size = Size;
    hi2c->Rx_Count = 0; 
    
    hi2c->state = I2C_STATE_REQ_ADDR;
        
    /* Clear MTF, To Prevent Errors */
    I2C_ClearFlag(I2Cx, I2C_SR_MTF); 
    
    /* Generate Start */
    I2C_GenerateSTART(I2Cx, ENABLE);

    I2C_ITConfig(I2Cx, I2C_CR_RXNE_INT_EN | I2C_CR_MTF_INT_EN | I2C_CR_STOPF_INTEN, ENABLE);
    
    /* Send Device Address */
    I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Receiver);
    
    return I2C_OK;
}

/************************************************************************
 * function   : I2C_Slave_Transmit_IT
 * Description: Transmit in slave mode an amount of data in non-blocking mode with Interrupt
 * input      : hi2c : pointer to a I2C_HandleTypeDef structure that contains
 *                     the configuration information for I2C module
 *              pData      : Pointer to data buffer
 *              Size       : Amount of data to be sent
 * return     : HAL_StatusTypeDef
 ************************************************************************/
uint8_t I2C_Slave_Transmit_IT(I2C_TypeDef* I2Cx, I2C_Handler_t* hi2c, uint8_t *pData, uint32_t Size)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    /* Slave machine is running */
    if (hi2c->state != I2C_STATE_IDLE)
        return I2C_ERROR;

    /* Set Slave machine is sending */
    hi2c->state = I2C_STATE_TX_DATA;

    hi2c->Tx_Buffer = pData;
    hi2c->Tx_Size   = Size;
    hi2c->Tx_Count = 0;

    CLEAR_BIT(I2Cx->CR, I2C_CR_TXE_SEL);

    I2C_SendData(I2Cx, hi2c->Tx_Buffer[0]);

    hi2c->Tx_Count++;

    /* Clear RX ADDR1 Flag */
	I2C_ClearFlag(I2Cx,I2C_SR_RX_ADDR1);

    /* RX ADDR1, RXNE, STOPF Interrupt Enable */
    I2C_ITConfig(I2Cx, I2C_CR_RX_ADDR1_INT_EN | I2C_CR_TXE_INT_EN | I2C_CR_STOPF_INTEN, ENABLE);
    
    return I2C_OK;
}

/************************************************************************
 * function   : I2C_Slave_Receive_IT
 * Description: Receive in slave mode an amount of data in non-blocking mode with Interrupt
 * input      : hi2c : pointer to a I2C_HandleTypeDef structure that contains
 *                     the configuration information for I2C module
 *              pData      : Pointer to data buffer
 *              Size       : Amount of data to be sent
 ************************************************************************/
uint8_t I2C_Slave_Receive_IT(I2C_TypeDef* I2Cx, I2C_Handler_t* hi2c, uint8_t *pData, uint32_t Size)
{	
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    /* Rx machine is running */
    if (hi2c->state != I2C_STATE_IDLE)
        return I2C_ERROR;

    /* Set Slave machine is receiving */
    hi2c->state = I2C_STATE_RX_DATA;
    
    hi2c->Rx_Buffer = pData;
    hi2c->Rx_Size   = Size;
    hi2c->Rx_Count = 0;
    
    /* Clear RX ADDR1 Flag */
	I2C_ClearFlag(I2Cx,I2C_SR_RX_ADDR1);
    
    /* RX ADDR1, RXNE, STOPF Interrupt Enable */
    I2C_ITConfig(I2Cx, I2C_CR_RX_ADDR1_INT_EN | I2C_CR_RXNE_INT_EN | I2C_CR_STOPF_INTEN, ENABLE);
    
    return I2C_OK;
}

/******************************************************************************
*@brief  : This function handles I2C interrupt request.
*@param  : I2Cx: where x can be 1 or 2 to select the I2C peripheral.
*@param  : hi2c : Slave Communication Structure
*@return : None
******************************************************************************/
__weak void I2C_IRQHandler(I2C_TypeDef *I2Cx, I2C_Handler_t* hi2c)
{
    if(!READ_BIT(I2Cx->CR, I2C_CR_MASTER))
    {
        /* Slave ADDR1 Interrupt */
        if (READ_BIT(I2Cx->SR, I2C_SR_RX_ADDR1))// && READ_BIT(I2Cx->CR, I2C_CR_RX_ADDR1_INT_EN))
        {
            /* Clear ADDR1 Interrupt Flag */
            I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_RX_ADDR1);
            
            CLEAR_BIT(I2Cx->CR, I2C_CR_RX_ADDR1_INT_EN );            

            /* Slave Transmit */
            if (READ_BIT(I2Cx->SR, I2C_SR_SRW))
            {
                hi2c->state = I2C_STATE_TX_DATA;
            }
            else
            {
                hi2c->state = I2C_STATE_RX_DATA;
            }
        }
        
        if(hi2c->state == I2C_STATE_TX_DATA)
        {
            if (READ_BIT(I2Cx->SR, I2C_SR_TXE))
            {
                if(hi2c->Tx_Count == hi2c->Tx_Size)
                {
                    ;
                }
                else
                {
                    I2Cx->DR = hi2c->Tx_Buffer[hi2c->Tx_Count++];
                }
            }
        }
        else if(hi2c->state == I2C_STATE_RX_DATA)
        {
            if (READ_BIT(I2Cx->SR, I2C_SR_RXNE))
            {
                hi2c->Rx_Buffer[hi2c->Rx_Count++] = I2Cx->DR;
            }
        }
  
    }
    else
    {   
        if(hi2c->state == I2C_STATE_TX_DATA)
        {
            if(READ_BIT(I2Cx->SR, I2C_SR_MTF))
            {
                //clear MTF
                I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_MTF);
                
                if (READ_BIT(I2Cx->SR, I2C_SR_RACK))
                {
                    /* Generate Stop */
                    SET_BIT(I2Cx->CR, I2C_CR_STOP);
                }
                
            }
            
            if (READ_BIT(I2Cx->SR, I2C_SR_TXE))
            {
                I2Cx->DR = hi2c->Tx_Buffer[hi2c->Tx_Count++];
                if(hi2c->Tx_Count > hi2c->Tx_Size)
                {
                    SET_BIT(I2Cx->CR, I2C_CR_STOP);
                }
            }
        }

        if((hi2c->state == I2C_STATE_RX_DATA) || (hi2c->state == I2C_STATE_REQ_ADDR))
        {
            if (READ_BIT(I2Cx->SR, I2C_SR_RXNE))
            {
                hi2c->Rx_Buffer[hi2c->Rx_Count++] = I2Cx->DR;
                
                if(hi2c->Rx_Size == hi2c->Rx_Count)
                {
                    /* Prepare for Generate NACK */
                    SET_BIT(I2Cx->CR, I2C_CR_TACK);
                    /* Prepare for Generate STOP */
                    SET_BIT(I2Cx->CR, I2C_CR_STOP); 
                }
            }
            
            if(READ_BIT(I2Cx->SR, I2C_SR_MTF))
            {
                //clear MTF
                I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_MTF);
                
                if(hi2c->state == I2C_STATE_REQ_ADDR)
                {
                    hi2c->state = I2C_STATE_RX_DATA;
                    I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_TX_RX_FLAG);
                    
                }
            }
        }
    }
    
    /* STOP Flag Interrupt */
    if (READ_BIT(I2Cx->SR, I2C_SR_STOPF))
    {
        /* Clear STOPF Interrupt Flag */
        I2Cx->SR = READ_BIT(I2Cx->SR, I2C_SR_STOPF);
        
        /* Clear STOPF */
        CLEAR_BIT(I2Cx->CR, I2C_CR_STOPF_INTEN);
        
        if(hi2c->state == I2C_STATE_RX_DATA)
        {
            if(READ_BIT(I2Cx->CR, I2C_CR_MASTER))
            {
                CLEAR_BIT(I2Cx->CR, I2C_CR_TACK);
                CLEAR_BIT(I2Cx->CR, I2C_CR_STOP); 
            }
        }
        
        if (hi2c->I2C_STOPF_Callback != NULL) 
        {
            hi2c->I2C_STOPF_Callback();
        }
        
        hi2c->state = I2C_STATE_IDLE;
        
        CLEAR_BIT(I2Cx->CR, I2C_CR_TXE_INT_EN | I2C_CR_MTF_INT_EN | I2C_CR_STOPF_INTEN | I2C_CR_RXNE_INT_EN);
    }
}
