/**
 *********************************************************************************
 *
 * @file    hal_i2c.c
 * @brief   I2C module driver.
 *
 * @version V1.0
 * @date    15 Nov 2017
 * @author  AE Team
 * @note
 *
 * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
 *
 @verbatim
 ==============================================================================
                       ##### How to use this driver #####
 ==============================================================================
 [..]
   The I2C driver can be used as follows:

   (#) Declare a i2c_handle_t handle structure, for example:
       i2c_handle_t  hperh;

   (#) Configure the Communication Speed, Duty cycle, Addressing mode, Own Address1,
       Dual Addressing mode, Own Address2, General call and Nostretch mode in the hperh init structure.

   (#) Initialize the I2C registers by calling the i2c_init().
   (#) To check if target device is ready for communication, use the function i2c_is_device_ready()

   (#) For I2C IO and IO MEM operations, three operation modes are available within this driver :

   *** Polling mode IO operation ***
   =================================
   [..]
     (+) Transmit in master mode an amount of data in blocking mode using i2c_master_send()
     (+) Receive in master mode an amount of data in blocking mode using i2c_master_recv()
     (+) Transmit in slave mode an amount of data in blocking mode using i2c_slave_send()
     (+) Receive in slave mode an amount of data in blocking mode using i2c_slave_recv()

   *** Polling mode IO MEM operation ***
   =====================================
   [..]
     (+) Write an amount of data in blocking mode to a specific memory address using i2c_mem_write()
     (+) Read an amount of data in blocking mode from a specific memory address using i2c_mem_read()


   *** Interrupt mode IO operation ***
   ===================================
   [..]
     (+) The I2C interrupts should have the highest priority in the application in order
         to make them uninterruptible.
     (+) Transmit in master mode an amount of data in non-blocking mode using i2c_master_send_by_it()
     (+) At transmission end of transfer, hperh->master_tx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->master_tx_cplt_cbk()
     (+) Receive in master mode an amount of data in non-blocking mode using i2c_master_recv_by_it()
     (+) At reception end of transfer, hperh->master_rx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->master_rx_cplt_cbk()
     (+) Transmit in slave mode an amount of data in non-blocking mode using i2c_slave_send_by_it()
     (+) At transmission end of transfer, hperh->slave_tx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->slave_tx_cplt_cbk()
     (+) Receive in slave mode an amount of data in non-blocking mode using i2c_slave_recv_by_it()
     (+) At reception end of transfer, hperh->slave_rx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->slave_rx_cplt_cbk()
     (+) In case of transfer Error, hperh->error_callback() function is executed and user can
          add his own code by customization of function pointer hperh->error_callback()

   *** Interrupt mode IO MEM operation ***
   =======================================
   [..]
     (+) The I2C interrupts should have the highest priority in the application in order
         to make them uninterruptible.
     (+) Write an amount of data in non-blocking mode with Interrupt to a specific memory address using
         i2c_mem_write_by_it()
     (+) At Memory end of write transfer, hperh->mem_tx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->mem_tx_cplt_cbk()
     (+) Read an amount of data in non-blocking mode with Interrupt from a specific memory address using
         i2c_mem_read_by_it()
     (+) At Memory end of read transfer, hperh->mem_rx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->mem_rx_cplt_cbk()
     (+) In case of transfer Error, hperh->error_callback() function is executed and user can
          add his own code by customization of function pointer hperh->error_callback()

   *** DMA mode IO operation ***
   ==============================
   [..]
     (+) Transmit in master mode an amount of data in non-blocking mode (DMA) using
         i2c_master_send_by_dma()
     (+) At transmission end of transfer, hperh->master_tx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->master_tx_cplt_cbk()
     (+) Receive in master mode an amount of data in non-blocking mode (DMA) using
         i2c_master_recv_by_dma()
     (+) At reception end of transfer, hperh->master_rx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->master_rx_cplt_cbk()
     (+) Transmit in slave mode an amount of data in non-blocking mode (DMA) using
         i2c_slave_send_by_dma()
     (+) At transmission end of transfer, hperh->slave_tx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->slave_tx_cplt_cbk()
     (+) Receive in slave mode an amount of data in non-blocking mode (DMA) using
         i2c_slave_recv_by_dma()
     (+) At reception end of transfer, hperh->slave_rx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->slave_rx_cplt_cbk()
     (+) In case of transfer Error, hperh->error_callback() function is executed and user can
          add his own code by customization of function pointer hperh->error_callback()

   *** DMA mode IO MEM operation ***
   =================================
   [..]
     (+) Write an amount of data in non-blocking mode with DMA to a specific memory address using
         i2c_mem_write_by_dma()
     (+) At Memory end of write transfer, hperh->mem_tx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->mem_tx_cplt_cbk()
     (+) Read an amount of data in non-blocking mode with DMA from a specific memory address using
         i2c_mem_read_by_dma()
     (+) At Memory end of read transfer, hperh->mem_rx_cplt_cbk() is executed and user can
          add his own code by customization of function pointer hperh->mem_rx_cplt_cbk()
     (+) In case of transfer Error, hperh->error_callback() function is executed and user can
          add his own code by customization of function pointer hperh->error_callback()


    *** I2C hal_status_t driver macros list ***
    ==================================
    [..]
      Below the list of most used macros in I2C hal_status_t driver.

     (+) __I2C_ENABLE: Enable the I2C peripheral
     (+) __I2C_DISABLE: Disable the I2C peripheral
     (+) I2C_GET_FLAG:    Check whether the specified I2C flag is set or not
     (+) I2C_CLEAR_FLAG : Clear the specified I2C pending flag
     (+) I2C_ENABLE_IT: Enable the specified I2C interrupt
     (+) I2C_DISABLE_IT: Disable the specified I2C interrupt
      (@) You can refer to the I2C hal_status_t driver header file for more useful macros


    *** I2C Workarounds linked to Silicon Limitation ***
    ====================================================
    [..]
      Below the list of all silicon limitations implemented for library on our product.
      (@) See ErrataSheet to know full silicon limitation list of your product.

      (#) Workarounds Implemented inside I2C library
         (##) Wrong data read into data register (Polling and Interrupt mode)
         (##) Start cannot be generated after a misplaced Stop
         (##) Some software events must be managed before the current byte is being transferred:
              Workaround: Use DMA in general, except when the Master is receiving a single byte.
              For Interupt mode, I2C should have the highest priority in the application.
         (##) Mismatch on the "Setup time for a repeated Start condition" timing parameter:
              Workaround: Reduce the frequency down to 88 kHz or use the I2C Fast-mode if
              supported by the slave.
         (##) Data valid time (tVD;DAT) violated without the OVR flag being set:
              Workaround: If the slave device allows it, use the clock stretching mechanism
              by programming no_stretch_mode = I2C_NOSTRETCH_DISABLE in i2c_init.

 @endverbatim
 *********************************************************************************
 */

#include "hal_i2c.h"

///** @addtogroup ES32FXXX_HAL
// * @{
// */

///** @defgroup I2C I2C
// * @brief I2C module driver
// * @{
// */
//#ifdef HAL_I2C

///** @addtogroup I2C_Private_Constants I2C Private Constants
// * @{
// */
#define I2C_TIMEOUT_FLAG          35
#define I2C_TIMEOUT_ADDR_SLAVE    10000
#define I2C_TIMEOUT_BUSY_FLAG     10000
#define I2C_MAX_DELAY             0xFFFFFFFF
///**
// * @}
// */
///** @addtogroup I2C_Private_Functions I2C Private Functions
// * @{
// */
#ifdef HAL_DMA
static void i2c_dma_master_send_cplt(void *argv);
static void i2c_dma_master_recv_cplt(void *argv);
static void i2c_dma_slave_send_cplt(void *argv);
static void i2c_dma_slave_recv_cplt(void *argv);
static void i2c_dma_mem_send_cplt(void *argv);
static void i2c_dma_mem_recv_cplt(void *argv);
static void i2c_dma_error(void *argv);
#endif
static hal_status_t i2c_master_req_write(i2c_handle_t *hperh, uint16_t dev_addr, uint32_t timeout);
static hal_status_t i2c_master_req_read(i2c_handle_t *hperh, uint16_t dev_addr, uint32_t timeout);
static hal_status_t i2c_req_mem_write(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr,
                                      uint16_t add_size, uint32_t timeout);
static hal_status_t i2c_req_mem_read(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr,
                                     uint16_t add_size, uint32_t timeout);
static hal_status_t i2c_wait_flag_to_timeout(i2c_handle_t *hperh, uint32_t flag,
                                                flag_status_t status, uint32_t timeout);
static hal_status_t i2c_wait_master_addr_to_timeout(i2c_handle_t *hperh, uint32_t flag, uint32_t timeout);
static hal_status_t i2c_wait_txe_to_timeout(i2c_handle_t *hperh, uint32_t timeout);
//static hal_status_t i2c_wait_btf_to_timeout(i2c_handle_t *hperh, uint32_t timeout);
static hal_status_t i2c_wait_rxne_to_timeout(i2c_handle_t *hperh, uint32_t timeout);
static hal_status_t i2c_wait_stop_to_timeout(i2c_handle_t *hperh, uint32_t timeout);
static hal_status_t i2c_is_ack_failed(i2c_handle_t *hperh);
static hal_status_t i2c_master_send_txe(i2c_handle_t *hperh);
static hal_status_t i2c_master_send_btf(i2c_handle_t *hperh);
static hal_status_t i2c_master_recv_rxne(i2c_handle_t *hperh);
static hal_status_t i2c_master_recv_btf(i2c_handle_t *hperh);
static hal_status_t i2c_slave_send_txe(i2c_handle_t *hperh);
//static hal_status_t i2c_slave_send_btf(i2c_handle_t *hperh);
static hal_status_t i2c_slave_recv_rxne(i2c_handle_t *hperh);
//static hal_status_t i2c_slave_recv_btf(i2c_handle_t *hperh);
static hal_status_t i2c_slave_addr(i2c_handle_t *hperh);
static hal_status_t i2c_slave_stopf(i2c_handle_t *hperh);
//static hal_status_t i2c_slave_af(i2c_handle_t *hperh);
//static uint32_t i2c_configure_speed(i2c_handle_t *hperh, uint32_t i2c_clk);
///**
// * @}
// */

///** @defgroup I2C_Public_Functions I2C Public functions
// * @{
// */

///** @defgroup I2C_Public_Functions_Group1 Initialization and de-initialization functions
// * @brief     Initialization and Configuration functions
// *
//@verbatim
// ===============================================================================
//              ##### Initialization and de-initialization functions #####
// ===============================================================================
//    [..]  This subsection provides a set of functions allowing to initialize and
//          de-initialiaze the I2Cx peripheral:

//      (+) Call the function i2c_init() to configure the selected device with
//          the selected configuration:
//        (++) Communication Speed
//        (++) Duty cycle
//        (++) Addressing mode
//        (++) Own Address 1
//        (++) Dual Addressing mode
//        (++) Own Address 2
//        (++) General call mode
//        (++) Nostretch mode

//      (+) Call the function i2c_reset() to restore the default configuration
//          of the selected I2Cx periperal.

//@endverbatim
// * @{
// */

/**
 * @brief  I2C Configuration Speed function *        
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the I2C speed.
 * @param  freqrange: I2C Peripheral bus clock
 * @retval Status, see @ref hal_status_t.
 */
