#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++; }