/**
 * @file i2c.cpp
 * @author zzy
 * @brief
 * @version 0.1
 * @date 2023-05-13
 *
 * @copyright Copyright (c) 2023
 *
 */

#include "i2c.h"

/**
 * @brief IIC类的构造函数
 *
 * @param scl_gpio_port SCL的GPIO组号
 * @param scl_pin SCL的GPIO端口号
 * @param sda_gpio_port SDA的GPIO组号
 * @param sda_pin SDA的GPIO端口号
 */
IIC::IIC(GPIO_TypeDef *scl_gpio_port, uint16_t scl_pin, GPIO_TypeDef *sda_gpio_port, uint16_t sda_pin)
{
    this->SCL_GPIO_PORT = scl_gpio_port;
    this->SCL_PIN = scl_pin;
    this->SDA_GPIO_PORT = sda_gpio_port;
    this->SDA_PIN = sda_pin;
}

/**
 * @brief 向SDA中写入0或1
 *
 * @param PinState
 */
void IIC::WriteSDA(GPIO_PinState PinState)
{
    HAL_GPIO_WritePin(this->SDA_GPIO_PORT, this->SDA_PIN, PinState);
}

/**
 * @brief 向SCL中写入0或1
 *
 * @param PinState
 */
void IIC::WriteSCL(GPIO_PinState PinState)
{
    HAL_GPIO_WritePin(this->SCL_GPIO_PORT, this->SCL_PIN, PinState);
}

/**
 * @brief 读取SDA的值
 *
 * @return GPIO_PinState
 */
GPIO_PinState IIC::ReadSDA()
{
    HAL_GPIO_ReadPin(this->SDA_GPIO_PORT, this->SDA_PIN);
}

/**
 * @brief 读取SCL的值
 *
 * @return GPIO_PinState
 */
GPIO_PinState IIC::ReadSCL()
{
    HAL_GPIO_ReadPin(this->SCL_GPIO_PORT, this->SCL_PIN);
}

/**
 * @brief IIC专用延时函数
 *
 */
void IIC::Delay()
{
    int z = 0xff;
    while (z--)
        ;
}

/**
 * @brief 产生I2C起始信号
 *
 */
void IIC::Start()
{
    this->WriteSDA(GPIO_PIN_SET);   // 需在SCL之前设定
    this->WriteSCL(GPIO_PIN_SET);   // SCL->高
    this->Delay();                  // 延时
    this->WriteSDA(GPIO_PIN_RESET); // SDA由1->0,产生开始信号
    this->Delay();                  // 延时
    this->WriteSCL(GPIO_PIN_RESET); // SCL->低
}

/**
 * @brief 产生I2C结束信号
 *
 */
void IIC::End()
{
    this->WriteSDA(GPIO_PIN_RESET); // 在SCL之前拉低
    this->WriteSCL(GPIO_PIN_SET);   // SCL->高
    this->Delay();                  // 延时
    this->WriteSDA(GPIO_PIN_SET);   // SDA由0->1,产生结束信号
    this->Delay();
}

/**
 * @brief 发送应答码
 *
 * @param ack 0应答 1不应答
 */
void IIC::Send_ACK(uint8_t ack)
{
    if (ack == 1)
        this->WriteSDA(GPIO_PIN_SET); // 产生应答电平
    else
        this->WriteSDA(GPIO_PIN_RESET);
    this->Delay();
    this->WriteSCL(GPIO_PIN_SET);   // 发送应答信号
    this->Delay();                  // 延时至少4us
    this->WriteSCL(GPIO_PIN_RESET); // 整个期间保持应答信号
}

/**
 * @brief 接收应答码
 *
 * @return uint8_t 应答码 0 应答 1 不应达
 */
uint8_t IIC::Get_ACK()
{
    uint8_t ret;                  // 用来接收返回值
    this->WriteSDA(GPIO_PIN_SET); // 电阻上拉,进入读
    this->Delay();
    this->WriteSCL(GPIO_PIN_SET); // 进入应答检测
    this->Delay();                // 至少延时4us
    ret = this->ReadSDA();        // 保存应答信号
    this->WriteSCL(GPIO_PIN_RESET);
    return ret;
}

/**
 * @brief I2C写1Byte
 *
 * @param dat 1Byte数据
 * @return uint8_t 应答结果 0应答 1不应答
 */
uint8_t IIC::SendByte(uint8_t dat)
{
    uint8_t ack;
    for (int i = 0; i < 8; i++)
    {
        // 高在前低在后
        if (dat & 0x80)
            this->WriteSDA(GPIO_PIN_SET);
        else
            this->WriteSDA(GPIO_PIN_RESET);
        this->Delay();
        this->WriteSCL(GPIO_PIN_SET);
        this->Delay(); // 延时至少4us
        this->WriteSCL(GPIO_PIN_RESET);
        dat <<= 1; // 低位向高位移动
    }

    ack = this->Get_ACK();

    return ack;
}

/**
 * @brief IIC读取1Byte数据
 *
 * @param ack 0应答 1不应答
 * @return uint8_t 读取到的数据
 */
uint8_t IIC::ReadByte(uint8_t ack)
{
    uint8_t ret = 0;
    this->WriteSDA(GPIO_PIN_SET);
    for (int i = 0; i < 8; i++)
    {
        ret <<= 1;
        this->WriteSCL(GPIO_PIN_SET);
        this->Delay();
        // 高在前低在后
        if (this->ReadSDA())
        {
            ret++;
        }
        this->WriteSCL(GPIO_PIN_RESET);
        this->Delay();
    }

    this->Send_ACK(ack);

    return ret;
}

/**
 * @brief I2C连续写
 *
 * @param addr 器件地址
 * @param reg 寄存器地址
 * @param len 长度
 * @param buf 缓冲区地址
 * @return uint8_t 状态 0成功 其他失败
 */
uint8_t IIC::WriteLen(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)
{
    uint8_t i;
    this->Start();
    this->SendByte((addr << 1) | 0); // 发送器件地址+写命令
    this->SendByte(reg);             // 写寄存器地址
    for (i = 0; i < len; i++)
    {
        this->SendByte(buf[i]); // 发送数据
    }
    this->End();
    return 0;
}

/**
 * @brief I2C连续读
 *
 * @param addr 器件地址
 * @param reg 寄存器地址
 * @param len 长度
 * @param buf 缓冲区地址
 * @return uint8_t 状态 0成功 其他失败
 */
uint8_t IIC::ReadLen(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)
{
    this->Start();
    this->SendByte((addr << 1) | 0); // 发送器件地址+写命令
    this->SendByte(reg);             // 写寄存器地址
    this->Start();
    this->SendByte((addr << 1) | 1); // 发送器件地址+读命令
    while (len)
    {
        if (len == 1)
            *buf = this->ReadByte(1); // 读数据,发送nACK
        else
            *buf = this->ReadByte(0); // 读数据,发送ACK
        len--;
        buf++;
    }
    this->End(); // 产生一个停止条件
    return 0;
}