hal_status_t i2c_speed_init(i2c_handle_t *hperh, uint32_t freqrange)
{
	if (I2C_FREQ_RANGE(freqrange) <= 0)
		return ERROR;
	
	if (hperh->init.clock_speed == 100000) {
		if (freqrange == 16000000) {
			hperh->perh->TIMINGR.PRESC  = 0x03;
			hperh->perh->TIMINGR.SCLL   = 0x19;
			hperh->perh->TIMINGR.SCLH   = 0x14;
			hperh->perh->TIMINGR.SDADEL = 0x3;
			hperh->perh->TIMINGR.SCLDEL = 0x6;
		}
		else if (freqrange == 8000000) {
			hperh->perh->TIMINGR.PRESC  = 0x01;
			hperh->perh->TIMINGR.SCLL   = 0x13;
			hperh->perh->TIMINGR.SCLH   = 0xF;
			hperh->perh->TIMINGR.SDADEL = 0x2;
			hperh->perh->TIMINGR.SCLDEL = 0x4;
		}
	}
	else if (hperh->init.clock_speed == 400000) {
		if (freqrange == 16000000) {
			hperh->perh->TIMINGR.PRESC  = 0x01;
			hperh->perh->TIMINGR.SCLL   = 0x0B;
			hperh->perh->TIMINGR.SCLH   = 0x5;
			hperh->perh->TIMINGR.SDADEL = 0x2;
			hperh->perh->TIMINGR.SCLDEL = 0x5;
		}
		else if (freqrange == 8000000) {
			hperh->perh->TIMINGR.PRESC  = 0x00;
			hperh->perh->TIMINGR.SCLL   = 0x9;
			hperh->perh->TIMINGR.SCLH   = 0x3;
			hperh->perh->TIMINGR.SDADEL = 0x1;
			hperh->perh->TIMINGR.SCLDEL = 0x3;
		}
	}
	else{
			hperh->perh->TIMINGR.PRESC  = 0x01;
			hperh->perh->TIMINGR.SCLL   = 0x13;
			hperh->perh->TIMINGR.SCLH   = 0xF;
			hperh->perh->TIMINGR.SDADEL = 0x2;
			hperh->perh->TIMINGR.SCLDEL = 0x4;
	}
	
	return OK;	
}
/**
 * @brief  Initializes the I2C according to the specified parameters
 *         in the i2c_init_t and initialize the associated handle.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @retval Status, see @ref hal_status_t.
 */
hal_status_t i2c_init(i2c_handle_t *hperh)
{
	uint32_t freqrange = 16000000;

	if (hperh == NULL)
		return ERROR;

	/* Check the parameters */
	assert_param(IS_I2C_CLOCK_SPEED(hperh->init.clock_speed));
	assert_param(IS_I2C_OWN_ADDRESS1(hperh->init.own_address1));
	assert_param(IS_I2C_ADDRESSING_MODE(hperh->init.addressing_mode));
	assert_param(IS_I2C_GENERAL_CALL(hperh->init.general_call_mode));
	assert_param(IS_I2C_NO_STRETCH(hperh->init.no_stretch_mode));

	if (hperh->init.dual_address_mode == I2C_DUALADDRESS_ENABLE)
		assert_param(IS_I2C_OWN_ADDRESS2(hperh->init.own_address2));

	if (hperh->state == I2C_STATE_RESET)
		hperh->lock = UNLOCK;

	hperh->state = I2C_STATE_BUSY;	
	
	__I2C_DISABLE(hperh);
	
	i2c_speed_init(hperh, freqrange);
	hperh->perh->CR1.NOSTRETCH = hperh->init.no_stretch_mode;
	hperh->perh->CR1.GCEN      = hperh->init.general_call_mode;
	hperh->perh->CR2.AUTOEND   = hperh->init.autoend_mode;

	hperh->perh->OAR2.OA2EN  = DISABLE;	
	hperh->perh->OAR2.OA2    = ((hperh->init.own_address1) >> 1);
	hperh->perh->OAR2.OA2EN  = ENABLE;

	if (hperh->init.dual_address_mode) {
		hperh->perh->OAR1.OA1MODE = ENABLE;
		hperh->perh->OAR1.OA1L    = 0x11;
		hperh->perh->OAR1.OA10    = 0x0;
		hperh->perh->OAR1.OA1H    = 0x0;
		hperh->perh->OAR1.OA1EN   = ENABLE;
	}
	
	hperh->perh->CR1.DNF = 0x06;
	hperh->perh->CR2.AUTOEND = 0;
	hperh->perh->CR2.HEAD10R = 0;

	hperh->error_code = I2C_ERROR_NONE;
	hperh->state      = I2C_STATE_READY;
	hperh->mode       = I2C_MODE_NONE;

	return OK;
}

/**
 * @brief  DeInitialize the I2C peripheral.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
hal_status_t i2c_reset(i2c_handle_t *hperh)
{
	if (hperh == NULL)
		return ERROR;

	hperh->state = I2C_STATE_BUSY;
	__I2C_DISABLE(hperh);

	hperh->error_code = I2C_ERROR_NONE;
	hperh->state      = I2C_STATE_RESET;
	hperh->mode       = I2C_MODE_NONE;

	__UNLOCK(hperh);

	__I2C_ENABLE(hperh);
	return OK;
}
///**
// * @}
// */

///** @defgroup I2C_Public_Functions_Group2 Input and Output operation functions
// *  @brief   Data transfers functions
// *
//@verbatim
// ===============================================================================
//                      ##### IO operation functions #####
// ===============================================================================
//    [..]
//    This subsection provides a set of functions allowing to manage the I2C data
//    transfers.

//    (#) There are two modes of transfer:
//       (++) Blocking mode : The communication is performed in the polling mode.
//            The status of all data processing is returned by the same function
//            after finishing transfer.
//       (++) No-Blocking mode : The communication is performed using Interrupts
//            or DMA. These functions return the status of the transfer startup.
//            The end of the data processing will be indicated through the
//            dedicated I2C IRQ when using Interrupt mode or the DMA IRQ when
//            using DMA mode.

//    (#) Blocking mode functions are :
//        (++) i2c_master_send()
//        (++) i2c_master_recv()
//        (++) i2c_slave_send()
//        (++) i2c_slave_recv()
//        (++) i2c_mem_write()
//        (++) i2c_mem_read()
//        (++) i2c_is_device_ready()

//    (#) No-Blocking mode functions with Interrupt are :
//        (++) i2c_master_send_by_it()
//        (++) i2c_master_recv_by_it()
//        (++) i2c_slave_send_by_it()
//        (++) i2c_slave_recv_by_it()
//        (++) i2c_mem_write_by_it()
//        (++) i2c_mem_read_by_it()

//    (#) No-Blocking mode functions with DMA are :
//        (++) i2c_master_send_by_dma()
//        (++) i2c_master_recv_by_dma()
//        (++) i2c_slave_send_by_dma()
//        (++) i2c_slave_recv_by_dma()
//        (++) i2c_mem_write_by_dma()
//        (++) i2c_mem_read_by_dma()

//    (#) A set of Transfer Complete Callbacks are provided in non Blocking mode:
//        (++) hperh->mem_tx_cplt_cbk()
//        (++) hperh->mem_rx_cplt_cbk()
//        (++) hperh->master_tx_cplt_cbk()
//        (++) hperh->master_rx_cplt_cbk()
//        (++) hperh->slave_tx_cplt_cbk()
//        (++) hperh->slave_rx_cplt_cbk()
//        (++) hperh->error_callback()

//@endverbatim
// * @{
// */
/**
 * @brief  Transmits in master mode an amount of data in blocking mode.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  dev_addr: Target device address
 * @param  buf: Pointer to data buffer
 * @param  size: Amount of data to be sent
 * @param  timeout: Timeout duration
  * @retval Status, see @ref hal_status_t.
 */
hal_status_t i2c_master_send(i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf,
                                 uint16_t size, uint32_t timeout)
{
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_TX;
	hperh->mode       = I2C_MODE_MASTER;
	hperh->error_code = I2C_ERROR_NONE;

	if (i2c_master_req_write(hperh, dev_addr, timeout) != OK) {	
		__UNLOCK(hperh);
		return ERROR;
	}

	if (size <= 0xff) {
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	if (!I2C_GET_FLAG(hperh, I2C_FLAG_TXE)) {
		__I2C_DISABLE(hperh);

		__NOP();
		__NOP();

		__I2C_ENABLE(hperh);
	}

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_ARLO))
		I2C_CLEAR_IT(hperh, I2C_IT_ARLO);
	
	if (I2C_GET_IT_FLAG(hperh, I2C_IT_NACK)) 
		I2C_CLEAR_IT(hperh, I2C_IT_NACK);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_TXOV))
		I2C_CLEAR_IT(hperh, I2C_IT_TXOV);	

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	while (size > 0) {
		hperh->perh->TXDR.TXDATA  = (*buf++);
		--size;

		if (i2c_wait_txe_to_timeout(hperh, timeout) != OK) {
			SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
			hperh->state = I2C_STATE_READY;
			__UNLOCK(hperh);

			return TIMEOUT;
		}
	}

	if ((size == 0) || I2C_GET_FLAG(hperh, I2C_FLAG_TC) ) {
		if (!(hperh->perh->CR2.AUTOEND))
			SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);

		hperh->state = I2C_STATE_READY;
		__UNLOCK(hperh);

		return OK;
	}

	return ERROR;
}

/**
 * @brief  Receives in master mode an amount of data in blocking mode.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  dev_addr: Target device address
 * @param  buf: Pointer to data buffer
 * @param  size: Amount of data to be sent
 * @param  timeout: Timeout duration
 * @retval Status, see @ref hal_status_t
 */
  hal_status_t i2c_master_recv(i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf,
                                uint16_t size, uint32_t timeout)
{
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);
	
	hperh->state      = I2C_STATE_BUSY_RX;
	hperh->mode       = I2C_MODE_MASTER;
	hperh->error_code = I2C_ERROR_NONE;

	if (i2c_master_req_read(hperh, dev_addr, timeout) != OK) {
		__UNLOCK(hperh);
		return ERROR;
	}
	
	if (I2C_GET_IT_FLAG(hperh, I2C_IT_STOP)) 
		I2C_CLEAR_IT(hperh, I2C_IT_STOP);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_RXUD))
		I2C_CLEAR_IT(hperh, I2C_IT_RXUD);
	
	if (size <= 0xff) {
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	if (!I2C_GET_FLAG(hperh, I2C_FLAG_RXE)) {
		uint8_t tmp = hperh->perh->RXDR.RXDATA;
	}

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	while (size > 0) {
		if (i2c_wait_rxne_to_timeout(hperh, timeout) != OK) {
			if (hperh->error_code == I2C_ERROR_TIMEOUT) {
				__UNLOCK(hperh);
				SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
				return TIMEOUT;
			}
			else {
				__UNLOCK(hperh);
				SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
				return ERROR;
			}
		}

		(*buf++) = hperh->perh->RXDR.RXDATA;
		size--;
	}

	if ((size == 0) || I2C_GET_FLAG(hperh, I2C_FLAG_TC) ) {
		if (!(hperh->perh->CR2.AUTOEND))
			SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
		hperh->state = I2C_STATE_READY;
		__UNLOCK(hperh);

		return OK;
	}

	return ERROR;
}

/**
 * @brief  Transmits in slave mode an amount of data in blocking mode.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  buf: Pointer to data buffer
 * @param  size: Amount of data to be sent
 * @param  timeout: Timeout duration
  * @retval Status, see @ref hal_status_t.
 */
hal_status_t i2c_slave_send(i2c_handle_t *hperh, uint8_t *buf, uint16_t size, uint32_t timeout)
{
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_TX;
	hperh->mode       = I2C_MODE_SLAVE;
	hperh->error_code = I2C_ERROR_NONE;
	
	if (!I2C_GET_FLAG(hperh, I2C_FLAG_TXE)) {
		__I2C_DISABLE(hperh);

		__NOP();
		__NOP();

		__I2C_ENABLE(hperh);
	}

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_TXOV))
		I2C_CLEAR_IT(hperh, I2C_IT_TXOV);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_NACK))
		I2C_CLEAR_IT(hperh, I2C_IT_NACK);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_STOP)) 
		I2C_CLEAR_IT(hperh, I2C_IT_STOP);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_ADDR))
		I2C_CLEAR_IT(hperh, I2C_IT_ADDR);

	if (i2c_wait_master_addr_to_timeout(hperh, I2C_IT_ADDR, timeout) != OK) {
		__UNLOCK(hperh);
		return TIMEOUT;
	}

	I2C_CLEAR_IT(hperh, I2C_IT_ADDR);

	while (size > 0) {
		hperh->perh->TXDR.TXDATA = (*buf++);
		--size;

		if (i2c_wait_txe_to_timeout(hperh, timeout) != OK) {
			if (hperh->error_code == I2C_ERROR_AF) {
				__UNLOCK(hperh);
				return ERROR;
			}
			else {
				__UNLOCK(hperh);
				return TIMEOUT;
			}
		}		
	}

	hperh->state = I2C_STATE_READY;
	hperh->mode  = I2C_MODE_NONE;
	__UNLOCK(hperh);

	return OK;
}

