#include "IIC.h"
#include "driver/i2c.h"
#include "esp_log.h"

// 日志标签，用于输出日志信息
#define TAG "I2C操作"

// 辅助函数：构建 I2C 命令句柄，用于指定设备地址和寄存器地址
static i2c_cmd_handle_t build_i2c_cmd(uint8_t dev_addr, uint8_t reg_addr) {
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg_addr, true);
    return cmd;
}

// 辅助函数：执行 I2C 命令句柄并释放资源
static esp_err_t execute_i2c_cmd(i2c_cmd_handle_t cmd) {
    esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}

/**
 * @brief 初始化 I2C 主设备
 * @return esp_err_t 初始化结果，ESP_OK 表示成功，其他值表示失败
 */
esp_err_t i2c_master_init(void) {
    int i2c_master_port = I2C_MASTER_NUM;
    i2c_config_t conf = {
       .mode = I2C_MODE_MASTER,           // 设置为 I2C 主设备模式
       .sda_io_num = I2C_MASTER_SDA_IO,   // 指定 SDA 引脚
       .scl_io_num = I2C_MASTER_SCL_IO,   // 指定 SCL 引脚
       .sda_pullup_en = GPIO_PULLUP_ENABLE, // 启用 SDA 引脚的上拉电阻
       .scl_pullup_en = GPIO_PULLUP_ENABLE, // 启用 SCL 引脚的上拉电阻
       .master.clk_speed = I2C_MASTER_FREQ_HZ, // 设置 I2C 时钟频率
    };
    // 配置 I2C 参数
    i2c_param_config(i2c_master_port, &conf);
    // 安装 I2C 驱动
    return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}

void i2c_scan(void) {
    esp_err_t err;
    i2c_cmd_handle_t cmd;
    ESP_LOGI(TAG, "开始扫描 I2C 总线...");
    for (int addr = 0; addr < 128; addr++) {
        cmd = i2c_cmd_link_create();
        i2c_master_start(cmd);
        i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
        i2c_master_stop(cmd);
        err = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
        if (err == ESP_OK) {
            ESP_LOGI(TAG, "在地址 0x%02X 发现设备", addr);
        }
        i2c_cmd_link_delete(cmd);
    }
    ESP_LOGI(TAG, "扫描完成。");
}

/**
 * @brief 从 I2C 设备读取单个字节数据的函数
 * @param dev_addr I2C 设备的 7 位地址
 * @param reg_addr 要读取数据的寄存器地址
 * @param data 用于存储读取到的单个字节数据的指针
 * @return esp_err_t 操作结果，ESP_OK 表示成功，其他值表示失败
 */
static esp_err_t my_i2c_master_read_byte(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data) {
    i2c_cmd_handle_t cmd = build_i2c_cmd(dev_addr, reg_addr);
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, data, I2C_MASTER_NACK);
    i2c_master_stop(cmd);
    return execute_i2c_cmd(cmd);
}



/*
 * 功能：从 8 位地址的寄存器读取一个字节数据
 * 参数：
 *   addr：寄存器的内部地址
 *   *Data：数据存储地址
 * 返回值：(1 = 成功, 0 = 失败)
 */
unsigned char i2cread(unsigned char addr, unsigned char *Data) {
    esp_err_t ret = my_i2c_master_read_byte(Device_Address, addr, Data);
    return (ret == ESP_OK) ? 1 : 0;
}

/**
 * @brief 从 Device_Address 的指定寄存器地址开始读取多个字节的数据
 * @param addr I2C 设备（Device_Address）内部寄存器的起始地址
 * @param length 要读取的数据字节长度
 * @param Data 用于存储读取到的数据的缓冲区地址
 * @return unsigned char 操作状态，1 表示读取成功，0 表示读取失败
 */
unsigned char i2creads(uint8_t addr, uint8_t length, uint8_t *Data) {
    i2c_cmd_handle_t cmd = build_i2c_cmd(Device_Address, addr);
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (Device_Address << 1) | I2C_MASTER_READ, true);

    if (length > 1) {
        i2c_master_read(cmd, Data, length - 1, I2C_MASTER_ACK);
    }
    i2c_master_read_byte(cmd, Data + length - 1, I2C_MASTER_NACK);

    i2c_master_stop(cmd);
    esp_err_t ret = execute_i2c_cmd(cmd);
    return (ret == ESP_OK) ? 1 : 0;
}

/**
 * @brief 向 8 位地址的寄存器写入一个字节数据
 * @param addr 寄存器的内部地址
 * @param Data 要写入的数据
 * @return unsigned char 操作结果，1 表示成功，0 表示失败
 */
unsigned char i2cwrite(uint8_t addr, uint8_t Data) {
    i2c_cmd_handle_t cmd = build_i2c_cmd(Device_Address, addr);
    i2c_master_write_byte(cmd, Data, true);
    i2c_master_stop(cmd);
    esp_err_t ret = execute_i2c_cmd(cmd);
    return (ret == ESP_OK) ? 1 : 0;
}

/**
 * @brief 向 Device_Address 的指定寄存器地址开始写入多个字节的数据
 * @param addr I2C 设备（Device_Address）内部寄存器的起始地址
 * @param length 要写入的数据字节长度
 * @param Data 要写入的数据缓冲区地址
 * @return unsigned char 操作状态，1 表示写入成功，0 表示写入失败
 */
unsigned char i2cwrites(uint8_t addr, uint8_t length, const uint8_t *Data) {
    i2c_cmd_handle_t cmd = build_i2c_cmd(Device_Address, addr);
    for (uint8_t i = 0; i < length; i++) {
        i2c_master_write_byte(cmd, Data[i], true);
    }
    i2c_master_stop(cmd);
    esp_err_t ret = execute_i2c_cmd(cmd);
    return (ret == ESP_OK) ? 1 : 0;
}