#include "QMI8658A.h"

// 定义日志标签
static const char *TAG = "QMI8658A.c";


// 定义一个数组存储所有命令信息
CommandInfo commandInfos[] = {
    {"CTRL_CMD_ACK", 0x00, "Ctrl9", "确认。主机向QMI8658确认，以结束协议。"},
    {"CTRL_CMD_RST_FIFO", 0x04, "Ctrl9", "从主机重置FIFO。"},
    {"CTRL_CMD_REQ_FIFO", 0x05, "Ctrl9R", "从设备获取FIFO数据。"},
    {"CTRL_CMD_WRITE_WOM_SETTING", 0x08, "WCtrl9", "设置并启用运动唤醒（WoM）功能。"},
    {"CTRL_CMD_ACCEL_HOST_DELTA_OFFSET", 0x09, "WCtrl9", "更改加速度计偏移量。"},
    {"CTRL_CMD_GYRO_HOST_DELTA_OFFSET", 0x0A, "WCtrl9", "更改陀螺仪偏移量。"},
    {"CTRL_CMD_CONFIGURE_TAP", 0x0C, "WCtrl9", "配置敲击检测。"},
    {"CTRL_CMD_CONFIGURE_PEDOMETER", 0x0D, "WCtrl9", "配置计步器。"},
    {"CTRL_CMD_CONFIGURE_MOTION", 0x0E, "WCtrl9", "配置任意运动/静止/显著运动检测。"},
    {"CTRL_CMD_RESET_PEDOMETER", 0x0F, "WCtrl9", "重置计步器步数。"},
    {"CTRL_CMD_COPY_USID", 0x10, "Ctrl9R", "将USID和固件版本复制到UI寄存器。"},
    {"CTRL_CMD_SET_RPU", 0x11, "WCtrl9", "配置IO上拉电阻。"},
    {"CTRL_CMD_AHB_CLOCK_GATING", 0x12, "WCtrl9", "内部AHB时钟门控开关。"},
    {"CTRL_CMD_ON_DEMAND_CALIBRATION", 0xA2, "WCtrl9", "对陀螺仪进行按需校准。"},
    {"CTRL_CMD_APPLY_GYRO_GAINS", 0xAA, "WCtrl9", "恢复保存的陀螺仪增益。"}
};

// 实现函数，通过枚举获取对应的命令信息结构体
CommandInfo getCommandInfo(CommandEnum cmd) {
    return commandInfos[cmd];
}

/*使用示例：
    // 通过枚举获取命令信息并打印
    CommandInfo info = getCommandInfo(CTRL_CMD_REQ_FIFO_ENUM);
    printf("Command Name: %s\n", info.commandName);
    printf("CTRL9 Command Value: 0x%X\n", info.ctrl9CommandValue);
    printf("Protocol Type: %s\n", info.protocolType);
    printf("Description: %s\n", info.description);
    */


/**
 * @brief 对加速度计进行自检操作
 * 
 * 此函数通过一系列的I2C操作对加速度计进行自检，包括禁用传感器、设置输出数据速率、
 * 等待特定状态位变化、读取自检结果并判断是否正常。
 * 
 * @param 无
 * 
 * @return uint8_t 
 *         - 1: 加速度计自检正常
 *         - 0: 加速度计自检异常或在自检过程中出现I2C通信错误
 */