/**
 * @brief  Receive in slave mode an amount of data in blocking mode
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  buf: Pointer to data buffer
 * @param  size: Amount of data to be sent
 * @param  timeout: Timeout duration
  * @retval Status, see @ref hal_status_t.
 */
hal_status_t i2c_slave_recv(i2c_handle_t *hperh, uint8_t *buf, uint16_t size, uint32_t timeout)
{
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_RX;
	hperh->mode       = I2C_MODE_SLAVE;
	hperh->error_code = I2C_ERROR_NONE;
	
	if (I2C_GET_IT_FLAG(hperh, I2C_IT_STOP)) 
		I2C_CLEAR_IT(hperh, I2C_IT_STOP);

	if (!I2C_GET_IT_FLAG(hperh, I2C_FLAG_RXE)) {
		uint8_t tmp = hperh->perh->RXDR.RXDATA;
	}
	
	if (I2C_GET_IT_FLAG(hperh, I2C_IT_ADDR))
		I2C_CLEAR_IT(hperh, I2C_IT_ADDR);
	
	if (i2c_wait_master_addr_to_timeout(hperh, I2C_IT_ADDR, timeout) != OK) {
		__UNLOCK(hperh);
		return TIMEOUT;
	}

	I2C_CLEAR_IT(hperh, I2C_IT_ADDR);
	
	while (size > 0) {
		if (i2c_wait_rxne_to_timeout(hperh, timeout) != OK) {
			hperh->perh->CR2.Word |= I2C_CR2_NACK;

			if (hperh->error_code == I2C_ERROR_TIMEOUT) {
				__UNLOCK(hperh);
				return TIMEOUT;
			}
			else {
				__UNLOCK(hperh);
				return ERROR;
			}
		}
		
		(*buf++) = hperh->perh->RXDR.RXDATA;
		--size;
	}

	hperh->state = I2C_STATE_READY;
	hperh->mode  = I2C_MODE_NONE;
	__UNLOCK(hperh);

	return OK;
}

/**
 * @brief  Transmit in master mode an amount of data in non-blocking mode with Interrupt
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  dev_addr: Target device address
 * @param  buf: Pointer to data buffer
 * @param  size: Amount of data to be sent
  * @retval Status, see @ref hal_status_t.
 */
hal_status_t i2c_master_send_by_it(i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint16_t size)
{
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);
	
	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_TX;
	hperh->mode       = I2C_MODE_MASTER;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	if (i2c_master_req_write(hperh, dev_addr, I2C_TIMEOUT_FLAG) != OK) {
		if (hperh->error_code == I2C_ERROR_AF) {
			__UNLOCK(hperh);
			return ERROR;
		}
		else {
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}

	if (size <= 0xff) {
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	if (!I2C_GET_FLAG(hperh, I2C_FLAG_TXE)) {
		__I2C_DISABLE(hperh);

		__NOP();
		__NOP();

		__I2C_ENABLE(hperh);
	}

	/* Note : The I2C interrupts must be enabled after unlocking current process
	 *        to avoid the risk of I2C interrupt handle execution before current
	 *        process unlock */
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_TXTH))
		I2C_CLEAR_IT(hperh , I2C_IT_TXTH);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_NACK))
		I2C_CLEAR_IT(hperh , I2C_IT_NACK);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_TC))
		I2C_CLEAR_IT(hperh , I2C_IT_TC);	

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);
	hperh->perh->TXDR.TXDATA = (*hperh->p_buff++);
	hperh->xfer_count--;

	I2C_DISABLE_IT(hperh, 0xFFFFFFFF);
	I2C_ENABLE_IT(hperh, I2C_IT_TXTH | I2C_IT_NACK | I2C_IT_TC);	

	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Receive in master mode an amount of data in non-blocking mode with Interrupt
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  dev_addr: Target device address
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_master_recv_by_it(i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf, uint16_t size)
{
	uint8_t tmp = 0;

	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);
	
	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_RX;
	hperh->mode       = I2C_MODE_MASTER;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	if (i2c_master_req_read(hperh, dev_addr, I2C_TIMEOUT_FLAG) != OK) {
		if (hperh->error_code == I2C_ERROR_AF) {
			__UNLOCK(hperh);
			return ERROR;
		}
		else {
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}
	
	if (size <= 0xff) {
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}
	__UNLOCK(hperh);

	/* Note : The I2C interrupts must be enabled after unlocking current process
	 *        to avoid the risk of I2C interrupt handle execution before current
	 *        process unlock */
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_RXTH))
		I2C_CLEAR_IT(hperh , I2C_IT_RXTH);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_TC))
		I2C_CLEAR_IT(hperh , I2C_IT_TC);	

	I2C_DISABLE_IT(hperh, 0xFFFFFFFF);
	I2C_ENABLE_IT(hperh, I2C_IT_RXTH | I2C_IT_TC);
	
	tmp = hperh->perh->RXDR.RXDATA;
	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	return OK;
}

/**
  * @brief  Transmit in slave mode an amount of data in non-blocking mode with Interrupt
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
   * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_slave_send_by_it(i2c_handle_t *hperh, uint8_t *buf, uint16_t size)
{
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);
	
	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_TX;
	hperh->mode       = I2C_MODE_SLAVE;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;	

	
	__UNLOCK(hperh);

	/* Note : The I2C interrupts must be enabled after unlocking current process
	 *        to avoid the risk of I2C interrupt handle execution before current
	 *        process unlock */
	if (!I2C_GET_FLAG(hperh, I2C_FLAG_TXE)) {
		__I2C_DISABLE(hperh);

		__NOP();
		__NOP();

		__I2C_ENABLE(hperh);
	}

	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_TXTH))
		I2C_CLEAR_IT(hperh , I2C_IT_TXTH);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_ADDR))
		I2C_CLEAR_IT(hperh , I2C_IT_ADDR);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_NACK))
		I2C_CLEAR_IT(hperh , I2C_IT_NACK);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_STOP))
		I2C_CLEAR_IT(hperh , I2C_IT_STOP);

	I2C_DISABLE_IT(hperh, 0xFFFFFFFF);
	I2C_ENABLE_IT(hperh, I2C_IT_ADDR | I2C_IT_NACK | I2C_IT_STOP | I2C_IT_TXTH);
	hperh->perh->TXDR.TXDATA = (*hperh->p_buff++);
	hperh->xfer_count--;

	return OK;
}

/**
  * @brief  Receive in slave mode an amount of data in non-blocking mode with Interrupt
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
   * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_slave_recv_by_it(i2c_handle_t *hperh, uint8_t *buf, uint16_t size)
{
	uint8_t Tmp_Data = 0;

	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);
	
	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_RX;
	hperh->mode       = I2C_MODE_SLAVE;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	__UNLOCK(hperh);

	/* Note : The I2C interrupts must be enabled after unlocking current process
	 *        to avoid the risk of I2C interrupt handle execution before current
	 *        process unlock */
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_RXTH))
		I2C_CLEAR_IT(hperh , I2C_IT_RXTH);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_ADDR))
		I2C_CLEAR_IT(hperh , I2C_IT_ADDR);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_STOP))
		I2C_CLEAR_IT(hperh , I2C_IT_STOP);

	I2C_DISABLE_IT(hperh, 0xFFFFFFFF);
	I2C_ENABLE_IT(hperh, I2C_IT_RXTH | I2C_IT_ADDR | I2C_IT_STOP);
	Tmp_Data = hperh->perh->RXDR.RXDATA;

	return OK;
}

#ifdef HAL_DMA
/**
 * @brief  Transmit in master mode an amount of data in non-blocking mode with DMA
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  dev_addr: Target device address
 * @param  buf: Pointer to data buffer
 * @param  size: Amount of data to be sent
 * @param  channel: DMA channel as I2C transmit
  * @retval Status, see @ref hal_status_t.
 */
