/******************************************************************************
*@file  : spl_i2c.c
*@brief : I2C SPL module driver
*@ver   : 1.0.0
*@date  : 2023.3
******************************************************************************/

#include "spl_i2c.h"

/**
  * @brief  Deinitialize the I2Cx peripheral registers to their default reset values.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @retval None
  */
void I2C_DeInit(I2C_TypeDef* I2Cx)
{
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));

	if (I2Cx == I2C1)
	{
        /* Enable I2C1 reset state */
        __RCC_I2C1_CLK_DISABLE();
	}
	else
	{
        /* Enable I2C2 reset state */
        __RCC_I2C2_CLK_DISABLE();
	}
}

/**
  * @brief  Set the I2Cx CLK DIV.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  ClockSpeed: The I2C clock speed.
  * @retval None
  */
void I2C_Set_Clock_Speed(I2C_TypeDef *I2Cx, uint32_t ClockSpeed)
{
    uint32_t Pclk1;

    Pclk1 = RCC_GetPCLK1Freq();

    I2Cx->CLK_DIV = Pclk1 / (4 * ClockSpeed) - 1;
}

/**
  * @brief  Fills each I2C_InitStruct member with its default value.
  * @param  I2C_InitStruct: pointer to an I2C_InitTypeDef structure which will be initialized.
  * @retval None
  */
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct)
{
    I2C_InitStruct->Clock_Speed = CLOCK_SPEED_STANDARD;

    I2C_InitStruct->I2C_Mode = I2C_MODE_MASTER;

    I2C_InitStruct->Stretch_Mode = STRETCH_MODE_DISABLE;

    I2C_InitStruct->Own_Address = 0;

    I2C_InitStruct->Tx_Auto_En = TX_AUTO_ENABLE;

    I2C_InitStruct->filter_enable = FILTER_ALGO_DISABLE;
}

/**
  * @brief  Initializes the I2Cx peripheral according to the specified 
  *         parameters in the I2C_InitStruct.        
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  I2C_InitStruct: pointer to a I2C_InitTypeDef structure that contains 
  *         the configuration information for the specified I2C peripheral.
  * @retval None
  */
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct)
{
    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_I2C_CLOCK_SPEED(I2C_InitStruct->Clock_Speed));
    assert_param(IS_I2C_MODE(I2C_InitStruct->I2C_Mode));
	
	/* Disable the selected I2C peripheral */
    CLEAR_BIT(I2Cx->CR, I2C_CR_MEN);
	
	switch(I2C_InitStruct->I2C_Mode)
    {
        /* Master Mode */
        case I2C_MODE_MASTER:
        {
            /* Set Master Mode */
            SET_BIT(I2Cx->CR, I2C_CR_MASTER);
            
            /* Set Clock Speed */
            I2C_Set_Clock_Speed(I2Cx, I2C_InitStruct->Clock_Speed);
            
            /* Set SDA auto change the direction */
            if (I2C_InitStruct->Tx_Auto_En == TX_AUTO_ENABLE)
                SET_BIT(I2Cx->CR, I2C_CR_TX_AUTO_EN);
            else
                CLEAR_BIT(I2Cx->CR, I2C_CR_TX_AUTO_EN);
            
        }break;
        
        /* Slave Mode */
        case I2C_MODE_SLAVE: 
        {
            SET_BIT(I2Cx->CR, I2C_CR_TXE_SEL);

            /* Set SDA auto change the direction */
            if (I2C_InitStruct->Tx_Auto_En == TX_AUTO_ENABLE)
                SET_BIT(I2Cx->CR, I2C_CR_TX_AUTO_EN);
            else
                CLEAR_BIT(I2Cx->CR, I2C_CR_TX_AUTO_EN);
            
            /* Set Clock Stretch Mode */
            if (I2C_InitStruct->Stretch_Mode == STRETCH_MODE_DISABLE)
                SET_BIT(I2Cx->CR, I2C_CR_NOSTRETCH);
            else
                CLEAR_BIT(I2Cx->CR, I2C_CR_NOSTRETCH);

            /* Set Address 1 */
            I2Cx->SLAVE_ADDR1 = I2C_InitStruct->Own_Address;
            
        }break;
        
        default: break; 
    }
}