uint8_t Acc_Self_Test()
{
    uint8_t data = 0;

    // 1. 禁用传感器
    // 向寄存器CTRL7写入0x00以禁用传感器
    if (!i2cwrite(CTRL7, 0x00))
    {
        // 若写入失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "发送禁用传感器命令失败！");
        return 0;
    }

    // 2. 设置合适的加速度计输出数据速率4G,896.8Hz
    // 向寄存器CTRL2写入0x93以设置加速度计输出数据速率为4G,896.8Hz
    if (!i2cwrite(CTRL2, 0x93))
    {
        // 若写入失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "设置合适的加速度计输出数据速率失败！");
        return 0;
    }

    // 3. 等待STATUSINT第0位为1
    // 初始化data为0x00
    data = 0x00;
    // 循环读取STATUSINT寄存器，直到第0位为1
    while ((data & 0x01) == 0)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示自检失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 4. 将CTRL2.aST（第7位）设为0
    // 读取CTRL2寄存器的值到data
    if (!i2cread(CTRL2, &data))
    {
        // 若读取失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "读取CTRL2失败！");
        return 0;
    }
    // 将data的第7位清零
    data &= 0x7F;
    // 将修改后的值写回CTRL2寄存器
    if (!i2cwrite(CTRL2, data))
    {
        // 若写入失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "设置CTRL2.aST（第7位）设为0失败！");
        return 0;
    }

    // 5. 等待STATUSINT第0位为0
    // 初始化data为0xFF
    data = 0xFF;
    // 此处逻辑有误，应改为(data & 0x01) != 0，原逻辑(data | 0xFE) == 0不可能成立
    while ((data & 0x01) != 0)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示自检失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 6. 读取加速度计自检结果
    // 定义一个长度为6的数组datas用于存储自检结果
    unsigned char datas[6] = {};
    // 从地址0x51开始连续读取6个字节的数据到datas数组
    if (!i2creads(0x51, 6, datas))
    {
        // 若读取失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "读取加速度计自检结果失败！");
        return 0;
    }

    // 判断结果绝对值是否高于200mg
    int16_t dVxData = 0, dVyData = 0, dVzData = 0;
    // 组合低字节和高字节得到完整的x轴数据
    dVxData = (datas[1] << 8) | datas[0];
    // 组合低字节和高字节得到完整的y轴数据
    dVyData = (datas[3] << 8) | datas[2];
    // 组合低字节和高字节得到完整的z轴数据
    dVzData = (datas[5] << 8) | datas[4];

    // 判断x、y、z轴数据的绝对值是否低于200mg
    if (fabs(dVxData * 0.5) < 200 || fabs(dVyData * 0.5) < 200 || fabs(dVzData * 0.5) < 200)
    {
        // 若有任何一个轴的数据绝对值低于200mg，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "读取加速度计自检结果为：异常！");
        return 0;
    }
    // 若所有轴的数据绝对值都高于200mg，记录日志表示自检正常并返回1
    ESP_LOGE(TAG, "读取加速度计自检结果为：正常！");
    return 1;
}


/**
 * @brief 对陀螺仪进行自检操作
 * 
 * 该函数通过一系列I2C通信步骤对陀螺仪进行自检，包含禁用传感器、设置特定寄存器位、
 * 等待状态位变化、读取自检结果并判断是否符合正常范围。
 * 
 * @param 无
 * @return uint8_t 
 *         - 1: 陀螺仪自检正常
 *         - 0: 陀螺仪自检异常或在自检过程中出现I2C通信错误
 */
uint8_t Gyr_Self_Test()
{
    uint8_t data = 0;

    // 1. 禁用传感器
    // 向寄存器CTRL7写入0x00，以禁用陀螺仪传感器
    if (!i2cwrite(CTRL7, 0x00))
    {
        // 若写入失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "发送禁用传感器命令失败！");
        return 0;
    }

    // 2. 将gST位设为1（CTRL3第7位 = 1’b1）
    // 从寄存器CTRL3读取数据到变量data
    if (!i2cread(CTRL3, &data))
    {
        // 若读取失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "读取CTRL3失败！");
        return 0;
    }
    // 将data的第7位设置为1
    data |= 0x80;
    // 将修改后的数据写回寄存器CTRL3
    if (!i2cwrite(CTRL3, data))
    {
        // 若写入失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "设置CTRL3.gST（第7位）设为1失败！");
        return 0;
    }

    // 3. 等待STATUSINT第0位为1
    // 初始化data为0x00
    data = 0x00;
    // 循环读取STATUSINT寄存器，直到其第0位变为1
    while ((data & 0x01) == 0)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示自检失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 4. 将gST位设为0（CTRL3第7位 = 0’b1）
    // 再次从寄存器CTRL3读取数据到变量data
    if (!i2cread(CTRL3, &data))
    {
        // 若读取失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "读取CTRL3失败！");
        return 0;
    }
    // 将data的第7位清零
    data &= 0x7F;
    // 将修改后的数据写回寄存器CTRL3
    if (!i2cwrite(CTRL3, data))
    {
        // 若写入失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "设置CTRL3.gST（第7位）设为0失败！");
        return 0;
    }

    // 5. 等待STATUSINT第0位为0
    // 初始化data为0xFF
    data = 0xFF;
    // 此处原逻辑(data | 0xFE) == 0有误，应改为(data & 0x01) != 0，以等待STATUSINT第0位为0
    while ((data & 0x01) != 0)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示自检失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 6. 读取陀螺仪自检结果
    // 定义一个长度为6的数组datas用于存储自检结果
    unsigned char datas[6] = {};
    // 从地址0x51开始连续读取6个字节的数据到datas数组
    if (!i2creads(0x51, 6, datas))
    {
        // 此处注释有误，应是读取陀螺仪自检结果，若读取失败，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "读取陀螺仪自检结果失败！");
        return 0;
    }

    // 判断结果绝对值是否高于300dps
    int16_t dVxData = 0, dVyData = 0, dVzData = 0;
    // 组合低字节和高字节得到完整的x轴数据
    dVxData = (datas[1] << 8) | datas[0];
    // 组合低字节和高字节得到完整的y轴数据
    dVyData = (datas[3] << 8) | datas[2];
    // 组合低字节和高字节得到完整的z轴数据
    dVzData = (datas[5] << 8) | datas[4];

    // 判断x、y、z轴数据经转换后的绝对值是否低于300dps
    if (fabs(dVxData * 62.5 / 1000) < 300 || fabs(dVyData * 62.5 / 1000) < 300 || fabs(dVzData * 62.5 / 1000) < 300)
    {
        // 若有任何一个轴的数据绝对值低于300dps，记录错误日志并返回0表示自检失败
        ESP_LOGE(TAG, "读取陀螺仪自检结果为：异常！");
        return 0;
    }
    // 若所有轴的数据绝对值都高于300dps，记录日志表示自检正常并返回1
    ESP_LOGE(TAG, "读取陀螺仪自检结果为：正常！");
    return 1;
}