hal_status_t i2c_master_send_by_dma(i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf,
                                    uint16_t size, uint8_t channel)
{
	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_TX;
	hperh->mode       = I2C_MODE_MASTER;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	if (hperh->hdmatx.perh == NULL)
		hperh->hdmatx.perh = DMA0;
	
	hperh->hdmatx.cplt_cbk = i2c_dma_master_send_cplt;
	hperh->hdmatx.cplt_arg = hperh;
	hperh->hdmatx.err_cbk  = i2c_dma_error;
	hperh->hdmatx.err_arg  = hperh;

	dma_config_struct(&hperh->hdmatx.config);
	hperh->hdmatx.config.burst      = ENABLE;
	hperh->hdmatx.config.src     	= (void *)buf;
	hperh->hdmatx.config.dst     	= (void *)&hperh->perh->TXDR.Word;
	hperh->hdmatx.config.size    	= size;
	hperh->hdmatx.config.data_width = DMA_DATA_SIZE_BYTE;
	hperh->hdmatx.config.src_inc 	= DMA_DATA_INC_BYTE;
	hperh->hdmatx.config.dst_inc 	= DMA_DATA_INC_NONE;
	hperh->hdmatx.config.msel    	= hperh->perh == I2C0 ? DMA_MSEL_I2C0 : DMA_MSEL_I2C1;
	hperh->hdmatx.config.msigsel 	= DMA_MSIGSEL_I2C_TXEMPTY;
	hperh->hdmatx.config.channel 	= channel;
	dma_config_basic(&hperh->hdmatx);

	if (i2c_master_req_write(hperh, dev_addr, I2C_TIMEOUT_FLAG) != OK) {
		if (hperh->error_code == I2C_ERROR_AF) {
			hperh->state = I2C_STATE_READY;
			__UNLOCK(hperh);
			return ERROR;
		}
		else {
			hperh->state = I2C_STATE_READY;
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}

	if (size <= 0xff) {
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	if (!I2C_GET_FLAG(hperh, I2C_FLAG_TXE)) {
		__I2C_DISABLE(hperh);

		__NOP();
		__NOP();

		__I2C_ENABLE(hperh);
	}

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_STOP)) 
		I2C_CLEAR_IT(hperh, I2C_IT_STOP);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_ARLO))
		I2C_CLEAR_IT(hperh, I2C_IT_ARLO);

	SET_BIT(hperh->perh->CR1.Word, I2C_CR1_TXDMAEN);
	__I2C_ENABLE(hperh);
	
	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Receive in master mode an amount of data in non-blocking mode with DMA
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  dev_addr: Target device address
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
  * @param  channel: DMA channel as I2C receive
   * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_master_recv_by_dma(i2c_handle_t *hperh, uint16_t dev_addr, uint8_t *buf,
                                    uint16_t size, uint8_t channel)
{
	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_RX;
	hperh->mode       = I2C_MODE_MASTER;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	if (hperh->hdmarx.perh == NULL)
		hperh->hdmarx.perh = DMA0;
	
	hperh->hdmarx.cplt_cbk = i2c_dma_master_recv_cplt;
	hperh->hdmarx.cplt_arg = (void *)hperh;
	hperh->hdmarx.err_cbk  = i2c_dma_error;
	hperh->hdmarx.err_arg  = (void *)hperh;

	dma_config_struct(&hperh->hdmarx.config);	
	hperh->hdmarx.config.src     	= (void *)&hperh->perh->RXDR.Word;
	hperh->hdmarx.config.dst     	= (void *)buf;
	hperh->hdmarx.config.size    	= size;
	hperh->hdmatx.config.data_width = DMA_DATA_SIZE_BYTE;
	hperh->hdmarx.config.src_inc 	= DMA_DATA_INC_NONE;
	hperh->hdmarx.config.dst_inc 	= DMA_DATA_INC_BYTE;
	hperh->hdmarx.config.msel    	= hperh->perh == I2C0 ? DMA_MSEL_I2C0 : DMA_MSEL_I2C1;
	hperh->hdmarx.config.msigsel 	= DMA_MSIGSEL_I2C_RNR;
	hperh->hdmarx.config.channel 	= channel;
	dma_config_basic(&hperh->hdmarx);

	if (i2c_master_req_read(hperh, dev_addr, I2C_TIMEOUT_FLAG) != OK) {
		if (hperh->error_code == I2C_ERROR_AF) {
			hperh->state = I2C_STATE_READY;
			__UNLOCK(hperh);
			return ERROR;
		}
		else {
			hperh->state = I2C_STATE_READY;
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}
	if (I2C_GET_IT_FLAG(hperh, I2C_IT_STOP)) 
		I2C_CLEAR_IT(hperh, I2C_IT_STOP);
	
	if (size <= 0xff) {
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	if (!I2C_GET_FLAG(hperh, I2C_FLAG_RXE)) {
		uint8_t tmp = hperh->perh->RXDR.RXDATA;
	}

	SET_BIT(hperh->perh->CR1.Word, I2C_CR1_RXDMAEN);
	__I2C_ENABLE(hperh);

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Transmit in slave mode an amount of data in non-blocking mode with DMA
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
  * @param  channel: DMA channel as I2C Transmit
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_slave_send_by_dma(i2c_handle_t *hperh, uint8_t *buf, uint16_t size, uint8_t channel)
{
	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_TX;
	hperh->mode       = I2C_MODE_SLAVE;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;
	
	if (hperh->hdmatx.perh == NULL)
		hperh->hdmatx.perh = DMA0;
	
	hperh->hdmatx.cplt_cbk = i2c_dma_slave_send_cplt;
	hperh->hdmatx.cplt_arg = hperh;
	hperh->hdmatx.err_cbk  = i2c_dma_error;
	hperh->hdmatx.err_arg  = hperh;

 	dma_config_struct(&hperh->hdmatx.config);
	hperh->hdmatx.config.R_power    = DMA_R_POWER_1024;
	hperh->hdmatx.config.src     	= (void *)buf;
	hperh->hdmatx.config.dst     	= (void *)&hperh->perh->TXDR.Word;
	hperh->hdmatx.config.size    	= size;
	hperh->hdmatx.config.data_width = DMA_DATA_SIZE_BYTE;
	hperh->hdmatx.config.src_inc 	= DMA_DATA_INC_BYTE;
	hperh->hdmatx.config.dst_inc 	= DMA_DATA_INC_NONE;
	hperh->hdmatx.config.msel    	= hperh->perh == I2C0 ? DMA_MSEL_I2C0 : DMA_MSEL_I2C1;
	hperh->hdmatx.config.msigsel 	= DMA_MSIGSEL_I2C_TXEMPTY;
	hperh->hdmatx.config.channel 	= channel;
	dma_config_basic(&hperh->hdmatx);
	
	SET_BIT(hperh->perh->CR1.Word, I2C_CR1_TXDMAEN);
	__I2C_ENABLE(hperh);

	if (i2c_wait_master_addr_to_timeout(hperh, I2C_IT_ADDR, I2C_TIMEOUT_ADDR_SLAVE) != OK) {
		__UNLOCK(hperh);
		return TIMEOUT;
	}

	I2C_CLEAR_IT(hperh, I2C_IT_ADDR);
	hperh->state = I2C_STATE_READY;

	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Receive in slave mode an amount of data in non-blocking mode with DMA
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
  * @param  channel: DMA channel as I2C receive
  * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_slave_recv_by_dma(i2c_handle_t *hperh, uint8_t *buf, uint16_t size, uint8_t channel)
{
	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_RX;
	hperh->mode       = I2C_MODE_SLAVE;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	if (hperh->hdmarx.perh == NULL)
		hperh->hdmarx.perh = DMA0;
	
	hperh->hdmarx.cplt_cbk = i2c_dma_slave_recv_cplt;
	hperh->hdmarx.cplt_arg = (void *)hperh;
	hperh->hdmarx.err_cbk  = i2c_dma_error;
	hperh->hdmarx.err_arg  = (void *)hperh;

	dma_config_struct(&hperh->hdmarx.config);
	hperh->hdmarx.config.src     	= (void *)&hperh->perh->RXDR.Word;
	hperh->hdmarx.config.dst     	= (void *)buf;
	hperh->hdmarx.config.size    	= size;
	hperh->hdmatx.config.data_width = DMA_DATA_SIZE_BYTE;
	hperh->hdmarx.config.src_inc 	= DMA_DATA_INC_NONE;
	hperh->hdmarx.config.dst_inc 	= DMA_DATA_INC_BYTE;
	hperh->hdmarx.config.msel    	= hperh->perh == I2C0 ? DMA_MSEL_I2C0 : DMA_MSEL_I2C1;
	hperh->hdmarx.config.msigsel 	= DMA_MSIGSEL_I2C_RNR;
	hperh->hdmarx.config.channel 	= channel;
	dma_config_basic(&hperh->hdmarx);

	SET_BIT(hperh->perh->CR1.Word, I2C_CR1_RXDMAEN);
	__I2C_ENABLE(hperh);

	if (i2c_wait_master_addr_to_timeout(hperh, I2C_IT_ADDR, I2C_TIMEOUT_ADDR_SLAVE) != OK) {
		__UNLOCK(hperh);
		return TIMEOUT;
	}

	I2C_CLEAR_IT(hperh, I2C_IT_ADDR);
	hperh->state = I2C_STATE_READY;

	__UNLOCK(hperh);

	return OK;
}
#endif

/**
  * @brief  Write an amount of data in blocking mode to a specific memory address
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  dev_addr: Target device address
  * @param  mem_addr: Internal memory address
  * @param  add_size: size of internal memory address
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
  * @param  timeout: Timeout duration
   * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_mem_write(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr,
                           i2c_addr_size_t add_size, uint8_t *buf, uint16_t size, uint32_t timeout)
{
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);
	
	assert_param(IS_I2C_MEMADD_size(add_size));

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_TX;
	hperh->mode       = I2C_MODE_MEM;
	hperh->error_code = I2C_ERROR_NONE;

	if (i2c_master_req_write(hperh, dev_addr, timeout) != OK) {	
		__UNLOCK(hperh);
		return ERROR;
	}

	if (size <= 0xff) {
		if (add_size == I2C_MEMADD_SIZE_8BIT) {
			hperh->perh->CR2.NBYTES = size + 1;
		}
		else {
			hperh->perh->CR2.NBYTES = size + 2;
		}
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	if (!I2C_GET_FLAG(hperh, I2C_FLAG_TXE)) {
		__I2C_DISABLE(hperh);

		__NOP();
		__NOP();

		__I2C_ENABLE(hperh);
	}

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_ADDR)) 
		I2C_CLEAR_IT(hperh, I2C_IT_ADDR);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_ARLO))
		I2C_CLEAR_IT(hperh, I2C_IT_ARLO);
	
	if (I2C_GET_IT_FLAG(hperh, I2C_IT_NACK)) 
		I2C_CLEAR_IT(hperh, I2C_IT_NACK);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_TXOV))
		I2C_CLEAR_IT(hperh, I2C_IT_TXOV);

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	if (add_size == I2C_MEMADD_SIZE_8BIT) {
		hperh->perh->TXDR.Word = I2C_MEM_ADD_LSB(mem_addr);
	}
	else {
		hperh->perh->TXDR.Word = I2C_MEM_ADD_LSB(mem_addr);
		
		if (i2c_wait_txe_to_timeout(hperh, timeout) != OK) {
			if (hperh->error_code == I2C_ERROR_AF) {
				SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
				__UNLOCK(hperh);
				return ERROR;
			}
			else {
				__UNLOCK(hperh);
				return TIMEOUT;
			}
		}

		hperh->perh->TXDR.Word = I2C_MEM_ADD_MSB(mem_addr);
	}

	while (size > 0) {
		if (i2c_wait_txe_to_timeout(hperh, timeout) != OK) {
			if (hperh->error_code == I2C_ERROR_AF) {
				SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
				__UNLOCK(hperh);
				return ERROR;
			}
			else {
				__UNLOCK(hperh);
				return TIMEOUT;
			}
		}			
			hperh->perh->TXDR.Word = (*buf++);
			--size;		
	}

	if ((size == 0) || I2C_GET_FLAG(hperh, I2C_FLAG_TC) ) {
		if (!(hperh->perh->CR2.AUTOEND))
			SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);

		hperh->state = I2C_STATE_READY;
		__UNLOCK(hperh);

		return OK;
	}

	return ERROR;
}

/**
  * @brief  Read an amount of data in blocking mode from a specific memory address
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  dev_addr: Target device address
  * @param  mem_addr: Internal memory address
  * @param  add_size: size of internal memory address
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
  * @param  timeout: Timeout duration
   * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_mem_read(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, i2c_addr_size_t add_size,
                          uint8_t *buf, uint16_t size, uint32_t timeout)
{
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);

	assert_param(IS_I2C_MEMADD_size(add_size));

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_RX;
	hperh->mode       = I2C_MODE_MEM;
	hperh->error_code = I2C_ERROR_NONE;

	if (i2c_master_req_write(hperh, dev_addr, timeout) != OK) {	
		__UNLOCK(hperh);
		return ERROR;
	}

	if (add_size == I2C_MEMADD_SIZE_8BIT) {
		hperh->perh->CR2.NBYTES = 1;
	}
	else {
		hperh->perh->CR2.NBYTES = 2;
	}

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_STOP)) 
		I2C_CLEAR_IT(hperh, I2C_IT_STOP);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_RXUD))
		I2C_CLEAR_IT(hperh, I2C_IT_RXUD);

	if (!I2C_GET_FLAG(hperh, I2C_FLAG_RXE)) {
		uint8_t tmp = hperh->perh->RXDR.RXDATA;
	}

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	if (add_size == I2C_MEMADD_SIZE_8BIT) {
		hperh->perh->TXDR.TXDATA = I2C_MEM_ADD_LSB(mem_addr);		
	}
	else {
		hperh->perh->TXDR.TXDATA = I2C_MEM_ADD_LSB(mem_addr);
		
		if (i2c_wait_txe_to_timeout(hperh, timeout) != OK) {
			if (hperh->error_code == I2C_ERROR_AF) {
				SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
				__UNLOCK(hperh);
				return ERROR;
			}
			else {
				__UNLOCK(hperh);
				return TIMEOUT;
			}
		}

		hperh->perh->TXDR.Word = I2C_MEM_ADD_MSB(mem_addr);
	}

	while(!I2C_GET_FLAG(hperh, I2C_FLAG_TXE));

	if (I2C_GET_FLAG(hperh, I2C_FLAG_TC)) {
		I2C_CLEAR_FLAG(hperh, I2C_FLAG_TC); 
	}

	if (!I2C_GET_FLAG(hperh, I2C_FLAG_RXE)) {
		uint8_t tmp = hperh->perh->RXDR.RXDATA;
	}

	if (size <= 0xff) {
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	hperh->perh->CR2.RD_WRN = 1;
	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	while (size > 0) {	
		if (i2c_wait_rxne_to_timeout(hperh, timeout) != OK) {
			if (hperh->error_code == I2C_ERROR_TIMEOUT) {
				__UNLOCK(hperh);
				SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
				return TIMEOUT;
			}
			else {
				__UNLOCK(hperh);
				SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
				return ERROR;
			}
		}

		(*buf++) = hperh->perh->RXDR.RXDATA; 
		size--;
	}

	if ((size == 0) || I2C_GET_FLAG(hperh, I2C_FLAG_TC) ) {
		if (!(hperh->perh->CR2.AUTOEND))
			SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
		hperh->state = I2C_STATE_READY;
		__UNLOCK(hperh);

		return OK;
	}
 
	return ERROR;
}

/**
  * @brief  Write an amount of data in non-blocking mode with Interrupt to a specific memory address
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  dev_addr: Target device address
  * @param  mem_addr: Internal memory address
  * @param  add_size: size of internal memory address
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
   * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_mem_write_by_it(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr,
                              i2c_addr_size_t add_size, uint8_t *buf, uint16_t size)
{
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);

	assert_param(IS_I2C_MEMADD_size(add_size));

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_TX;
	hperh->mode       = I2C_MODE_MEM;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	if (size <= 0xff) {
		if (add_size == I2C_MEMADD_SIZE_8BIT) {
			hperh->perh->CR2.NBYTES = size + 1;
		}
		else {
			hperh->perh->CR2.NBYTES = size + 2;
		}
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	if (!I2C_GET_FLAG(hperh, I2C_FLAG_TXE)) {
		__I2C_DISABLE(hperh);

		__NOP();
		__NOP();

		__I2C_ENABLE(hperh);
	}
	
	if (i2c_req_mem_write(hperh, dev_addr, mem_addr, add_size, I2C_TIMEOUT_FLAG) != OK) {
		if (hperh->error_code == I2C_ERROR_AF) {
			__UNLOCK(hperh);
			return ERROR;
		}
		else {
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}

	__UNLOCK(hperh);

	/* Note : The I2C interrupts must be enabled after unlocking current process
	 *        to avoid the risk of I2C interrupt handle execution before current
	 *        process unlock */
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_TXTH))
		I2C_CLEAR_IT(hperh , I2C_IT_TXTH);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_NACK))
		I2C_CLEAR_IT(hperh , I2C_IT_NACK);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_TC))
		I2C_CLEAR_IT(hperh , I2C_IT_TC);	

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);
	hperh->perh->TXDR.TXDATA = (*hperh->p_buff++);
	hperh->xfer_count--;

	I2C_DISABLE_IT(hperh, 0xFFFFFFFF);
	I2C_ENABLE_IT(hperh, I2C_IT_TXTH | I2C_IT_NACK | I2C_IT_TC);	

	__UNLOCK(hperh);
	return OK;
}