/**
  * @brief  Enables or disables the specified I2C peripheral.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  NewState: new state of the I2Cx peripheral. 
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	assert_param(IS_FUNCTIONAL_STATE(NewState));
	
	if (NewState != DISABLE)
	{
		/* Enable the selected I2C peripheral */
		I2Cx->CR |= I2C_CR_MEN;
	}
	else
	{
		/* Disable the selected I2C peripheral */
		I2Cx->CR &= ~I2C_CR_MEN;
	}
}

/**
  * @brief  Enables or disables the specified I2C DMA requests.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  NewState: new state of the I2C DMA transfer.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	assert_param(IS_FUNCTIONAL_STATE(NewState));
	if (NewState != DISABLE)
	{
		/* Enable the selected I2C DMA requests */
		I2Cx->CR |= I2C_CR_DMA_EN;
	}
	else
	{
		/* Disable the selected I2C DMA requests */
		I2Cx->CR &= ~I2C_CR_DMA_EN;
	}
}

/**
  * @brief  Checks whether the specified I2C flag is set or not.
  * @param  I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
  * @param  I2C_FLAG: specifies the flag to check. 
  *          This parameter can be one of the following values:
  *            @arg I2C_SR_RACK: Received ACK or NACK
  *            @arg I2C_SR_STARTF: Master send STARTF flag
  *            @arg I2C_SR_STOPF: STOPF flag(Master/Slave mode)
  *            @arg I2C_SR_SRW: Transmitter/Receiver flag (Slave mode)
  *            @arg I2C_SR_BUS_BUSY: Bus busy flag
  *            @arg I2C_SR_TX_RX_FLAG: Transmit state changes to Receive state flag(Master mode)
  *            @arg I2C_SR_MARLO: Arbitration lost flag (Master mode)
  *            @arg I2C_SR_MTF: Byte transmit completed flag
  *            @arg I2C_SR_RX_ADDR1: Address1 matched flag (Slave mode)
  *            @arg I2C_SR_TXE: Data register empty flag (Transmitter)
  *            @arg I2C_SR_RXNE: Data register not empty (Receiver) flag
  *            @arg I2C_SR_OVR: Overrun flag(Slave mode)
  *            @arg I2C_SR_RX_ADDR2: Address2 matched flag (Slave mode)
  *            @arg I2C_SR_DETR: SDA filter flag
  *            @arg I2C_SR_RX_ADDR3: Address3 matched flag (Slave mode)
  *            @arg I2C_SR_TIMEOUTAF: TIMEOUTA timeout flag
  *            @arg I2C_SR_TIMEOUTBF: TIMEOUTB timeout flag
  * @retval The new state of I2C_FLAG (SET or RESET).
  */
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG)
{
	FlagStatus bitstatus = RESET;
	
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	
	if(READ_BIT(I2Cx->SR,I2C_FLAG))
	{
		bitstatus =SET;
	}
		
	return bitstatus;	
}

/**
  * @brief  Clears the I2Cx's pending flags.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  I2C_FLAG: specifies the flag to clear. 
  *          This parameter can be one of the following values:
  *            @arg I2C_SR_STARTF: Master send STARTF flag
  *            @arg I2C_SR_STOPF: STOPF flag(Master/Slave mode)
  *            @arg I2C_SR_TX_RX_FLAG: Transmit state changes to Receive state flag(Master mode)
  *            @arg I2C_SR_MTF: Byte transmit completed flag
  *            @arg I2C_SR_RX_ADDR1: Address1 matched flag (Slave mode)
  *            @arg I2C_SR_TXE: Data register empty flag (Transmitter)
  *            @arg I2C_SR_RXNE: Data register not empty (Receiver) flag
  *            @arg I2C_SR_RX_ADDR2: Address2 matched flag (Slave mode)
  *            @arg I2C_SR_DETR: SDA filter flag
  *            @arg I2C_SR_RX_ADDR3: Address3 matched flag (Slave mode)
  *            @arg I2C_SR_TIMEOUTAF: TIMEOUTA timeout flag
  *            @arg I2C_SR_TIMEOUTBF: TIMEOUTB timeout flag
  * @retval None
  */
void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG)
{
    uint32_t flagpos = 0;
	
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    assert_param(IS_I2C_CLEAR_FLAG(I2C_FLAG));
	
  /* Clear the selected I2C flag  */
	WRITE_REG(I2Cx->SR, I2C_FLAG);
}