/**
 * @brief 对陀螺仪进行按需校准（COD, Calibration On Demand）操作
 * 
 * 此函数用于对陀螺仪进行按需校准，校准过程中建议将设备置于安静环境，
 * 否则校准可能失败并报错。函数通过一系列I2C通信操作完成校准流程，
 * 包括禁用传感器、发送校准指令、等待校准完成、确认校准结果、检查校准状态，
 * 最后开启加速度计和陀螺仪的同步采样模式。
 * 
 * @param 无
 * @return uint8_t 
 *         - 1: 陀螺仪校准成功
 *         - 0: 陀螺仪校准失败或在校准过程中出现I2C通信错误
 */
uint8_t Gyr_COD()
{
    uint8_t data = 0;

    // 1. 禁用传感器
    // 向寄存器CTRL7写入0x00，以禁用传感器，为后续校准操作做准备
    if (!i2cwrite(CTRL7, 0x00))
    {
        // 若写入失败，记录错误日志并返回0表示校准失败
        ESP_LOGE(TAG, "发送禁用传感器命令失败！");
        return 0;
    }

    // 2. 通过CTRL9命令发出CTRL_CMD_ON_DEMAND_CALIBRATION（0xA2）指令
    // 向寄存器CTRL9写入0xA2，启动陀螺仪的按需校准操作
    if (!i2cwrite(CTRL9, 0xA2))
    {
        // 若写入失败，记录错误日志并返回0表示校准失败
        ESP_LOGE(TAG, "发送CTRL_CMD_ON_DEMAND_CALIBRATION（0xA2）指令失败！");
        return 0;
    }

    // 3. 等待约1.5秒，让QMI8658A完成CTRL9命令
    // 延时1500毫秒，确保设备有足够时间执行校准命令
    vTaskDelay(pdMS_TO_TICKS(1500));

    // 4. 等待STATUSINT第7位为1
    // 初始化data为0x00
    data = 0x00;
    // 循环读取STATUSINT寄存器，直到其第7位变为1，表示校准操作开始
    while (((data >> 7) & 0x01) == 0)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示校准失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 5. 向CTRL9寄存器写入CTRL_CMD_ACK（0x00）来确认
    // 向寄存器CTRL9写入0x00，确认校准操作开始
    if (!i2cwrite(CTRL9, 0x00))
    {
        // 若写入失败，记录错误日志并返回0表示校准失败
        ESP_LOGE(TAG, "发送CTRL_CMD_ACK（0x00）指令失败！");
        return 0;
    }

    // 6. 等待STATUSINT第7位为0
    // 初始化data为0xFF
    data = 0xFF;
    // 循环读取STATUSINT寄存器，直到其第7位变为0，表示校准操作完成
    while (((data >> 7) & 0x01) == 1)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示校准失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 7. 读取COD_STATUS寄存器（0x46），检查COD实施的结果/状态
    // 从寄存器COD_STATUS读取校准结果
    if (!i2cread(COD_STATUS, &data))
    {
        // 若读取失败，记录错误日志并返回0表示校准失败
        ESP_LOGE(TAG, "读取COD_STATUS失败！");
        return 0;
    }
    // 若data不为0，表示校准失败
    if (data)
    {
        // 记录错误日志，显示错误类型
        ESP_LOGE(TAG, "陀螺仪校准失败，错误类型：%X", data);
        return 0;
    }

    // 8. 开启加速度计和陀螺仪，同步采样模式
    // 向寄存器CTRL7写入0x83，开启加速度计和陀螺仪的同步采样模式
    if (!i2cwrite(CTRL7, 0x83))
    {
        // 若写入失败，记录错误日志并返回0表示校准失败
        ESP_LOGE(TAG, "启加速度计和陀螺仪,同步采样模式失败！");
        return 0;
    }

    // 9. 校准成功
    // 记录日志表示陀螺仪校准成功，并返回1
    ESP_LOGE(TAG, "陀螺仪校准成功!");
    return 1;
}


// 存储陀螺仪校准值
float GyrCompensate[6];

