/**
 *!
 * \file        b_hal_i2c.c
 * \version     v0.0.1
 * \date        2020/03/25
 * \author      Bean(notrynohigh@outlook.com)
 *******************************************************************************
 * @attention
 *
 * Copyright (c) 2020 Bean
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SUARTL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *******************************************************************************
 */
/*Includes ----------------------------------------------*/
#include "hal/inc/b_hal.h"
/**
 * \addtogroup B_HAL
 * \{
 */

/**
 * \addtogroup I2C
 * \{
 */

/**
 * \defgroup I2C_Private_TypesDefinitions
 */
typedef struct
{
    bHalGPIOInstance_t clk;
    bHalGPIOInstance_t sda;
    uint32_t           frq;
} bHalI2CIO_t;
/**
 * \}
 */

/**
 * \addtogroup I2C_Private_Functions
 * \{
 */
// 计算I2C延时微秒数值的函数
static uint32_t _HalCalculateI2CDelayUs(uint32_t i2cFrequency)
{
    if (i2cFrequency <= 100000)
    {
        // Standard Mode (100kHz)
        return 5;
    }
    else if (i2cFrequency <= 200000)
    {
        return 2;
    }
    else if (i2cFrequency <= 400000)
    {
        // Fast Mode (400kHz)
        return 1;
    }
    else if (i2cFrequency <= 1000000)
    {
        // Fast Mode Plus (1MHz)
        return 1;
    }
    else
    {
        return 1;
    }
}

static void _HalI2CIOStart(bHalI2CIO_t i2c)
{
    uint32_t us = _HalCalculateI2CDelayUs(i2c.frq);
    bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 1);
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 1);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 0);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 0);
    bHalDelayUs(us);
}

static void _HalI2CIOStop(bHalI2CIO_t i2c)
{
    uint32_t us = _HalCalculateI2CDelayUs(i2c.frq);
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 0);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 0);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 1);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 1);
    bHalDelayUs(us);
}

static int _HalI2CIOACK(bHalI2CIO_t i2c)
{
    int      retval = -1;
    uint32_t us     = _HalCalculateI2CDelayUs(i2c.frq);
    bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 1);
    bHalGpioConfig(i2c.sda.port, i2c.sda.pin, B_HAL_GPIO_INPUT, B_HAL_GPIO_NOPULL);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 1);
    bHalDelayUs(us);
    if (bHalGpioReadPin(i2c.sda.port, i2c.sda.pin))
    {
        retval = -1;
    }
    else
    {
        retval = 0;
    }
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 0);
    bHalGpioConfig(i2c.sda.port, i2c.sda.pin, B_HAL_GPIO_OUTPUT, B_HAL_GPIO_NOPULL);
    bHalDelayUs(us);

    return retval;
}

static void _HalI2CIOmACK(bHalI2CIO_t i2c)
{
    uint32_t us = _HalCalculateI2CDelayUs(i2c.frq);
    bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 0);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 1);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 0);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 1);
}

static void _HalI2CIOmNACK(bHalI2CIO_t i2c)
{
    uint32_t us = _HalCalculateI2CDelayUs(i2c.frq);
    bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 1);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 1);
    bHalDelayUs(us);
    bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 0);
    bHalDelayUs(us);
}

static void _HalI2CIOWriteByte(bHalI2CIO_t i2c, uint8_t dat)
{
    uint8_t  i  = 0;
    uint32_t us = _HalCalculateI2CDelayUs(i2c.frq);
    for (i = 0; i < 8; i++)
    {
        if (dat & 0x80)
        {
            bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 1);
        }
        else
        {
            bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 0);
        }
        bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 1);
        bHalDelayUs(us);
        bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 0);
        if (i == 7)
        {
            bHalGpioWritePin(i2c.sda.port, i2c.sda.pin, 1);
        }
        dat <<= 1;
        bHalDelayUs(us);
    }
}

static uint8_t _HalI2CIOReadByte(bHalI2CIO_t i2c, uint8_t mack)
{
    uint8_t  i = 0, tmp = 0;
    uint32_t us = _HalCalculateI2CDelayUs(i2c.frq);
    bHalGpioConfig(i2c.sda.port, i2c.sda.pin, B_HAL_GPIO_INPUT, B_HAL_GPIO_NOPULL);
    bHalDelayUs(us);
    for (i = 0; i < 8; i++)
    {
        tmp <<= 1;
        bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 1);
        bHalDelayUs(us);
        if (bHalGpioReadPin(i2c.sda.port, i2c.sda.pin))
        {
            tmp++;
        }
        bHalGpioWritePin(i2c.clk.port, i2c.clk.pin, 0);
        bHalDelayUs(us);
    }
    bHalGpioConfig(i2c.sda.port, i2c.sda.pin, B_HAL_GPIO_OUTPUT, B_HAL_GPIO_NOPULL);
    bHalDelayUs(us);
    if (mack == 0)
        _HalI2CIOmNACK(i2c);
    else
        _HalI2CIOmACK(i2c);

    return tmp;
}