/**
  * @brief  Enables or disables the specified I2C interrupts.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  I2C_IT: specifies the I2C interrupts sources to be enabled or disabled. 
  *          This parameter can be any combination of the following values:
  *            @arg I2C_CR_STOPF_INTEN: STOPF interrupt
  *            @arg I2C_CR_RX_ADDR3_INTEN: RXADDR3 addres match interrupt
  *            @arg I2C_CR_MARLO_INTEN: Arbitration lost interrupt 
  *            @arg I2C_CR_DETR_INT_EN: SDA filter interrupt
  *            @arg I2C_CR_RX_ADDR2_INT_EN: RXADDR2 addres match interrupt
  *            @arg I2C_CR_OVR_INT_EN:  Overrun interrupt
  *            @arg I2C_CR_RXNE_INT_EN: Data register not empty(Receiver) interrupt
  *            @arg I2C_CR_TXE_INT_EN:  Date register empty(Transmitter) interrupt
  *            @arg I2C_CR_RX_ADDR1_INT_EN: RXADDR1 addres match interrupt
  *            @arg I2C_CR_MTF_INT_EN:  
  * @param  NewState: new state of the specified I2C interrupts.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_ITConfig(I2C_TypeDef* I2Cx, uint32_t I2C_IT, FunctionalState NewState)
{
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	assert_param(IS_FUNCTIONAL_STATE(NewState));
  
	if (NewState != DISABLE)
	{
        /* Enable the selected I2C interrupts */
        I2Cx->CR |= I2C_IT;
	}
	else
	{
        /* Disable the selected I2C interrupts */
        I2Cx->CR &= (uint16_t)~I2C_IT;
	}
}

/**
  * @brief  Generates I2Cx communication START condition.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  NewState: new state of the I2C START condition generation.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	assert_param(IS_FUNCTIONAL_STATE(NewState));
	
	if (NewState != DISABLE)
	{
	/* Generate a START condition */
	I2Cx->CR |= I2C_CR_START;
	}
	else
	{
	/* Disable the START condition generation */
	I2Cx->CR &= ~I2C_CR_START;
	}
}

/**
  * @brief  Generates I2Cx communication STOP condition.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  NewState: new state of the I2C STOP condition generation.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_I2C_ALL_PERIPH(I2Cx));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
	
  if (NewState != DISABLE)
  {
    /* Generate a STOP condition */
    I2Cx->CR |= I2C_CR_STOP;
  }
  else
  {
    /* Disable the STOP condition generation */
    I2Cx->CR &= ~I2C_CR_STOP;
  }
}

/**
  * @brief  Generates I2Cx communication ACK or NACK condition.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  NewState: Generate ACK or NACK condition generation.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void I2C_GenerateNACK(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_I2C_ALL_PERIPH(I2Cx));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
	
  if (NewState != DISABLE)
  {
    /* Generate a STOP condition */
    I2Cx->CR |= I2C_CR_TACK;
  }
  else
  {
    /* Disable the STOP condition generation */
    I2Cx->CR &= ~I2C_CR_TACK;
  }
}


/**
  * @brief  Transmits the address byte to select the slave device.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  Address: specifies the slave address which will be transmitted
  * @param  I2C_Direction: specifies whether the I2C device will be a Transmitter
  *         or a Receiver. 
  *          This parameter can be one of the following values
  *            @arg I2C_Direction_Transmitter: Transmitter mode
  *            @arg I2C_Direction_Receiver: Receiver mode
  * @retval None.
  */
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction)
{
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	assert_param(IS_I2C_DIRECTION(I2C_Direction));
	
	/* Test on the direction to set/reset the read/write bit */
	if (I2C_Direction != I2C_Direction_Transmitter)
	{
		/* Set the address bit0 for read */
		Address |= 0x01;
	}
	else
	{
		/* Reset the address bit0 for write */
		Address &= 0xFE;
	}
	/* Send the address */
	I2Cx->DR = Address;	
}

/**
  * @brief  Configures the specified I2C own address2.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  Address: specifies the 7bit I2C own address2.
  * @retval None.
  */