/**
 * @brief 初始化QMI8658A传感器
 * 
 * 该函数用于对QMI8658A传感器进行初始化操作，包括复位传感器、配置通讯方式和中断引脚、
 * 进行加速度计和陀螺仪的自检、配置传感器参数、使用锁定机制、开启传感器同步采样模式、
 * 进行陀螺仪自带校准和手动校准等步骤。
 * 
 * @param 无
 * @return int 
 *         - 1: 传感器初始化成功
 *         - 0: 传感器初始化失败，可能是某个步骤的I2C通信出错或自检、校准失败
 */
int QMI8658A_Init(void)
{
    uint8_t data = 0;

    // 1. 复位QMI8658A传感器
    // 向复位寄存器RESET写入0xB0，触发传感器复位操作
    if (!i2cwrite(RESET, 0xB0))
    {
        // 若写入失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "发送复位命令失败！");
        return 0;
    }
    // 延时100毫秒，等待复位操作完成
    vTaskDelay(pdMS_TO_TICKS(100));

    // 读取复位状态
    // 从dQY_L寄存器读取复位状态数据到变量data
    if (!i2cread(dQY_L, &data))
    {
        // 若读取失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "读取复位状态失败！");
        return 0;
    }

    // 检查复位状态
    // 判断读取到的复位状态数据是否为0x80，若不是则表示复位失败
    if (data != 0x80)
    {
        // 记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "复位失败！");
        return 0;
    }

    // 2. 配置通讯方式和中断引脚
    // 向寄存器CTRL1写入0x60，配置传感器的通讯方式和中断引脚
    if (!i2cwrite(CTRL1, 0x60))
    {
        // 若写入失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "配置通讯方式和中断引脚失败！");
        return 0;
    }

    // 3. 加速度计自检
    // 调用Acc_Self_Test函数进行加速度计自检
    if (!Acc_Self_Test())
    {
        // 若自检失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "加速度计自检失败！");
        return 0;
    }

    // 4. 陀螺仪自检
    // 调用Gyr_Self_Test函数进行陀螺仪自检
    if (!Gyr_Self_Test())
    {
        // 若自检失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "陀螺仪自检失败！");
        return 0;
    }

    // 5. 配置加速度计
    // 向寄存器CTRL2写入0x33，禁用加速度自检，设置量程为16G，输出数据速率为896.8Hz
    if (!i2cwrite(CTRL2, 0x33))
    {
        // 若写入失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "配置加速度计失败！");
        return 0;
    }

    // 6. 配置陀螺仪
    // 向寄存器CTRL3写入0x73，禁用陀螺仪自检，设置量程为2048dps，输出数据速率为896.8Hz
    if (!i2cwrite(CTRL3, 0x73))
    {
        // 若写入失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "配置陀螺仪计失败！");
        return 0;
    }

    // 7. 配置低通滤波器
    // 向寄存器CTRL5写入0x35，配置低通滤波器为3.63%
    if (!i2cwrite(CTRL5, 0x35))
    {
        // 若写入失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "配置低通滤波器失败！");
        return 0;
    }

    // 8. 使用锁定机制
    // 8.1 禁用内部AHB时钟
    // 向CAL1_L寄存器写入0x01
    if (!i2cwrite(CAL1_L, 0x01))
    {
        // 若写入失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "CAL1_L寄存器写入失败！");
        return 0;
    }
    // 在CTRL9协议中写入0x12（CTRL_CMD_AHB_CLOCK_GATING）
    if (!i2cwrite(CTRL9, 0x12))
    {
        // 若写入失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "发送CTRL_CMD_ON_DEMAND_CALIBRATION（0x12）指令失败！");
        return 0;
    }
    // 等待10毫秒，让QMI8658A完成CTRL9命令
    vTaskDelay(pdMS_TO_TICKS(10));

    // 8.2 等待STATUSINT第7位为1
    // 初始化data为0x00
    data = 0x00;
    // 循环读取STATUSINT寄存器，直到其第7位变为1
    while (((data >> 7) & 0x01) == 0)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示初始化失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 8.3 向CTRL9寄存器写入CTRL_CMD_ACK（0x00）来确认
    // 向寄存器CTRL9写入0x00，确认操作
    if (!i2cwrite(CTRL9, 0x00))
    {
        // 若写入失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "发送CTRL_CMD_ACK（0x00）指令失败！");
        return 0;
    }

    // 8.4 等待STATUSINT第7位为0
    // 初始化data为0xFF
    data = 0xFF;
    // 循环读取STATUSINT寄存器，直到其第7位变为0
    while (((data >> 7) & 0x01) == 1)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示初始化失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 9. 开启加速度计和陀螺仪，同步采样模式
    // 向寄存器CTRL7写入0x83，开启加速度计和陀螺仪的同步采样模式
    if (!i2cwrite(CTRL7, 0x83))
    {
        // 若写入失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "启加速度计和陀螺仪,同步采样模式失败！");
        return 0;
    }
    // 延时10毫秒，等待模式开启完成
    vTaskDelay(pdMS_TO_TICKS(10));

    // 10. 陀螺仪自带校准
    // 调用Gyr_COD函数进行陀螺仪自带校准
    if (!Gyr_COD())
    {
        // 若校准失败，记录错误日志并返回0表示初始化失败
        ESP_LOGE(TAG, "陀螺仪校准失败！");
        return 0;
    }
    // 延时100毫秒，等待校准完成
    vTaskDelay(pdMS_TO_TICKS(100));

    // 11. 陀螺仪手动校准
    // 循环调用calibrationGYR函数进行陀螺仪手动校准，直到校准成功
    while (!calibration_ACC_GYR(GyrCompensate))
    {
    }

    // 12. 初始化成功
    // 记录日志表示传感器初始化成功，并返回1
    ESP_LOGE(TAG, "初始化成功！");
    return 1;
}