static int _HalI2CIOWriteData(bHalI2CIO_t i2c, uint8_t dev, uint8_t *pdat, uint16_t len)
{
    uint16_t i;
    _HalI2CIOStart(i2c);
    _HalI2CIOWriteByte(i2c, dev);
    if (_HalI2CIOACK(i2c) < 0)
    {
        _HalI2CIOStop(i2c);
        return -1;
    }

    for (i = 0; i < len; i++)
    {
        _HalI2CIOWriteByte(i2c, pdat[i]);
        if (_HalI2CIOACK(i2c) < 0)
        {
            _HalI2CIOStop(i2c);
            return -1;
        }
    }

    _HalI2CIOStop(i2c);
    return 0;
}

static int _HalI2CIOReadData(bHalI2CIO_t i2c, uint8_t dev, uint8_t *pdat, uint16_t len)
{
    uint16_t i;
    _HalI2CIOStart(i2c);
    _HalI2CIOWriteByte(i2c, dev | 0x1);
    if (_HalI2CIOACK(i2c) < 0)
    {
        _HalI2CIOStop(i2c);
        return -1;
    }

    for (i = 0; i < (len - 1); i++)
    {
        *pdat = _HalI2CIOReadByte(i2c, 1);
        pdat++;
    }

    *pdat = _HalI2CIOReadByte(i2c, 0);
    _HalI2CIOStop(i2c);
    return 0;
}

static int _HalI2CIOReadBuff(bHalI2CIO_t i2c, uint8_t dev, uint16_t addr, uint8_t addr_size,
                             uint8_t *pdat, uint16_t len)
{
    _HalI2CIOStart(i2c);
    _HalI2CIOWriteByte(i2c, dev);
    if (_HalI2CIOACK(i2c) < 0)
    {
        _HalI2CIOStop(i2c);
        return -1;
    }

    if (addr_size > 1)
    {
        _HalI2CIOWriteByte(i2c, (uint8_t)((addr & 0xff00) >> 8));
        if (_HalI2CIOACK(i2c) < 0)
        {
            _HalI2CIOStop(i2c);
            return -1;
        }
    }
    _HalI2CIOWriteByte(i2c, (uint8_t)(addr & 0x00ff));
    if (_HalI2CIOACK(i2c) < 0)
    {
        _HalI2CIOStop(i2c);
        return -1;
    }

    _HalI2CIOStart(i2c);
    _HalI2CIOWriteByte(i2c, dev | 0x1);
    if (_HalI2CIOACK(i2c) < 0)
    {
        _HalI2CIOStop(i2c);
        return -1;
    }
    while (len-- > 1)
    {
        *pdat++ = _HalI2CIOReadByte(i2c, 1);
    }
    *pdat++ = _HalI2CIOReadByte(i2c, 0);

    _HalI2CIOStop(i2c);
    return 0;
}

static int _HalI2CIOWriteBuff(bHalI2CIO_t i2c, uint8_t dev, uint16_t addr, uint8_t addr_size,
                              const uint8_t *pdat, uint8_t len)
{
    uint32_t i = 0;
    _HalI2CIOStart(i2c);
    _HalI2CIOWriteByte(i2c, dev);
    if (_HalI2CIOACK(i2c) < 0)
    {
        _HalI2CIOStop(i2c);
        return -1;
    }

    if (addr_size > 1)
    {
        _HalI2CIOWriteByte(i2c, (uint8_t)((addr & 0xff00) >> 8));
        if (_HalI2CIOACK(i2c) < 0)
        {
            _HalI2CIOStop(i2c);
            return -1;
        }
    }
    _HalI2CIOWriteByte(i2c, (uint8_t)(addr & 0x00ff));
    if (_HalI2CIOACK(i2c) < 0)
    {
        _HalI2CIOStop(i2c);
        return -1;
    }

    for (i = 0; i < len; i++)
    {
        _HalI2CIOWriteByte(i2c, pdat[i]);
        if (_HalI2CIOACK(i2c) < 0)
        {
            _HalI2CIOStop(i2c);
            return -1;
        }
    }
    _HalI2CIOStop(i2c);
    return 0;
}

/**
 * \}
 */

/**
 * \addtogroup I2C_Exported_Functions
 * \{
 */
#if defined(__WEAKDEF)
__WEAKDEF int bMcuI2CReadByte(const bHalI2CIf_t *i2c_if, uint8_t *pbuf, uint16_t len)
{
    return -1;
}

__WEAKDEF int bMcuI2CWriteByte(const bHalI2CIf_t *i2c_if, uint8_t *pbuf, uint16_t len)
{
    return -1;
}

__WEAKDEF int bMcuI2CMemWrite(const bHalI2CIf_t *i2c_if, uint16_t mem_addr, uint8_t mem_addr_size,
                              const uint8_t *pbuf, uint16_t len)
{
    return -1;
}