void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address)
{
    uint16_t tmpreg = 0;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    /* Get the old register value */
    tmpreg = (uint16_t)I2Cx->SLAVE_ADDR2_3;

    /* Reset I2Cx Own address2 bit [7:1] */
    tmpreg &= ~((uint16_t)I2C_SLAVE_ADDR2_Msk);

    /* Set I2Cx Own address2 */
    tmpreg |= Address & 0x00FE;

    /* Store the new register value */
    I2Cx->SLAVE_ADDR2_3 = tmpreg;
}

/**
  * @brief  Enable or Disable I2C own address2.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  NewState: new state of the I2C dual addressing mode.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void OwnAddress2Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_I2C_ALL_PERIPH(I2Cx));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
    
  if (NewState != DISABLE)
  {
    /* Enable dual addressing mode */
    I2Cx->SLAVE_ADDR2_3 |= I2C_SLAVE_ADDR2EN;
  }
  else
  {
    /* Disable dual addressing mode */
    I2Cx->SLAVE_ADDR2_3 &= ~I2C_SLAVE_ADDR2EN;
  }
}

/**
  * @brief  Configures the specified I2C own address2.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  Address: specifies the 7bit I2C own address3.
  * @retval None.
  */
void I2C_OwnAddress3Config(I2C_TypeDef* I2Cx, uint8_t Address)
{
    uint16_t tmpreg = 0;

    /* Check the parameters */
    assert_param(IS_I2C_ALL_PERIPH(I2Cx));

    /* Get the old register value */
    tmpreg = (uint16_t)I2Cx->SLAVE_ADDR2_3;

    /* Reset I2Cx Own address2 bit [7:1] */
    tmpreg &= ~((uint16_t)I2C_SLAVE_ADDR3_Msk);

    /* Set I2Cx Own address2 */
    tmpreg |= Address & 0x00FE;

    /* Store the new register value */
    I2Cx->SLAVE_ADDR2_3 = tmpreg;
}

/**
  * @brief  Enable or Disable I2C own address3.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  NewState: new state of the I2C dual addressing mode.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void OwnAddress3Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_I2C_ALL_PERIPH(I2Cx));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
    
  if (NewState != DISABLE)
  {
    /* Enable dual addressing mode */
    I2Cx->SLAVE_ADDR2_3 |= I2C_SLAVE_ADDR3EN;
  }
  else
  {
    /* Disable dual addressing mode */
    I2Cx->SLAVE_ADDR2_3 &= ~I2C_SLAVE_ADDR3EN;
  }
}

/**
  * @brief  Enables or disables the specified I2C Clock stretching.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  NewState: new state of the I2Cx Clock stretching mode.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void I2C_StretchClockCmd(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	assert_param(IS_FUNCTIONAL_STATE(NewState));
	
	if (NewState == DISABLE)
	{
		/* Enable the selected I2C Clock stretching */
		I2Cx->CR &= ~I2C_CR_NOSTRETCH;
	}
	else
	{
		/* Disable the selected I2C Clock stretching */
		I2Cx->CR |= I2C_CR_NOSTRETCH;
	}
}

/**
  * @brief  Reads the specified I2C register and returns its value.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  I2C_Register: specifies the register to read.
  *          This parameter can be:
  *                    I2Cx->SLAVE_ADDR1
  *                    I2Cx->CLK_DIV
  *                    I2Cx->CR
  *                    I2Cx->SR
  *                    I2Cx->DR
  *                    I2Cx->SLAVE_ADDR2_3
  *                    I2Cx->FILTER
  *                    I2Cx->TIMEOUT
  * @retval The specified register value.
  */
uint32_t I2C_ReadRegister(I2C_TypeDef* I2Cx, uint8_t I2C_Register)
{
    __IO uint32_t tmp = 0;
	
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	assert_param(IS_I2C_REGISTER(I2C_Register));
	
	tmp = (uint32_t) I2Cx;
	tmp += I2C_Register;

	/* Return the selected register value */
	return (*(__IO uint32_t *) tmp);
}

/**
  * @brief  Sends a data byte through the I2Cx peripheral.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  Data: Byte to be transmitted..
  * @retval None
  */
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data)
{
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	
	/* Write in the DR register the data to be sent */
	I2Cx->DR = Data;
}

/**
  * @brief  Returns the most recent received data by the I2Cx peripheral.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @retval The value of the received data.
  */
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx)
{
	/* Check the parameters */
	assert_param(IS_I2C_ALL_PERIPH(I2Cx));
	
	/* Return the data in the DR register */
	return (uint8_t)I2Cx->DR;
}