/**
 * @brief 读取QMI8658A六轴传感器数据
 * 
 * 该函数用于读取QMI8658A六轴传感器的数据，在读取前会先检查数据的可用性和锁定状态，
 * 确保数据有效后再进行读取操作，并将读取到的原始数据存储到传入的数组中。
 * 
 * @param DATA 指向一个长度为6的int16_t类型数组的指针，用于存储读取到的六轴传感器原始数据，
 *             数组元素依次为AX、AY、AZ、GX、GY、GZ
 * @return int 
 *         - 1: 数据读取成功
 *         - 0: 读取过程中出现错误，如读取状态寄存器失败或读取传感器数据寄存器失败
 */
int QMI8658A_ReadData(int16_t *DATA)
{
    uint8_t data = 0;

    // 1. 判断数据是否可用（Avail）
    // 循环读取STATUSINT寄存器，直到其第0位为1，表示数据可用
    while ((data & 0x01) != 1)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示读取失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 2. 判断数据是否锁定（Locked）
    // 重置data为0
    data = 0;
    // 循环读取STATUSINT寄存器，直到其第1位为1，表示数据已锁定
    while (((data >> 1) & 0x01) != 1)
    {
        // 读取STATUSINT寄存器的值到data
        if (!i2cread(STATUSINT, &data))
        {
            // 若读取失败，记录错误日志并返回0表示读取失败
            ESP_LOGE(TAG, "读取STATUSINT状态失败！");
            return 0;
        }
    }

    // 3. 读取传感器数据寄存器
    // 定义一个长度为12的数组datas，用于存储从传感器数据寄存器读取的数据
    uint8_t datas[12] = {};
    // 从地址A_XYZ开始连续读取12个字节的数据到datas数组
    if (!i2creads(A_XYZ, 12, datas))
    {
        // 此处注释有误，应是读取传感器数据失败，若读取失败，记录错误日志并返回0表示读取失败
        ESP_LOGE(TAG, "读取传感器数据失败！");
        return 0;
    }

    // 4. 组合数据
    // 将读取到的低字节和高字节数据组合成完整的16位数据，并存储到DATA数组中
    DATA[0] = (datas[1] << 8) | datas[0]; // AX
    DATA[1] = (datas[3] << 8) | datas[2]; // AY
    DATA[2] = (datas[5] << 8) | datas[4]; // AZ
    DATA[3] = (datas[7] << 8) | datas[6]; // GX
    DATA[4] = (datas[9] << 8) | datas[8]; // GY
    DATA[5] = (datas[11] << 8) | datas[10]; // GZ

    // 5. 返回读取成功标志
    return 1;
}

/**
 * @brief 将QMI8658A传感器的原始数据根据加速度计和陀螺仪的量程进行转换，并补偿陀螺仪数据
 * 
 * 该函数接收QMI8658A传感器的原始数据、加速度计和陀螺仪的量程作为输入，
 * 根据不同的量程将原始数据转换为实际的物理量值。加速度计数据转换为以g为单位，
 * 陀螺仪数据转换为以dps（度每秒）为单位，并对陀螺仪数据进行补偿。
 * 
 * @param InData 指向包含原始传感器数据的数组的指针，数组长度应为6，
 *               前3个元素为加速度计数据（AX, AY, AZ），后3个元素为陀螺仪数据（GX, GY, GZ）
 * @param OutData 指向用于存储转换后数据的数组的指针，数组长度应为6，
 *                前3个元素存储转换后的加速度计数据，后3个元素存储转换后的陀螺仪数据
 * @param accelRange 加速度计的量程，合法值为2, 4, 8, 16（单位：g）
 * @param gyroRange 陀螺仪的量程，合法值为16, 32, 64, 128, 256, 512, 1024, 2048（单位：dps）
 * @return 无
 */