/**
  * @brief  Read an amount of data in non-blocking mode with Interrupt from a specific memory address
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  dev_addr: Target device address
  * @param  mem_addr: Internal memory address
  * @param  add_size: size of internal memory address
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
   * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_mem_read_by_it(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr,
                             i2c_addr_size_t add_size, uint8_t *buf, uint16_t size)
{
	uint8_t tmp = 0;
	if (hperh->perh->CR1.PE == DISABLE)
		__I2C_ENABLE(hperh);

	assert_param(IS_I2C_MEMADD_size(add_size));

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_RX;
	hperh->mode       = I2C_MODE_MEM;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	if (i2c_req_mem_read(hperh, dev_addr, mem_addr, add_size, I2C_TIMEOUT_FLAG) != OK) {
		if (hperh->error_code == I2C_ERROR_AF) {
			__UNLOCK(hperh);
			return ERROR;
		}
		else {
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}
	if (size <= 0xff) {
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	hperh->perh->CR2.RD_WRN = 1;
	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);
	
	__UNLOCK(hperh);

	/* Note : The I2C interrupts must be enabled after unlocking current process
	 *        to avoid the risk of I2C interrupt handle execution before current
	 *        process unlock */
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_RXTH))
		I2C_CLEAR_IT(hperh , I2C_IT_RXTH);
	if (I2C_GET_IT_SOURCE(hperh , I2C_IT_TC))
		I2C_CLEAR_IT(hperh , I2C_IT_TC);	

	I2C_DISABLE_IT(hperh, 0xFFFFFFFF);
	I2C_ENABLE_IT(hperh, I2C_IT_RXTH | I2C_IT_TC);	
	tmp = hperh->perh->RXDR.RXDATA;

	return OK;
}

#ifdef HAL_DMA
/**
  * @brief  Write an amount of data in non-blocking mode with DMA to a specific memory address
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  dev_addr: Target device address
  * @param  mem_addr: Internal memory address
  * @param  add_size: size of internal memory address
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be sent
  * @param  channel: DMA channel
   * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_mem_write_by_dma(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, i2c_addr_size_t add_size,
                               uint8_t *buf, uint16_t size, uint8_t channel)
{
	uint8_t data[0xff] = {0};
	
	if (strcpy((char *)&data[2], (const char *)buf) != NULL) {
		data[0] = I2C_MEM_ADD_MSB(mem_addr);
		data[1] = I2C_MEM_ADD_LSB(mem_addr);
	}

	assert_param(IS_I2C_MEMADD_size(add_size));

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);
	
	hperh->state      = I2C_STATE_BUSY_TX;
	hperh->mode       = I2C_MODE_MASTER;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	if (hperh->hdmatx.perh == NULL)
		hperh->hdmatx.perh = DMA0;
	
	hperh->hdmatx.cplt_cbk = i2c_dma_master_send_cplt;
	hperh->hdmatx.cplt_arg = hperh;
	hperh->hdmatx.err_cbk  = i2c_dma_error;
	hperh->hdmatx.err_arg  = hperh;

	dma_config_struct(&hperh->hdmatx.config);
	hperh->hdmatx.config.burst      = ENABLE;
	if (add_size == I2C_MEMADD_SIZE_16BIT) {
		hperh->hdmatx.config.src  = (void *)data;
		hperh->hdmatx.config.size = size + 2;
	}
	else {
		hperh->hdmatx.config.src  = (void *)&data[1];
		hperh->hdmatx.config.size = size + 1;
	}
	hperh->hdmatx.config.dst     	= (void *)&hperh->perh->TXDR.Word;
	hperh->hdmatx.config.data_width = DMA_DATA_SIZE_BYTE;
	hperh->hdmatx.config.src_inc 	= DMA_DATA_INC_BYTE;
	hperh->hdmatx.config.dst_inc 	= DMA_DATA_INC_NONE;
	hperh->hdmatx.config.msel    	= hperh->perh == I2C0 ? DMA_MSEL_I2C0 : DMA_MSEL_I2C1;
	hperh->hdmatx.config.msigsel 	= DMA_MSIGSEL_I2C_TXEMPTY;
	hperh->hdmatx.config.channel 	= channel;
	dma_config_basic(&hperh->hdmatx);

	if (size <= 0xff) {	
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}

	SET_BIT(hperh->perh->CR1.Word, I2C_CR1_TXDMAEN);
	__I2C_ENABLE(hperh);

	if (i2c_master_req_write(hperh, dev_addr, I2C_TIMEOUT_FLAG) != OK) {
		__UNLOCK(hperh);
		return ERROR;
	}
	
	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Reads an amount of data in non-blocking mode with DMA from a specific memory address.
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @param  dev_addr: Target device address
  * @param  mem_addr: Internal memory address
  * @param  add_size: size of internal memory address
  * @param  buf: Pointer to data buffer
  * @param  size: Amount of data to be read
  * @param  channel: DMA channel
   * @retval Status, see @ref hal_status_t.
  */
hal_status_t i2c_mem_read_by_dma(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, i2c_addr_size_t add_size,
                              uint8_t *buf, uint16_t size, uint8_t channel)
{
	assert_param(IS_I2C_MEMADD_size(add_size));

	if (hperh->state != I2C_STATE_READY)
		return BUSY;

	if ((buf == NULL) || (size == 0))
		return  ERROR;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
		return BUSY;

	__LOCK(hperh);

	hperh->state      = I2C_STATE_BUSY_RX;
	hperh->mode       = I2C_MODE_MEM;
	hperh->error_code = I2C_ERROR_NONE;
	hperh->p_buff     = buf;
	hperh->xfer_size  = size;
	hperh->xfer_count = size;

	if (hperh->hdmarx.perh == NULL)
		hperh->hdmarx.perh = DMA0;

	hperh->hdmarx.cplt_cbk = i2c_dma_mem_recv_cplt;
	hperh->hdmarx.cplt_arg = (void *)hperh;
	hperh->hdmarx.err_cbk  = i2c_dma_error;
	hperh->hdmarx.err_arg  = (void *)hperh;

	dma_config_struct(&hperh->hdmarx.config);
	hperh->hdmatx.config.burst      = ENABLE;
	hperh->hdmarx.config.src     	= (void *)&hperh->perh->RXDR.Word;
	hperh->hdmarx.config.dst     	= (void *)buf;
	hperh->hdmarx.config.data_width	= DMA_DATA_SIZE_BYTE;
	hperh->hdmarx.config.size	= size;
	hperh->hdmarx.config.src_inc 	= DMA_DATA_INC_NONE;
	hperh->hdmarx.config.dst_inc 	= DMA_DATA_INC_BYTE;
	hperh->hdmarx.config.msel    	= hperh->perh == I2C0 ? DMA_MSEL_I2C0 : DMA_MSEL_I2C1;
	hperh->hdmarx.config.msigsel 	= DMA_MSIGSEL_I2C_RNR;
	hperh->hdmarx.config.channel 	= channel;
	dma_config_basic(&hperh->hdmarx);

	SET_BIT(hperh->perh->CR1.Word, I2C_CR1_RXDMAEN);
	__I2C_ENABLE(hperh);

	if (i2c_req_mem_read(hperh, dev_addr, mem_addr, add_size, I2C_TIMEOUT_FLAG) != OK) {
		if (hperh->error_code == I2C_ERROR_AF) {
			__UNLOCK(hperh);
			return ERROR;
		}
		else {
			__UNLOCK(hperh);
			return TIMEOUT;
		}
	}

	if (size <= 0xff) {
		hperh->perh->CR2.NBYTES = size;
	}
	else {
		hperh->perh->CR2.NBYTES = 0xff;
	}
	hperh->perh->CR2.RD_WRN = 1;
	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	__UNLOCK(hperh);

	return OK;
}
#endif

///**
//  * @brief  Checks if target device is ready for communication.
//  * @note   This function is used with Memory devices
//  * @param  hperh: Pointer to a i2c_handle_t structure that contains
//  *                the configuration information for the specified I2C.
//  * @param  dev_addr: Target device address
//  * @param  trials: Number of trials
//  * @param  timeout: Timeout duration
//   * @retval Status, see @ref hal_status_t.
//  */
//hal_status_t i2c_is_device_ready(i2c_handle_t *hperh, uint16_t dev_addr, uint32_t trials, uint32_t timeout)
//{
//	uint32_t tickstart = 0;
//	uint32_t tmp1 = 0;
//	uint32_t tmp2 = 0;
//	uint32_t tmp3 = 0;
//	uint32_t I2C_Trials = 1;

//	if (hperh->state != I2C_STATE_READY)
//		return BUSY;

//	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG) != OK)
//		return BUSY;

//	__LOCK(hperh);
//	CLEAR_BIT(hperh->perh->CR1.Word, I2C_CR1_POS);

//	hperh->state = I2C_STATE_BUSY;
//	hperh->error_code = I2C_ERROR_NONE;

//	do {
//		SET_BIT(hperh->perh->CR1.Word, I2C_CR1_START);

//		if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_SB, RESET, timeout) != OK) {
//			__UNLOCK(hperh);
//			return TIMEOUT;
//		}

//		hperh->perh->DR.Word = I2C_7BIT_ADD_WRITE(dev_addr);
//		tickstart            = __get_tick();
//		tmp1 		     = I2C_GET_FLAG(hperh, I2C_FLAG_ADDR);
//		tmp2 		     = I2C_GET_FLAG(hperh, I2C_FLAG_AF);
//		tmp3 		     = hperh->state;

//		while ((tmp1 == RESET) && (tmp2 == RESET) && (tmp3 != I2C_STATE_TIMEOUT)) {
//			if ((timeout == 0) || ((__get_tick() - tickstart ) > timeout))
//				hperh->state = I2C_STATE_TIMEOUT;

//			tmp1 = I2C_GET_FLAG(hperh, I2C_FLAG_ADDR);
//			tmp2 = I2C_GET_FLAG(hperh, I2C_FLAG_AF);
//			tmp3 = hperh->state;
//		}
//		hperh->state = I2C_STATE_READY;

//		if (I2C_GET_FLAG(hperh, I2C_FLAG_ADDR) == SET) {
//			SET_BIT(hperh->perh->CR1.Word, I2C_CR1_STOP);
//			I2C_CLEAR_ADDRFLAG(hperh);

//			if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET,
//							I2C_TIMEOUT_BUSY_FLAG) != OK) {
//				__UNLOCK(hperh);
//				return TIMEOUT;
//			}

