#include "SoftI2C.h"

void Dri_I2C_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 1. 时钟使能GPIOA
    __HAL_RCC_GPIOA_CLK_ENABLE();

    // 2. 设置引脚模式，PA8(SDA), PA9(SCL)设置为通用开漏输出
    GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;   // 通用开漏输出
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

// 发送起始信号
void Dri_I2C_Start(void)
{
    // 拉高SCL、拉高SDA、延时
    SCL_HIGH;
    SDA_HIGH;
    SCL_DELAY;

    // 拉低SDA，下降沿表示起始信号; 延时
    SDA_LOW;
    SCL_DELAY;

    // 拉低SCL，方便发送方修改SDA
    SCL_LOW;
    SCL_DELAY;
}

// 发送停止信号
void Dri_I2C_Stop(void)
{
    // 在拉高SCL之前，先拉低SDA （避免意外产生起始信号）
    SDA_LOW;

    // 拉高SCL并延时
    SCL_HIGH;
    SCL_DELAY;

    // SCL高电平期间拉高SDA，发起结束信号
    SDA_HIGH;
    SCL_DELAY;
}

// 发送一个字节的数据
void Dri_I2C_TransmitByte(uint8_t byte)
{
    // 分8次逐bit发送，先发高位
    for (uint8_t i = 0; i < 8; i++)
    {
        // 判断当前 byte 的最高位
        if (byte & 0x80)
        {
            SDA_HIGH;
        }
        else
        {
            SDA_LOW;
        }

        // 拉高时钟并延时，让接收方采样
        SCL_HIGH;
        SCL_DELAY;

        // byte 左移1位
        byte <<= 1;

        // 拉低SCL，好准备下一个要发的数据
        SCL_LOW;
        SCL_DELAY;
    }
}

// 接收一个字节的数据
uint8_t Dri_I2C_ReceiveByte(void)
{
    // 释放数据总线
    SDA_HIGH;

    // 定义变量，用于接收数据
    uint8_t byte = 0;

    // 分8次逐bit接收，先收高位
    for (uint8_t i = 0; i < 8; i++)
    {
        // 拉高SCL
        SCL_HIGH;

        // byte 左移1位，空出最低位用于接收当前的bit
        byte <<= 1;

        // 读SDA, 如果SDA是高电平
        if (SDA_READ)
        {
            byte |= 0x01;
        }

        // 延时
        SCL_DELAY;

        // 拉低SCL并延时，让发送方准备数据
        SCL_LOW;
        SCL_DELAY;
    }

    // 返回
    return byte;
}

// 发送一个ACK信号, 参数是0(ACK)或1(NACK)
void Dri_I2C_TransmitACK(uint8_t ack)
{
    if (ack)
    {
        // 发送NACK
        SDA_HIGH;
    }
    else
    {
        // 发送ACK
        SDA_LOW;
    }

    // 拉高时钟并延时
    SCL_HIGH;
    SCL_DELAY;

    // 高低时钟并延时
    SCL_LOW;
    SCL_DELAY;
}

// 接收一个ACK信号并返回
uint8_t Dri_I2C_ReceiveACK(void)
{
    // 定义变量保存收到的ack信号
    uint8_t ack = 0;

    // 释放数据总线
    SDA_HIGH;

    // 拉高SCL
    SCL_HIGH;

    // 读取SDA
    ack = SDA_READ ? 1 : 0;
    // 延时
    SCL_DELAY;

    // 拉低SCL并延时
    SCL_LOW;
    SCL_DELAY;

    return ack;
}

void Dri_I2C_WriteAddr(uint8_t slave_addr)
{
    Dri_I2C_TransmitByte(slave_addr);
    uint8_t ack = Dri_I2C_ReceiveACK();
    if (ack)
    {
        debug_printf("I2C send slave address error");
        return;
    }
}

void Dri_I2C_WriteReg(uint8_t reg, uint8_t data)
{
    Dri_I2C_TransmitByte(reg);
    uint8_t ack = Dri_I2C_ReceiveACK();
    if (ack)
    {
        debug_printf("I2C send register address error");
        return;
    }
    Dri_I2C_TransmitByte(data);
    ack = Dri_I2C_ReceiveACK();
    if (ack)
    {
        debug_printf("I2C send register data error");
        return;
    }
}

uint8_t Dri_I2C_ReadReg(uint8_t slave_addr_read, uint8_t reg)
{
    Dri_I2C_Start();
    Dri_I2C_WriteAddr(slave_addr_read - 1);
    Dri_I2C_TransmitByte(reg);
    uint8_t ack = Dri_I2C_ReceiveACK();
    if (ack)
    {
        debug_printf("I2C send register address error");
        return 0;
    }
    Dri_I2C_Start();
    Dri_I2C_WriteAddr(slave_addr_read);
    uint8_t data = Dri_I2C_ReceiveByte();
    Dri_I2C_TransmitACK(0);
    Dri_I2C_Stop();
    return data;
}