void QMI8658A_ConvertData(int16_t *InData, float *OutData, int accelRange, int gyroRange)
{
    float accelFactor, compensate;

    // 1. 计算加速度计转换因子
    // 根据加速度计量程选择合适的转换因子
    switch (accelRange)
    {
        case 2: // ±2g
            accelFactor = 2.0f * 2 / 65536;
            break;
        case 4: // ±4g
            accelFactor = 4.0f * 2 / 65536;
            break;
        case 8: // ±8g
            accelFactor = 8.0f * 2 / 65536;
            break;
        case 16: // ±16g
            accelFactor = 16.0f * 2 / 65536;
            break;
        default:
            accelFactor = 0;
            break;
    }

    // 2. 转换加速度计数据
    // 将原始加速度计数据乘以转换因子，得到以g为单位的加速度计数据
    for (int i = 0; i < 3; i++)
    {
        OutData[i] = InData[i] * accelFactor;
    }

    float gyroFactor;

    // 3. 计算陀螺仪转换因子
    // 根据陀螺仪量程选择合适的转换因子
    switch (gyroRange)
    {
        case 16: // ±16dps
            gyroFactor = 16.0f * 2 / 65536;
            break;
        case 32: // ±32dps
            gyroFactor = 32.0f * 2 / 65536;
            break;
        case 64: // ±64dps
            gyroFactor = 64.0f * 2 / 65536;
            break;
        case 128: // ±128dps
            gyroFactor = 128.0f * 2 / 65536;
            break;
        case 256: // ±256dps
            gyroFactor = 256.0f * 2 / 65536;
            break;
        case 512: // ±512dps
            gyroFactor = 512.0f * 2 / 65536;
            break;
        case 1024: // ±1024dps
            gyroFactor = 1024.0f * 2 / 65536;
            break;
        case 2048: // ±2048dps
            gyroFactor = 2048.0f * 2 / 65536;
            break;
        default:
            gyroFactor = 0;
            break;
    }

    // 4. 转换并补偿陀螺仪数据
    // 将原始陀螺仪数据乘以转换因子，并减去对应的补偿值，得到以dps为单位的陀螺仪数据
    for (int i = 3; i < 6; i++)
    {
        switch (i)
        {
            case 3:
                compensate = GyrCompensate[3];
                break;
            case 4:
                compensate = GyrCompensate[4];
                break;
            case 5:
                compensate = GyrCompensate[5];
                break;
            default:
                // 如果 i 不是 3、4、5，可以在这里添加默认处理逻辑
                break;
        }
        OutData[i] = InData[i] * gyroFactor - compensate;
    }
}

/**
 * @brief 获取单位为g的三轴加速度计和单位为dps的三轴陀螺仪数据
 * 
 * 该函数通过调用QMI8658A_ReadData函数读取传感器的原始数据，
 * 再调用QMI8658A_ConvertData函数将原始数据转换为以g为单位的加速度计数据
 * 和以dps为单位的陀螺仪数据，并将转换后的数据存储到传入的数组中。
 * 
 * @param OutData 指向一个长度为6的float类型数组的指针，用于存储转换后的六轴传感器数据，
 *                数组元素依次为AX、AY、AZ、GX、GY、GZ，单位分别为g和dps
 * @return 无
 */
void QMI8658A_Get_G_DPS(float *OutData)
{
    // 1. 定义用于存储原始数据的数组
    int16_t data[6];

    // 2. 读取原始数据
    // 调用QMI8658A_ReadData函数读取传感器的原始数据
    QMI8658A_ReadData(data);

    // 3. 转换数据
    // 调用QMI8658A_ConvertData函数将原始数据转换为以g和dps为单位的数据
    QMI8658A_ConvertData(data, OutData, ACCRANGE, GYRRANGE);
}

/**
 * @brief 计算加速度的模长
 * 
 * 该函数接收一个包含三轴加速度数据的数组，通过计算三个轴加速度值的平方和的平方根，
 * 得到加速度的模长。
 * 
 * @param OutData 指向一个长度至少为 3 的 float 类型数组的指针，数组前三个元素依次为 X、Y、Z 轴的加速度值，单位为 g
 * @return float 加速度的模长，单位为 g
 */
float calculateAccelerationMagnitude(float *OutData)
{
    return (float)sqrt(OutData[0] * OutData[0] + OutData[1] * OutData[1] + OutData[2] * OutData[2]);
}

/**
 * @brief 比较函数，用于 qsort
 * 
 * 该函数是一个用于 qsort 函数的比较函数，用于对 float 类型的数据进行排序。
 * 它会比较两个 float 类型的值，并根据它们的大小关系返回相应的结果。
 * 
 * @param a 指向第一个 float 类型数据的指针
 * @param b 指向第二个 float 类型数据的指针
 * @return int 
 *         - 若 *a > *b，返回 1
 *         - 若 *a <= *b，返回 -1
 */