//			hperh->state = I2C_STATE_READY;
//			__UNLOCK(hperh);
//			return OK;
//		}
//		else {
//			SET_BIT(hperh->perh->CR1.Word, I2C_CR1_STOP);
//			I2C_CLEAR_FLAG(hperh, I2C_FLAG_AF);

//			if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_BUSY, SET,
//							I2C_TIMEOUT_BUSY_FLAG) != OK) {
//				__UNLOCK(hperh);
//				return TIMEOUT;
//			}
//		}
//	}while (I2C_Trials++ < trials);

//	hperh->state = I2C_STATE_READY;
//	__UNLOCK(hperh);
//	return OK;
//}
///**
// * @}
// */

///** @defgroup I2C_Public_Functions_Group3 Peripheral state and Errors functions
// *  @brief   Peripheral state and Errors functions
// *
//@verbatim
// ===============================================================================
//            ##### Peripheral state and Errors functions #####
// ===============================================================================
//    [..]
//    This subsection permits to get in run-time the status of the peripheral
//    and the data flow.

//@endverbatim
// * @{
// */

///**
// * @brief  Return the I2C handle state.
// * @param  hperh: Pointer to a i2c_handle_t structure that contains
// *                the configuration information for the specified I2C.
// * @retval hal_status_t state
// */
//i2c_state_t i2c_get_state(i2c_handle_t *hperh)
//{
//	return hperh->state;
//}

///**
// * @brief  Return the I2C error code.
// * @param  hperh: Pointer to a i2c_handle_t structure that contains
// *                the configuration information for the specified I2C.
// * @retval I2C Error Code
// */
//uint32_t i2c_get_error(i2c_handle_t *hperh)
//{
//	return hperh->error_code;
//}
///**
// * @}
// */

///** @defgroup I2C_Public_Functions_Group4 IRQ Handler and Callbacks
// * @{
// */

/**
  * @brief  This function handles I2C event interrupt request.
  * @param  hperh: Pointer to a i2c_handle_t structure that contains
  *                the configuration information for the specified I2C.
  * @retval None
  */
void i2c_ev_irq_handler(i2c_handle_t *hperh)
{
	uint32_t tmp1 = 0;
	uint32_t tmp2 = 0;
	uint32_t tmp3 = 0;
	/* Master mode selected */	
	if ((hperh->mode == I2C_MODE_MASTER) || (hperh->mode == I2C_MODE_MEM)) {
		/* I2C in mode Trismmit */
		if (I2C_GET_DIR(hperh, I2C_CR2_RD_WRN) != SET) {
			tmp1 = I2C_GET_IT_SOURCE(hperh, I2C_IT_TXTH);
			tmp2 = I2C_GET_IT_SOURCE(hperh, I2C_IT_TC);
			tmp3 = I2C_GET_IT_SOURCE(hperh, I2C_IT_NACK);
			
			if (tmp1 || tmp2) {
				if (hperh->perh->CR2.AUTOEND == 0){
					i2c_master_send_btf(hperh);
				}
				else {				
					i2c_master_send_txe(hperh);
				}
			}

			if (tmp3) {
				hperh->error_code |= I2C_ERROR_AF;
				I2C_CLEAR_IT(hperh, I2C_IT_NACK);
				hperh->state = I2C_STATE_READY;
				
				if (!(hperh->perh->CR2.AUTOEND))
					SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);				
			}
		}
		/* I2C in mode Receiver */
		else {
			tmp1 = I2C_GET_IT_SOURCE(hperh, I2C_IT_RXTH);
			tmp2 = I2C_GET_IT_SOURCE(hperh, I2C_IT_TC);

			if (tmp1 || tmp2) {
				if (hperh->perh->CR2.AUTOEND == 0){
					i2c_master_recv_btf(hperh);
				}
				else {				
					i2c_master_recv_rxne(hperh);
				}
			}
		}
	}

	/* Slave mode selected */
	else {
		tmp1 = I2C_GET_IT_SOURCE(hperh, I2C_IT_ADDR);
		tmp2 = I2C_GET_IT_SOURCE(hperh, I2C_IT_STOP);
		tmp3 = I2C_GET_FLAG(hperh, I2C_FLAG_DIR);

		if (tmp1) {
			i2c_slave_addr(hperh);
		}
		else if (tmp2) {
			i2c_slave_stopf(hperh);
		}
		/* I2C in mode Transmitter */
		if (tmp3) {
			tmp1 = I2C_GET_IT_SOURCE(hperh, I2C_IT_TXTH);
			tmp2 = I2C_GET_IT_SOURCE(hperh, I2C_IT_NACK);
			
			if (tmp1)
				i2c_slave_send_txe(hperh);
			if (tmp2) {
				hperh->error_code |= I2C_ERROR_AF;
				hperh->state = I2C_STATE_READY;
				I2C_CLEAR_IT(hperh, I2C_IT_NACK);
			}
		}
		/* I2C in mode Receiver */
		else {
			tmp1 = I2C_GET_IT_SOURCE(hperh, I2C_IT_RXTH);

			if (tmp1)
				i2c_slave_recv_rxne(hperh);
		}
	}
}

///**
//  * @brief  This function handles I2C error interrupt request.
//  * @param  hperh: pointer to a i2c_handle_t structure that contains
//  *         the configuration information for I2C module
//  * @retval  NONE
//  */
//void i2c_er_irq_handler(i2c_handle_t *hperh)
//{
//	uint32_t tmp1 = 0;
//	uint32_t tmp2 = 0;
//	uint32_t tmp3 = 0;

//	tmp1 = I2C_GET_FLAG(hperh, I2C_FLAG_BERR);
//	tmp2 = I2C_GET_IT_SOURCE(hperh, I2C_IT_ERR);

//	/* I2C Bus error interrupt occurred */
//	if ((tmp1 == SET) && (tmp2 == SET)) {
//		hperh->error_code |= I2C_ERROR_BERR;
//		I2C_CLEAR_FLAG(hperh, I2C_FLAG_BERR);
//		SET_BIT(hperh->perh->CR1.Word, I2C_CR1_SWRST);
//	}

//	tmp1 = I2C_GET_FLAG(hperh, I2C_FLAG_ARLO);
//	tmp2 = I2C_GET_IT_SOURCE(hperh, I2C_IT_ERR);

//	/* I2C Arbitration Loss error interrupt occurred */
//	if ((tmp1 == SET) && (tmp2 == SET)) {
//		hperh->error_code |= I2C_ERROR_ARLO;
//		I2C_CLEAR_FLAG(hperh, I2C_FLAG_ARLO);
//	}

//	tmp1 = I2C_GET_FLAG(hperh, I2C_FLAG_AF);
//	tmp2 = I2C_GET_IT_SOURCE(hperh, I2C_IT_ERR);

//	/* I2C Acknowledge failure error interrupt occurred */
//	if ((tmp1 == SET) && (tmp2 == SET)) {
//		tmp1 = hperh->mode;
//		tmp2 = hperh->xfer_count;
//		tmp3 = hperh->state;
//		if ((tmp1 == I2C_MODE_SLAVE) && (tmp2 == 0) && \
//		   (tmp3 == I2C_STATE_BUSY_TX)) {
//			i2c_slave_af(hperh);
//		}
//		else {
//			hperh->error_code |= I2C_ERROR_AF;
//			SET_BIT(hperh->perh->CR1.Word,I2C_CR1_STOP);
//			I2C_CLEAR_FLAG(hperh, I2C_FLAG_AF);
//		}
//	}

//	tmp1 = I2C_GET_FLAG(hperh, I2C_FLAG_OVR);
//	tmp2 = I2C_GET_IT_SOURCE(hperh, I2C_IT_ERR);

//	/* I2C Over-Run/Under-Run interrupt occurred */
//	if ((tmp1 == SET) && (tmp2 == SET)) {
//		hperh->error_code |= I2C_ERROR_OVR;
//		I2C_CLEAR_FLAG(hperh, I2C_FLAG_OVR);
//	}

//	if (hperh->error_code != I2C_ERROR_NONE) {
//		hperh->state = I2C_STATE_READY;
//		CLEAR_BIT(hperh->perh->CR1.Word, I2C_CR1_POS);
//		if (hperh->error_callback)
//			hperh->error_callback(hperh);
//	}
//}
///**
// * @}
// */

///**
// * @}
// */

///** @addtogroup I2C_Private_Functions
// * @{
// */

/**
 * @brief  Handle TXE flag for Master Transmit mode
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_master_send_txe(i2c_handle_t *hperh)
{
	if (hperh->xfer_count == 0) {
		I2C_DISABLE_IT(hperh, I2C_IT_TXTH | I2C_IT_NACK);
		
		if (hperh->mode == I2C_MODE_MEM) {
			hperh->state = I2C_STATE_READY;
			if (hperh->mem_tx_cplt_cbk)
				hperh->mem_tx_cplt_cbk(hperh);
		}
		else {
			hperh->state = I2C_STATE_READY;
			if (hperh->master_tx_cplt_cbk)
				hperh->master_tx_cplt_cbk(hperh);
		}
	}
	else {
		I2C_CLEAR_IT(hperh, I2C_IT_TXTH);
		hperh->perh->TXDR.TXDATA = (*hperh->p_buff++);
		hperh->xfer_count--;
	}

	return OK;
}

/**
 * @brief  Handle BTF flag for Master Transmit mode
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_master_send_btf(i2c_handle_t *hperh)
{
	if (hperh->xfer_count != 0) {
		hperh->perh->TXDR.TXDATA = (*hperh->p_buff++);
		hperh->xfer_count--;
		
		I2C_CLEAR_IT(hperh, I2C_IT_TXTH);
	}
	else {
		I2C_CLEAR_IT(hperh, I2C_IT_TXTH | I2C_IT_NACK | I2C_IT_TC);
		I2C_DISABLE_IT(hperh, I2C_IT_TXTH | I2C_IT_NACK | I2C_IT_TC);
		SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
		if (hperh->mode == I2C_MODE_MEM) {
			hperh->state = I2C_STATE_READY;
			if (hperh->mem_tx_cplt_cbk)
				hperh->mem_tx_cplt_cbk(hperh);
		}
		else {
			hperh->state = I2C_STATE_READY;
			if (hperh->master_tx_cplt_cbk)
				hperh->master_tx_cplt_cbk(hperh);
		}
	}
	return OK;
}

/**
 * @brief  Handle RXNE flag for Master Receive mode
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_master_recv_rxne(i2c_handle_t *hperh)
{
	uint32_t tmp = 0;

	tmp = hperh->xfer_count;
	if (tmp > 0) {
		(*hperh->p_buff++) = hperh->perh->RXDR.RXDATA;
		hperh->xfer_count--;
		I2C_CLEAR_IT(hperh, I2C_IT_RXTH);
	}
	
	if (tmp == 0) {
		I2C_DISABLE_IT(hperh, I2C_IT_RXTH | I2C_IT_NACK);

		if (hperh->mode == I2C_MODE_MEM) {
			hperh->state = I2C_STATE_READY;
			if (hperh->mem_rx_cplt_cbk)
				hperh->mem_rx_cplt_cbk(hperh);
		}
		else {
			hperh->state = I2C_STATE_READY;
			if (hperh->master_rx_cplt_cbk)
				hperh->master_rx_cplt_cbk(hperh);
		}
	}

	return OK;
}

/**
 * @brief  Handle BTF flag for Master Receive mode
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_master_recv_btf(i2c_handle_t *hperh)
{	
	uint32_t tmp = 0;

	tmp = hperh->xfer_count;
	if (tmp > 0) {
		(*hperh->p_buff++) = hperh->perh->RXDR.RXDATA;
		hperh->xfer_count--;
		I2C_CLEAR_IT(hperh, I2C_IT_RXTH);
	}
	
	if ((tmp == 0) || (I2C_GET_IT_FLAG(hperh, I2C_IT_TC))) {
		I2C_DISABLE_IT(hperh, I2C_IT_RXTH | I2C_IT_NACK | I2C_IT_TC);
		SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
		if (hperh->mode == I2C_MODE_MEM) {
			hperh->state = I2C_STATE_READY;
			if (hperh->mem_rx_cplt_cbk)
				hperh->mem_rx_cplt_cbk(hperh);
		}
		else {
			hperh->state = I2C_STATE_READY;
			if (hperh->master_rx_cplt_cbk)
				hperh->master_rx_cplt_cbk(hperh);
		}
	}

	return OK;
}

/**
 * @brief  Handle TXE flag for Slave Transmit mode
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_slave_send_txe(i2c_handle_t *hperh)
{
	I2C_CLEAR_IT(hperh, I2C_IT_TXTH);

	if (hperh->xfer_count != 0) {
		hperh->perh->TXDR.TXDATA = (*hperh->p_buff++);
		--hperh->xfer_count;
	}

	return OK;
}

///**
// * @brief  Handle BTF flag for Slave Transmit mode
// * @param  hperh: Pointer to a i2c_handle_t structure that contains
// *                the configuration information for the specified I2C.
//  * @retval Status, see @ref hal_status_t.
// */
//static hal_status_t i2c_slave_send_btf(i2c_handle_t *hperh)
//{
//	if (hperh->xfer_count != 0) {
//		hperh->perh->DR.Word = (*hperh->p_buff++);
//		--hperh->xfer_count;
//	}
//	return OK;
//}