__WEAKDEF int bMcuI2CMemRead(const bHalI2CIf_t *i2c_if, uint16_t mem_addr, uint8_t mem_addr_size,
                             uint8_t *pbuf, uint16_t len)
{
    return -1;
}
#endif
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------

int bHalI2CReadByte(const bHalI2CIf_t *i2c_if, uint8_t *pbuf, uint16_t len)
{
    int         retval = 0;
    bHalI2CIO_t simulating_iic;
    if (IS_NULL(i2c_if) || (IS_NULL(pbuf) && (len > 0)))
    {
        return -1;
    }
    if (i2c_if->is_simulation == 1)
    {
        simulating_iic.clk = i2c_if->_if.simulating_i2c.clk;
        simulating_iic.sda = i2c_if->_if.simulating_i2c.sda;
        simulating_iic.frq = i2c_if->_if.simulating_i2c.frq;
        retval             = _HalI2CIOReadData(simulating_iic, i2c_if->dev_addr, pbuf, len);
    }
    else
    {
        retval = bMcuI2CReadByte(i2c_if, pbuf, len);
    }
    return retval;
}

int bHalI2CWriteByte(const bHalI2CIf_t *i2c_if, uint8_t *pbuf, uint16_t len)
{
    int         retval = 0;
    bHalI2CIO_t simulating_iic;
    if (IS_NULL(i2c_if) || (IS_NULL(pbuf) && (len > 0)))
    {
        return -1;
    }
    if (i2c_if->is_simulation == 1)
    {
        simulating_iic.clk = i2c_if->_if.simulating_i2c.clk;
        simulating_iic.sda = i2c_if->_if.simulating_i2c.sda;
        simulating_iic.frq = i2c_if->_if.simulating_i2c.frq;
        retval             = _HalI2CIOWriteData(simulating_iic, i2c_if->dev_addr, pbuf, len);
    }
    else
    {
        retval = bMcuI2CWriteByte(i2c_if, pbuf, len);
    }
    return retval;
}

int bHalI2CMemWrite(const bHalI2CIf_t *i2c_if, uint16_t mem_addr, uint8_t mem_addr_size,
                    const uint8_t *pbuf, uint16_t len)
{
    int         retval = 0;
    bHalI2CIO_t simulating_iic;
    if (IS_NULL(i2c_if) || (IS_NULL(pbuf) && (len > 0)))
    {
        return -1;
    }
    if (i2c_if->is_simulation == 1)
    {
        simulating_iic.clk = i2c_if->_if.simulating_i2c.clk;
        simulating_iic.sda = i2c_if->_if.simulating_i2c.sda;
        simulating_iic.frq = i2c_if->_if.simulating_i2c.frq;
        retval = _HalI2CIOWriteBuff(simulating_iic, i2c_if->dev_addr, mem_addr, mem_addr_size, pbuf,
                                    len);
    }
    else
    {
        retval = bMcuI2CMemWrite(i2c_if, mem_addr, mem_addr_size, pbuf, len);
    }
    return retval;
}

int bHalI2CMemRead(const bHalI2CIf_t *i2c_if, uint16_t mem_addr, uint8_t mem_addr_size,
                   uint8_t *pbuf, uint16_t len)
{
    int         retval = 0;
    bHalI2CIO_t simulating_iic;
    if (IS_NULL(i2c_if) || (IS_NULL(pbuf) && (len > 0)))
    {
        return -1;
    }
    if (i2c_if->is_simulation == 1)
    {
        simulating_iic.clk = i2c_if->_if.simulating_i2c.clk;
        simulating_iic.sda = i2c_if->_if.simulating_i2c.sda;
        simulating_iic.frq = i2c_if->_if.simulating_i2c.frq;
        retval =
            _HalI2CIOReadBuff(simulating_iic, i2c_if->dev_addr, mem_addr, mem_addr_size, pbuf, len);
    }
    else
    {
        retval = bMcuI2CMemRead(i2c_if, mem_addr, mem_addr_size, pbuf, len);
    }
    return retval;
}


int bHalI2CAddressCheck(const bHalI2CIf_t *i2c_if)
{
    int         retval = 0;
	uint8_t 	dummy = i2c_if->dev_addr;
    bHalI2CIO_t simulating_iic;
    if (IS_NULL(i2c_if))
    {
        return -1;
    }
    if (i2c_if->is_simulation == 1)
    {
        simulating_iic.clk = i2c_if->_if.simulating_i2c.clk;
        simulating_iic.sda = i2c_if->_if.simulating_i2c.sda;
        simulating_iic.frq = i2c_if->_if.simulating_i2c.frq;
        retval             = _HalI2CIOWriteData(simulating_iic, i2c_if->dev_addr, &dummy, 0);
    }
    else
    {
        retval = bMcuI2CWriteByte(i2c_if, &dummy, 0);
    }
    return retval;
}
/**
 * \}
 */

/**
 * \}
 */

/**
 * \}
 */

/************************ Copyright (c) 2019 Bean *****END OF FILE****/