int compareFloat(const void *a, const void *b)
{
    return (*(float *)a - *(float *)b) > 0 ? 1 : -1;
}

/**
 * @brief 计算平均值的辅助函数
 * 
 * 该函数用于计算陀螺仪在一段时间内采集的多个数据的平均值。具体做法是，
 * 先将采集到的陀螺仪AX、AY、AZ、GX、GY、GZ 六个轴数据分别复制到临时数组中并排序，然后选取中间的
 * USED_DATA_COUNT 条数据计算平均值，最终将结果存储到 OutData 数组中。
 * 
 * @param DATA 一个二维 float 类型数组，大小为 [MIN_COLLECTION_COUNT][6]，
 *             存储了陀螺仪在 MIN_COLLECTION_COUNT 次采集过程中AX、AY、AZ、GX、GY、GZ 六个轴的数据，单位为 g和dps
 * @param OutData 指向一个长度至少为 6 的 float 类型数组的指针，用于存储计算得到的陀螺仪AX、AY、AZ、GX、GY、GZ轴数据的平均值，单位为 g和dps
 * @return 无
 */
void calculateAverages(float DATA[MIN_COLLECTION_COUNT][6], float *OutData) {
    // 动态分配内存用于存储排序后的数据
    float **sortedData = (float **)malloc(6 * sizeof(float *));
    if (sortedData == NULL) {
        ESP_LOGE(TAG, "内存分配失败");
        return;
    }

    for (int j = 0; j < 6; j++) {
        sortedData[j] = (float *)malloc(MIN_COLLECTION_COUNT * sizeof(float));
        if (sortedData[j] == NULL) {
            // 释放已分配的内存
            for (int k = 0; k < j; k++) {
                free(sortedData[k]);
            }
            free(sortedData);
            ESP_LOGE(TAG, "内存分配失败");
            return;
        }
    }

    // 复制数据到临时数组
    for (int i = 0; i < MIN_COLLECTION_COUNT; i++) {
        for (int j = 0; j < 6; j++) {
            sortedData[j][i] = DATA[i][j];
        }
    }

    // 对数据分别排序
    for (int j = 0; j < 6; j++) {
        qsort(sortedData[j], MIN_COLLECTION_COUNT, sizeof(float), compareFloat);
    }

    // 计算中间 USED_DATA_COUNT 条数据的起始索引
    int startIndex = (MIN_COLLECTION_COUNT - USED_DATA_COUNT) / 2;

    // 计算中间 USED_DATA_COUNT 条数据的总和并计算平均值
    for (int j = 0; j < 6; j++) {
        float sum = 0;
        for (int i = startIndex; i < startIndex + USED_DATA_COUNT; i++) {
            sum += sortedData[j][i];
        }
        OutData[j] = sum / USED_DATA_COUNT;
    }

    // 释放动态分配的内存
    for (int j = 0; j < 6; j++) {
        free(sortedData[j]);
    }
    free(sortedData);
}


/**
 * @brief 校准陀螺仪和加速度计，等待收集至少 500 次静止状态下的数据，对数据排序后取中间 200 条计算平均值。
 *
 * 该函数会不断读取六轴传感器的数据，计算加速度的模长，判断设备是否处于静止状态。
 * 当收集到至少 500 次静止状态下的数据后，对陀螺仪的 AX、AY、AZ、GX、GY、GZ 六个轴数据分别排序，
 * 取排序后中间的 200 条数据计算平均值，并将结果存储在 OutData 数组中。
 *
 * @param[out] OutData 用于存储校准后陀螺仪六个轴（ AX、AY、AZ、GX、GY、GZ）的平均值，数组长度至少为 6。
 * @return uint8_t 校准结果状态码：
 *         - 0：表示读取六轴数据失败、传入参数无效或者还未收集满 500 次静止数据。
 *         - 1：表示成功收集 500 次静止数据并完成陀螺仪校准。
 */