/**
 * @brief  Handle RXNE flag for Slave Receive mode
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_slave_recv_rxne(i2c_handle_t *hperh)
{
	if (hperh->xfer_count != 0) {
		(*hperh->p_buff++) = hperh->perh->RXDR.RXDATA;
		--hperh->xfer_count;
	}
	else {
		hperh->state = I2C_STATE_READY;
	}
//	I2C_CLEAR_IT(hperh, I2C_IT_RXNE);
	I2C_CLEAR_IT(hperh, I2C_IT_RXTH);
	return OK;
}

///**
// * @brief  Handle BTF flag for Slave Receive mode
// * @param  hperh: Pointer to a i2c_handle_t structure that contains
// *                the configuration information for the specified I2C.
//  * @retval Status, see @ref hal_status_t.
// */
//static hal_status_t i2c_slave_recv_btf(i2c_handle_t *hperh)
//{
//	if (hperh->xfer_count != 0) {
//		(*hperh->p_buff++) = hperh->perh->DR.Word;
//		--hperh->xfer_count;
//	}
//	return OK;
//}

/**
 * @brief  Handle ADD flag for Slave
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_slave_addr(i2c_handle_t *hperh)
{
	I2C_CLEAR_IT(hperh, I2C_IT_ADDR);

	return OK;
}

/**
 * @brief  Handle STOPF flag for Slave mode
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_slave_stopf(i2c_handle_t *hperh)
{
//	I2C_DISABLE_IT(hperh, I2C_IT_ADDR | I2C_IT_NACK | I2C_IT_RXNE | I2C_IT_STOP);
	I2C_DISABLE_IT(hperh, I2C_IT_ADDR | I2C_IT_NACK | I2C_IT_RXF | I2C_IT_STOP);
	I2C_CLEAR_IT(hperh, I2C_IT_STOP);

	hperh->state = I2C_STATE_READY;

	if (hperh->slave_rx_cplt_cbk)
		hperh->slave_rx_cplt_cbk(hperh);

	return OK;
}

///**
// * @brief  Handle Acknowledge Failed for Slave mode
// * @param  hperh: Pointer to a i2c_handle_t structure that contains
// *                the configuration information for the specified I2C.
//  * @retval Status, see @ref hal_status_t.
// */
//static hal_status_t i2c_slave_af(i2c_handle_t *hperh)
//{
//	I2C_DISABLE_IT(hperh, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR);
//	I2C_CLEAR_FLAG(hperh, I2C_FLAG_AF);

//	CLEAR_BIT(hperh->perh->CR1.Word, I2C_CR1_ACK);
//	hperh->state = I2C_STATE_READY;

//	if (hperh->slave_tx_cplt_cbk)
//		hperh->slave_tx_cplt_cbk(hperh);

//	return OK;
//}

/**
 * @brief  Master sends target device address followed by internal memory address for write request.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  dev_addr: Target device address
 * @param  timeout: Timeout duration
 * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_master_req_write(i2c_handle_t *hperh, uint16_t dev_addr, uint32_t timeout)
{
	if (hperh->init.addressing_mode == I2C_ADDRESSINGMODE_7BIT) {
		hperh->perh->CR2.ADD10  = I2C_ADDRESSINGMODE_7BIT;
		hperh->perh->CR2.SADDL  = (dev_addr >> 1);
		hperh->perh->CR2.RD_WRN = 0;
		
		__NOP();
		__NOP();
		__NOP();

		if (hperh->perh->CR2.SADDL != (dev_addr >> 1)) {
			return ERROR;
		}
	}
	else {
		hperh->perh->CR2.ADD10 = I2C_ADDRESSINGMODE_10BIT;
		
		hperh->perh->CR2.SADD0  = dev_addr & 0x01;
		hperh->perh->CR2.SADDL  = (dev_addr >> 1) & 0x7F;
		hperh->perh->CR2.SADDH  = (dev_addr >> 8) & 0x03;
		hperh->perh->CR2.RD_WRN = 0;

		__NOP();
		__NOP();
		__NOP();	

		if (((hperh->perh->CR2.SADD0) != (dev_addr & 0x01)) || ((hperh->perh->CR2.SADDL) != ((dev_addr >> 1) & 0x7F)) \
			|| ((hperh->perh->CR2.SADDH) != ((dev_addr >> 8) & 0x03))) {
				
			return ERROR;
		}
	}

	return OK;
}

/**
 * @brief  Master sends target device address followed by internal memory address for read request.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  dev_addr: Target device address
 * @param  timeout: Timeout duration
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_master_req_read(i2c_handle_t *hperh, uint16_t dev_addr, uint32_t timeout)
{

	if (hperh->init.addressing_mode == I2C_ADDRESSINGMODE_7BIT) {
		hperh->perh->CR2.ADD10  = I2C_ADDRESSINGMODE_7BIT;
		hperh->perh->CR2.SADDL  = (dev_addr >> 1);
		hperh->perh->CR2.RD_WRN = 1;
		
		__NOP();
		__NOP();
		__NOP();

		if (hperh->perh->CR2.SADDL != (dev_addr >> 1)) {
			return ERROR;
		}
	}
	else {
		hperh->perh->CR2.ADD10 = I2C_ADDRESSINGMODE_10BIT;
		
		hperh->perh->CR2.SADD0  = dev_addr & 0x01;
		hperh->perh->CR2.SADDL  = (dev_addr >> 1) & 0x7F;
		hperh->perh->CR2.SADDH  = (dev_addr >> 8) & 0x03;
		hperh->perh->CR2.RD_WRN = 1;
		
		__NOP();
		__NOP();
		__NOP();	

		if (((hperh->perh->CR2.SADD0) != (dev_addr & 0x01)) || ((hperh->perh->CR2.SADDL) != ((dev_addr >> 1) & 0x7F)) \
			|| ((hperh->perh->CR2.SADDH) != ((dev_addr >> 8) & 0x03))) {
				
			return ERROR;
		}
	}	
	return OK;
}

/**
 * @brief  Master sends target device address followed by internal memory address for write request.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  dev_addr: Target device address
 * @param  mem_addr: Internal memory address
 * @param  add_size: size of internal memory address
 * @param  timeout: Timeout duration
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_req_mem_write(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, uint16_t add_size, uint32_t timeout)
{	
	if (i2c_master_req_write(hperh, dev_addr, timeout) != OK) {
		__UNLOCK(hperh);
		return ERROR;
	}

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	if (add_size == I2C_MEMADD_SIZE_8BIT) {
		hperh->perh->TXDR.Word = I2C_MEM_ADD_LSB(mem_addr);
	}
	else {
		hperh->perh->TXDR.Word = I2C_MEM_ADD_MSB(mem_addr);

		if (i2c_wait_txe_to_timeout(hperh, timeout) != OK) {
			if(hperh->error_code == I2C_ERROR_AF) {
				SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
				return ERROR;
			}
			else {
				return TIMEOUT;
			}
		}

		hperh->perh->TXDR.Word = I2C_MEM_ADD_LSB(mem_addr);
	}
	
	while(!I2C_GET_FLAG(hperh, I2C_FLAG_TXE));

	return OK;
}

/**
 * @brief  Master sends target device address followed by internal memory address for read request.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  dev_addr: Target device address
 * @param  mem_addr: Internal memory address
 * @param  add_size: size of internal memory address
 * @param  timeout: Timeout duration
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_req_mem_read(i2c_handle_t *hperh, uint16_t dev_addr, uint16_t mem_addr, uint16_t add_size, uint32_t timeout)
{	
	if (add_size == I2C_MEMADD_SIZE_8BIT) {
		hperh->perh->CR2.NBYTES =  1;
	}
	else {
		hperh->perh->CR2.NBYTES =  2;
	}

	if (i2c_master_req_write(hperh, dev_addr, timeout) != OK) {
		__UNLOCK(hperh);
		return ERROR;
	}
	
	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_START);

	if (add_size == I2C_MEMADD_SIZE_8BIT) {
		hperh->perh->TXDR.Word = I2C_MEM_ADD_LSB(mem_addr);
	}
	else {
		hperh->perh->RXDR.Word = I2C_MEM_ADD_MSB(mem_addr);

		if (i2c_wait_txe_to_timeout(hperh, timeout) != OK) {
			if (hperh->error_code == I2C_ERROR_AF) {
				SET_BIT(hperh->perh->CR1.Word,I2C_CR2_STOP);
				return ERROR;
			}
			else {
				return TIMEOUT;
			}
		}
		hperh->perh->TXDR.Word = I2C_MEM_ADD_LSB(mem_addr);
	}

	while(!I2C_GET_FLAG(hperh, I2C_FLAG_TXE));

	return OK;
}

#ifdef HAL_DMA
/**
* @brief  DMA I2C master transmit process complete callback.
* @param  argv: I2C handle
* @retval None
*/
static void i2c_dma_master_send_cplt(void *argv)
{
	i2c_handle_t* hperh = (i2c_handle_t*)argv;

	if (i2c_wait_flag_to_timeout(hperh, I2C_FLAG_TXE, RESET, I2C_TIMEOUT_FLAG) != OK)
		hperh->error_code |= I2C_ERROR_TIMEOUT;

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
	CLEAR_BIT(hperh->perh->CR1.Word, I2C_CR1_TXDMAEN);

	hperh->xfer_count = 0;
	hperh->state      = I2C_STATE_READY;
	hperh->mode       = I2C_MODE_NONE;

	if (hperh->error_code != I2C_ERROR_NONE) {
		if (hperh->error_callback)
			hperh->error_callback(hperh);
	}
	else {
		if (hperh->master_tx_cplt_cbk)
			hperh->master_tx_cplt_cbk(hperh);
	}
}

/**
 * @brief  DMA I2C slave transmit process complete callback.
 * @param  argv: I2C handle
 * @retval None
 */
static void i2c_dma_slave_send_cplt(void *argv)
{
	i2c_handle_t* hperh = (i2c_handle_t*)argv;

	if (i2c_wait_stop_to_timeout(hperh, I2C_TIMEOUT_FLAG) != OK) {
		if (hperh->error_code == I2C_ERROR_AF)
			hperh->error_code |= I2C_ERROR_AF;
		else
			hperh->error_code |= I2C_ERROR_TIMEOUT;
	}

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_NACK))
		I2C_CLEAR_FCRFLAG(hperh, I2C_IT_NACK);

	if (I2C_GET_IT_FLAG(hperh, I2C_IT_STOP))
		I2C_CLEAR_FCRFLAG(hperh, I2C_IT_STOP);

	CLEAR_BIT(hperh->perh->CR1.Word, I2C_CR1_TXDMAEN);

	hperh->xfer_count = 0;
	hperh->state      = I2C_STATE_READY;
	hperh->mode       = I2C_MODE_NONE;

	if (hperh->error_code != I2C_ERROR_NONE) {
		if (hperh->error_callback)
			hperh->error_callback(hperh);
	}
	else {
		if (hperh->slave_tx_cplt_cbk)
			hperh->slave_tx_cplt_cbk(hperh);
	}
}

