2026-01-20 16:55:17 +08:00

1007 lines
37 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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位设为1CTRL3第7位 = 1b1
// 从寄存器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位设为0CTRL3第7位 = 0b1
// 再次从寄存器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_CALIBRATION0xA2指令
// 向寄存器CTRL9写入0xA2启动陀螺仪的按需校准操作
if (!i2cwrite(CTRL9, 0xA2))
{
// 若写入失败记录错误日志并返回0表示校准失败
ESP_LOGE(TAG, "发送CTRL_CMD_ON_DEMAND_CALIBRATION0xA2指令失败");
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_ACK0x00来确认
// 向寄存器CTRL9写入0x00确认校准操作开始
if (!i2cwrite(CTRL9, 0x00))
{
// 若写入失败记录错误日志并返回0表示校准失败
ESP_LOGE(TAG, "发送CTRL_CMD_ACK0x00指令失败");
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协议中写入0x12CTRL_CMD_AHB_CLOCK_GATING
if (!i2cwrite(CTRL9, 0x12))
{
// 若写入失败记录错误日志并返回0表示初始化失败
ESP_LOGE(TAG, "发送CTRL_CMD_ON_DEMAND_CALIBRATION0x12指令失败");
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_ACK0x00来确认
// 向寄存器CTRL9写入0x00确认操作
if (!i2cwrite(CTRL9, 0x00))
{
// 若写入失败记录错误日志并返回0表示初始化失败
ESP_LOGE(TAG, "发送CTRL_CMD_ACK0x00指令失败");
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++;
}