uint8_t calibration_ACC_GYR(float *OutData) {
    // 检查传入的 OutData 指针是否为 NULL
    if (OutData == NULL) {
        ESP_LOGE(TAG, "传入的 OutData 指针为 NULL，无法进行校准。");
        return 0;
    }

    // 用于存储从传感器读取的原始六轴数据，数组长度为 6，分别对应 AX、AY、AZ、GX、GY、GZ
    int16_t rawData[6];
    // 用于存储转换后的六轴数据，同样数组长度为 6
    float convertedData[6], AM = 0;
    // 用于存储上一次计算得到的加速度模长，初始值为 0
    float OldAM = 0;
    // 动态分配内存，用于存储至少 500 次静止状态下的陀螺仪数据（AX、AY、AZ、GX、GY、GZ）
    float (*DATA)[6] = (float (*)[6])malloc(MIN_COLLECTION_COUNT * sizeof(float[6]));
    if (DATA == NULL) {
        ESP_LOGE(TAG, "内存分配失败，无法进行校准。");
        return 0;
    }
    // 计数器，记录当前已经收集到的静止数据的次数，初始值为 0
    int i = 0;

    // 若还未收集满 500 次静止数据，则继续收集
    while (i < MIN_COLLECTION_COUNT) {
        // 调用 QMI8658A_ReadData 函数读取六轴数据
        if (!QMI8658A_ReadData(rawData)) {
            // 若读取失败，打印错误信息，释放内存并返回 0 表示失败
            ESP_LOGE(TAG, "读取六轴数据失败，无法继续处理。");
            free(DATA);
            return 0;
        }

        // 调用 QMI8658A_ConvertData 函数将原始数据进行转换，转换结果存储在 convertedData 数组中
        QMI8658A_ConvertData(rawData, convertedData, ACCRANGE, GYRRANGE);

        // 计算加速度的模长，存储在 AM 变量中
        AM = calculateAccelerationMagnitude(convertedData);

        // 判断当前加速度模长与上一次的差值是否小于阈值，若小于则认为处于静止状态
        if (fabs(AM - OldAM) < STATIONARY_THRESHOLD) {
            // 保存数据（AX、AY、AZ、GX、GY、GZ）
            DATA[i][0] = convertedData[0];
            DATA[i][1] = convertedData[1];
            DATA[i][2] = convertedData[2];
            DATA[i][3] = convertedData[3];
            DATA[i][4] = convertedData[4];
            DATA[i][5] = convertedData[5];
            // 计数器加 1，表示又收集到一次静止数据
            i++;
        } else {
            // 若不处于静止状态，更新 OldAM 为当前的加速度模长
            OldAM = AM;
        }
    }

    // 已经收集到至少 500 次静止数据，计算陀螺仪平均值
    calculateAverages(DATA, OutData);

    // 释放动态分配的内存
    free(DATA);

    // 打印校准结果，输出 GX、GY、GZ 轴的平均值
    ESP_LOGI(TAG, "陀螺仪校准完成，AX 平均值: %.2f, AY 平均值: %.2f, AZ 平均值: %.2f,GX 平均值: %.2f, GY 平均值: %.2f, GZ 平均值: %.2f", OutData[0], OutData[1], OutData[2],OutData[3], OutData[4], OutData[5]);

    // 返回 1 表示成功完成陀螺仪校准
    return 1;
}


/**
 * @brief 读取QMI8658A传感器的六轴数据，进行转换并打印转换后的数据
 *
 * 该函数会调用 QMI8658A_ReadData 函数从 QMI8658A 传感器读取原始的六轴数据，
 * 若读取成功，再调用 QMI8658A_ConvertData 函数根据传入的加速度计和陀螺仪量程
 * 对原始数据进行转换，最后使用 ESP_LOGE 函数将转换后的加速度计和陀螺仪数据打印输出。
 * 如果读取数据失败，会打印错误信息并提前返回。
 *
 * @param accelRange 加速度计的量程，合法值为 2, 4, 8, 16（单位：g）
 * @param gyroRange 陀螺仪的量程，合法值为 16, 32, 64, 128, 256, 512, 1024, 2048（单位：dps）
 *
 * @return 无
 */
void QMI8658A_ReadConvertAndPrint() {
    
    static int i=0;
    
    // 用于存储从传感器读取的原始六轴数据
    int16_t rawData[6];
    // 用于存储转换后的六轴数据
    float convertedData[6],AM=0;

    // 调用 QMI8658A_ReadData 函数读取六轴数据
    if (!QMI8658A_ReadData(rawData)) {
        // 若读取失败，打印错误信息并返回
        ESP_LOGE(TAG, "读取六轴数据失败，无法继续处理。");
        return;
    }

    // 调用 QMI8658A_ConvertData 函数将原始数据进行转换
    QMI8658A_ConvertData(rawData, convertedData, ACCRANGE, GYRRANGE);


    // 计算加速度的模长
    AM= calculateAccelerationMagnitude(convertedData);


    // 打印转换后的加速度计数据，保留小数点后6位
    // ESP_LOGE(TAG, " (g): AX=%d, AY=%d, AZ=%d (dps): GX=%d, GY=%d, GZ=%d", 
    //  rawData[0], rawData[1], rawData[2],rawData[3], rawData[4], rawData[5]);
    if(i==100)
    {
        i=0;
    ESP_LOGE(TAG, " (g): AX=%.3f, AY=%.3f, AZ=%.3f, AM=%.3f (dps): GX=%.3f, GY=%.3f, GZ=%.3f",
              (float)convertedData[0], (float)convertedData[1], (float)convertedData[2],AM,(float)convertedData[3], (float)convertedData[4], (float)convertedData[5]);
    }
               i++;
}