/**
 * @brief  DMA I2C master receive process complete callback
 * @param  argv: I2C handle
 * @retval None
 */
static void i2c_dma_master_recv_cplt(void *argv)
{
	i2c_handle_t* hperh = (i2c_handle_t*)argv;

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
	CLEAR_BIT(hperh->perh->CR1.Word, I2C_CR1_RXDMAEN);

	hperh->xfer_count = 0;
	hperh->state      = I2C_STATE_READY;
	hperh->mode       = I2C_MODE_NONE;

	if (hperh->error_code != I2C_ERROR_NONE) {
		if (hperh->error_callback)
			hperh->error_callback(hperh);
	}
	else {
		if (hperh->master_rx_cplt_cbk)
			hperh->master_rx_cplt_cbk(hperh);
	}
}

/**
 * @brief  DMA I2C slave receive process complete callback.
 * @param  argv: I2C handle
 * @retval None
 */
static void i2c_dma_slave_recv_cplt(void *argv)
{
	i2c_handle_t* hperh = (i2c_handle_t*)argv;

	if (i2c_wait_stop_to_timeout(hperh, I2C_TIMEOUT_FLAG) != OK) {
		if (hperh->error_code == I2C_ERROR_AF)
			hperh->error_code |= I2C_ERROR_AF;
		else
			hperh->error_code |= I2C_ERROR_TIMEOUT;
	}

	CLEAR_BIT(hperh->perh->CR1.Word, I2C_CR1_RXDMAEN);

	hperh->xfer_count = 0;
	hperh->state      = I2C_STATE_READY;
	hperh->mode       = I2C_MODE_NONE;

	if (hperh->error_code != I2C_ERROR_NONE) {
		if (hperh->error_callback)
			hperh->error_callback(hperh);
	}
	else {
		if (hperh->slave_rx_cplt_cbk)
			hperh->slave_rx_cplt_cbk(hperh);
	}
}

/**
 * @brief  DMA I2C Memory Write process complete callback
 * @param  argv: I2C handle
 * @retval None
 */
static void i2c_dma_mem_send_cplt(void *argv)
{
	i2c_handle_t* hperh = (i2c_handle_t*)argv;

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
	__I2C_DISABLE(hperh);
	CLEAR_BIT(hperh->perh->CR1.Word, I2C_CR1_TXDMAEN);

	hperh->xfer_count = 0;
	hperh->state      = I2C_STATE_READY;
	hperh->mode       = I2C_MODE_NONE;

	if (hperh->error_code != I2C_ERROR_NONE) {
		if (hperh->error_callback)
			hperh->error_callback(hperh);
	}
	else {
		if (hperh->mem_tx_cplt_cbk)
			hperh->mem_tx_cplt_cbk(hperh);
	}
}

/**
 * @brief  DMA I2C Memory Read process complete callback
 * @param  argv: I2C handle
 * @retval None
 */
static void i2c_dma_mem_recv_cplt(void *argv)
{
	i2c_handle_t* hperh = (i2c_handle_t*)argv;

	SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
	__I2C_DISABLE(hperh);
	CLEAR_BIT(hperh->perh->CR1.Word, I2C_CR1_RXDMAEN);

	hperh->xfer_count = 0;
	hperh->state = I2C_STATE_READY;
	hperh->mode = I2C_MODE_NONE;

	if (hperh->error_code != I2C_ERROR_NONE) {
		if (hperh->error_callback)
			hperh->error_callback(hperh);
	}
	else {
		if (hperh->mem_rx_cplt_cbk)
			hperh->mem_rx_cplt_cbk(hperh);
	}
}
#endif

///**
// * @brief  I2C Configuration Speed function
// * @param  hperh: Pointer to a i2c_handle_t structure that contains
// *                the configuration information for the specified I2C.
// * @param  i2c_clk: PCLK frequency from RCC.
// * @retval CCR Speed: Speed to set in I2C CCR Register
// */
//static uint32_t i2c_configure_speed(i2c_handle_t *hperh, uint32_t i2c_clk)
//{
//	uint32_t tmp1 = 0;

//	if (hperh->init.clock_speed <= I2C_STANDARD_MODE_MAX_CLK) {
//		tmp1 = (i2c_clk / (hperh->init.clock_speed << 1));
//		if ((tmp1 & I2C_CCR_CCR) < 4 )
//			return 4;
//		else
//			return tmp1;
//	}
//	else {
//		tmp1 = I2C_CCR_FS;

//		if (hperh->init.duty_cycle == I2C_DUTYCYCLE_2)
//			tmp1 |= (i2c_clk / (hperh->init.clock_speed * 3)) | I2C_DUTYCYCLE_2;
//		else
//			tmp1 |= (i2c_clk / (hperh->init.clock_speed * 25)) | I2C_DUTYCYCLE_16_9;

//		if ((tmp1 & I2C_CCR_CCR) < 1 )
//			return 1;
//		else
//			return tmp1;
//	}
//}

#ifdef HAL_DMA
/**
* @brief  DMA I2C communication error callback.
* @param  argv: I2C handle
* @retval None
*/
static void i2c_dma_error(void *argv)
{
 	i2c_handle_t* hperh = (i2c_handle_t*)argv;

 	hperh->xfer_count  = 0;
 	hperh->state       = I2C_STATE_READY;
 	hperh->mode        = I2C_MODE_NONE;
 	hperh->error_code |= I2C_ERROR_DMA;

 	if (hperh->error_callback)
 		hperh->error_callback(hperh);
}
#endif

/**
 * @brief  This function handles I2C Communication timeout.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  flag: specifies the I2C flag to check.
 * @param  status: The checked flag status (SET or RESET).
 * @param  timeout: Timeout duration
 * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_wait_flag_to_timeout(i2c_handle_t *hperh, uint32_t flag, flag_status_t status, uint32_t timeout)
{
	uint32_t tickstart = 0; 

	tickstart = __get_tick();
	if (status == RESET) {
		while (I2C_GET_FLAG(hperh, flag) == RESET) {
			if ((timeout == 0) || ((__get_tick() - tickstart ) > timeout)) {
				hperh->state = I2C_STATE_READY;
				__UNLOCK(hperh);
				return TIMEOUT;
			}
		}
	}
	else {
		while (I2C_GET_FLAG(hperh, flag) != RESET) {
			if ((timeout == 0) || ((__get_tick() - tickstart ) > timeout)) {
				hperh->state = I2C_STATE_READY;
				__UNLOCK(hperh);
				return TIMEOUT;
			}
		}
	}

	return OK;
}
   
/**
 * @brief  This function handles I2C Communication timeout for Master addressing phase.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  flag: specifies the I2C flag to check.
 * @param  timeout: Timeout duration
 * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_wait_master_addr_to_timeout(i2c_handle_t *hperh, uint32_t flag, uint32_t timeout)
{
	uint32_t tickstart = 0;

	tickstart = __get_tick();
	while (I2C_GET_IT_FLAG(hperh, flag) == RESET) {
		if (I2C_GET_IT_FLAG(hperh, I2C_IT_NACK) == SET) {
			SET_BIT(hperh->perh->CR2.Word, I2C_CR2_STOP);
			I2C_CLEAR_IT(hperh, I2C_IT_NACK);

			hperh->error_code = I2C_ERROR_AF;
			hperh->state = I2C_STATE_READY;
			__UNLOCK(hperh);
			return ERROR;
		}

		if (timeout != I2C_MAX_DELAY) {
			if ((timeout == 0) || ((__get_tick() - tickstart ) > timeout)) {
				hperh->state = I2C_STATE_READY;
				__UNLOCK(hperh);
				return TIMEOUT;
			}
		}
	}

	return OK;
}

/**
 * @brief  This function handles I2C Communication timeout for specific usage of TXE flag.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  timeout: Timeout duration
 * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_wait_txe_to_timeout(i2c_handle_t *hperh, uint32_t timeout)
{
	uint32_t tickstart = __get_tick();

	while (I2C_GET_FLAG(hperh, I2C_FLAG_TXE) == RESET) {
		if (I2C_GET_IT_FLAG(hperh, I2C_IT_ARLO)) {
			hperh->error_code |= I2C_ERROR_OVR;
			I2C_CLEAR_IT(hperh, I2C_IT_ARLO);
			hperh->state = I2C_STATE_READY;
			return ERROR;
		}
		
		if (i2c_is_ack_failed(hperh) != OK)
			return ERROR;

		if (timeout != I2C_MAX_DELAY) {
			if ((timeout == 0) || ((__get_tick()-tickstart) > timeout)) {
				hperh->error_code |= I2C_ERROR_TIMEOUT;
				hperh->state = I2C_STATE_READY;
				__UNLOCK(hperh);

				return TIMEOUT;
			}
		}
	}

	return OK;
}

///**
// * @brief  This function handles I2C Communication timeout for specific usage of BTF flag.
// * @param  hperh: Pointer to a i2c_handle_t structure that contains
// *                the configuration information for the specified I2C.
// * @param  timeout: Timeout duration
//  * @retval Status, see @ref hal_status_t.
// */
//static hal_status_t i2c_wait_btf_to_timeout(i2c_handle_t *hperh, uint32_t timeout)
//{
//	uint32_t tickstart = __get_tick();

//	while (I2C_GET_FLAG(hperh, I2C_FLAG_TC) == RESET) {
//		if (i2c_is_ack_failed(hperh) != OK) {
//			return ERROR;
//		}

//		if (timeout != I2C_MAX_DELAY) {
//			if ((timeout == 0) || ((__get_tick()-tickstart) > timeout)) {
//				hperh->error_code |= I2C_ERROR_TIMEOUT;
//				hperh->state = I2C_STATE_READY;
//				__UNLOCK(hperh);
//				return TIMEOUT;
//			}
//		}
//	}
//	return OK;
//}

/**
 * @brief  This function handles I2C Communication timeout for specific usage of STOP flag.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  timeout: Timeout duration
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_wait_stop_to_timeout(i2c_handle_t *hperh, uint32_t timeout)
{
	uint32_t tickstart = 0x00;
	tickstart = __get_tick();

	while (I2C_GET_IT_FLAG(hperh, I2C_IT_STOP) == RESET) {
		if (i2c_is_ack_failed(hperh) != OK) {
			hperh->error_code |= I2C_ERROR_AF;
			hperh->state = I2C_STATE_READY;
			return ERROR;
		}

		if ((timeout == 0) || ((__get_tick() - tickstart) > timeout)) {
			hperh->error_code |= I2C_ERROR_TIMEOUT;
			hperh->state = I2C_STATE_READY;
			__UNLOCK(hperh);

			return TIMEOUT;
		}
	}

	return OK;
}

/**
 * @brief  This function handles I2C Communication timeout for specific usage of RXNE flag.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
 * @param  timeout: Timeout duration
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_wait_rxne_to_timeout(i2c_handle_t *hperh, uint32_t timeout)
{
	uint32_t tickstart = 0x00;
	tickstart = __get_tick();

	while (I2C_GET_FLAG(hperh, I2C_FLAG_RXE) == SET) {
		if ((timeout == 0) || ((__get_tick() - tickstart) > timeout)) {
			hperh->error_code |= I2C_ERROR_TIMEOUT;
			hperh->state = I2C_STATE_READY;
			__UNLOCK(hperh);

			return TIMEOUT;
		}
	}
	
	return OK;
}

/**
 * @brief  This function handles Acknowledge failed detection during an I2C Communication.
 * @param  hperh: Pointer to a i2c_handle_t structure that contains
 *                the configuration information for the specified I2C.
  * @retval Status, see @ref hal_status_t.
 */
static hal_status_t i2c_is_ack_failed(i2c_handle_t *hperh)
{
	if (I2C_GET_IT_FLAG(hperh, I2C_IT_NACK) == SET) {
		I2C_CLEAR_IT(hperh, I2C_IT_NACK);
		hperh->error_code = I2C_ERROR_AF;
		hperh->state = I2C_STATE_READY;
		__UNLOCK(hperh);

		return ERROR;
	}

	return OK;
}
///**
// * @}
// */

//#endif /* HAL_I2C */
///**
// * @}
// */

///**
// * @}
// */
