ATK-MB018模块使用说明
这里以正点原子M48Z-M3最小系统板STM32F103版为例,给大家介绍一下模块和板卡的连接方法。其它板卡与模块的硬件连接方法,请大家在“ATK-MB018六轴陀螺仪模块\3,程序源码\相应板卡例程文件夹\readme.txt”路径下查看。
六轴陀螺仪模块可通过杜邦线与正点原子M48Z-M3最小系统板STM32F103版进行连接,具体的连接关系,如下表所示:

表1.1.1 六轴陀螺仪模块与M48Z-M3最小系统板STM32F103版连接关系
在本实验中,开发板主控芯片通过模拟IIC与六轴陀螺仪模块进行通讯,从而获取模块的三轴加速度、三轴陀螺仪和温度的采样值以及MPL库实现姿态解算后的欧拉角等信息,并实时在串口上打印这些数据,同时可通过开发板上的按键,选择性地将获取到的数据通过串口发送到串口调试助手或通过串口发送到匿名地面站 V4 进行查看。
开发板的LED0闪烁,提示程序运行。
打开本实验的工程文件夹,能够在./Drivers/BSP目录下看到ATK_QMI8658A文件夹,和IIC文件夹,其中ATK_QMI8658A文件夹中就包含了六轴陀螺仪模块的驱动文件和MPL库实现的姿态解算驱动,IIC文件夹中就包含了软件模拟IIC的驱动文件如下图所示:

图2.1.2.1 六轴陀螺仪模块驱动代码

图2.1.2.2 IIC驱动代码
下面将简要介绍atk_qmi8658.c中几个重要的API 函数。
1. 函数atk_qmi8658_init ()
该函数用于初始化六轴陀螺仪模块,,具体的代码,如下所示:
/**
* @brief 初始化QMI8658
* @param 无
* @retval 初始化结果
* @arg 0: 成功
* @arg 1: 失败
*/
uint8_t atk_qmi8658_init(void)
{
iic_init();
atk_qmi8658_reset(); /* 复位传感器 */
if(atk_qmi8658_check_whoami()) /* 检查设备ID是否正确 */
{
return 1;
}
if(atk_qmi8658_calibration())
{
return 1;
}
printf("校准成功!!\r\n");
atk_qmi8658_write_byte(Register_Ctrl1, 0x60); /* I2C驱动 */
atk_qmi8658_write_byte(Register_Ctrl7, 0x00); /* 关闭陀螺仪、加速度计 */
atk_qmi8658_config_reg(0); /* 配置陀螺仪和加速度计的量程和数据输出速率等参数 */
atk_qmi8658_enablesensors(g_imu.cfg.ensensors); /* 使能陀螺仪、加速度计 */
return 0;
}
从上述代码可以看出,首先通过初始化I2C接口,确保开发板可以通过I2C通信与QMI8658传感器进行交互,接着调用函atk_qmi8658_reset()用于复位传感器,将其恢复到默认状态,确保后续操作在已知的起始状态下进行。然后进行传感器校准操作,最后向其寄存器写入对应的配置值。以此完成对六轴陀螺仪模块的初始化,确保QMI8658能够正确工作。
2. 函数atk_qmi8658_get_temperature /atk_qmi8658_read_xyz()
这两个函数分别用于获取六轴陀螺仪模块中温度传感器、加速度传感器和陀螺仪传感器的数据,具体代码,如下所示:
/**
* @brief 获取传感器温度
* @param 无
* @retval 温度值,单位为℃
*/
float atk_qmi8658_get_temperature(void)
{
short temp = 0;
float temp_f = 0;
uint8_t buf[2];
atk_qmi8568_read_nbytes(Register_Tempearture_L, buf, 2);/* 读取温度数据*/
temp = ((short)buf[1] << 8) | buf[0];
temp_f = (float)temp / 256.0f;
return temp_f;
}
/**
* @brief 判断数据更新后,在读取补偿后QMI8658陀螺仪和加速度的数据(推荐使用)
* @param acc : 加速度计 X,Y,Z缓存区;
* @param gyro : 陀螺仪 X,Y,Z缓存区;
* @retval 无
*/
void atk_qmi8658_read_xyz(float *acc, float *gyro)
{
unsigned char status = 0;
unsigned char data_ready = 0;
int retry = 0;
while (retry++ < 3)
{
#if defined(QMI8658_SYNC_SAMPLE_MODE)
atk_qmi8568_read_nbytes(Register_StatusInt, &status, 1);
if (status & 0x01)
{
/* delay 12us <=500Hz, 12us 1000Hz, 4us 2000Hz 2us > 2000Hz */
delay_us(12);
}
if ((status & 0x01) || (status & 0x03))
{
data_ready = 1;
break;
}
#else
/* 检查加速度计和陀螺仪数据是否可用 */
atk_qmi8568_read_nbytes(Register_Status0, &status, 1);
if (status & 0x03)
{
data_ready = 1;
break;
}
#endif
}
if (data_ready)
{
atk_qmi8658_read_sensor_data(acc, gyro);
g_imu.imu[0] = acc[0];
g_imu.imu[1] = acc[1];
g_imu.imu[2] = acc[2];
g_imu.imu[3] = gyro[0];
g_imu.imu[4] = gyro[1];
g_imu.imu[5] = gyro[2];
}
else
{
acc[0] = g_imu.imu[0];
acc[1] = g_imu.imu[1];
acc[2] = g_imu.imu[2];
gyro[0] = g_imu.imu[3];
gyro[1] = g_imu.imu[4];
gyro[2] = g_imu.imu[5];
// printf("数据还未准备好\r\n"); /* 调试使用 */
}
}
从上面的代码中可以看出,获取六轴陀螺仪模块中温度传感器、加速度传感器和陀螺仪传感器的数据就是通过相应的寄存器地址,从寄存器中读取相应的数据即可, 当然,这三个函数还对获取到的数据值做了合法判断等处理,使得数据更加直观可靠。
3. 六轴陀螺仪模块的MPL库姿态解算驱动
该姿态解算库的核心代码参考自:https://github.com/Krasjet/quaternion,感兴趣的同学可自行学习!我们这里重点讲解重要的API函数imu_get_eulerian_angles()。
imu_get_eulerian_angles()该函数主要实现获取欧拉角数据。代码如下:
/**
* @brief 获取欧拉角数据
* @note 姿态解算融合, 核心算法,互补滤波算法,没有使用Kalman滤波算法
* 尽量保证该函数的调用频率为: IMU_DELTA_T , 否则YAW会相应的偏大/偏小
* @param gyro : 3轴陀螺仪数据
* @param acc : 3轴加速度数据
* @param rpy : 欧拉角存放buf
* @param dt : 调用频率
* @retval 无
*/
void imu_get_eulerian_angles(float acc[3], float gyro[3], float *rpy , float dt)
{
float normalise;
float ex, ey, ez;
float halfT = 0.5f * dt;
float accBuf[3] = {0.f};
float tempacc[3];
tempacc[0] = acc[0];
tempacc[1] = acc[1];
tempacc[2] = acc[2];
static unsigned short int uiPostureInitWaitTimer = 200;
if(uiPostureInitWaitTimer > 0)
{
uiPostureInitWaitTimer--;
Kp = 20; /* 初始化开机角度的时候把KP值变大,便于快速收敛到实际值 */
}
else
{
Kp = 1.0;
}
/* 度转弧度 */
gyro[0] = gyro[0];
gyro[1] = gyro[1];
gyro[2] = gyro[2];
/* 加速度计输出有效时,利用加速度计补偿陀螺仪*/
if((acc[0] != 0.0f) || (acc[1] != 0.0f) || (acc[2] != 0.0f))
{
/* 单位化加速计测量值 */
normalise = imu_inv_sqrt(acc[0] * acc[0] + acc[1] * acc[1] + acc[2] *
acc[2]);
acc[0] *= normalise;
acc[1] *= normalise;
acc[2] *= normalise;
/* 加速计读取的方向与重力加速计方向的差值,用向量叉乘计算 */
ex = (acc[1] * rMat[2][2] - acc[2] * rMat[2][1]);
ey = (acc[2] * rMat[2][0] - acc[0] * rMat[2][2]);
ez = (acc[0] * rMat[2][1] - acc[1] * rMat[2][0]);
/* 误差累计,与积分常数相乘 */
exInt += Ki * ex * dt ;
eyInt += Ki * ey * dt ;
ezInt += Ki * ez * dt ;
/* 用叉积误差来做PI修正陀螺零偏,即抵消陀螺读数中的偏移量 */
gyro[0] += Kp * ex + exInt;
gyro[1] += Kp * ey + eyInt;
gyro[2] += Kp * ez + ezInt;
}
/* 一阶近似算法,四元数运动学方程的离散化形式和积分 */
float q0Last = q0;
float q1Last = q1;
float q2Last = q2;
float q3Last = q3;
q0 += (-q1Last * gyro[0] - q2Last * gyro[1] - q3Last * gyro[2]) * halfT;
q1 += ( q0Last * gyro[0] + q2Last * gyro[2] - q3Last * gyro[1]) * halfT;
q2 += ( q0Last * gyro[1] - q1Last * gyro[2] + q3Last * gyro[0]) * halfT;
q3 += ( q0Last * gyro[2] + q1Last * gyro[1] - q2Last * gyro[0]) * halfT;
/* 单位化四元数 */
normalise = imu_inv_sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
q0 *= normalise;
q1 *= normalise;
q2 *= normalise;
q3 *= normalise;
imu_computerotationmatrix(); /* 计算旋转矩阵 */
/*计算roll pitch yaw 欧拉角*/
/* pitch角度计算不采用原生欧拉角计算方法-asinf(rMat[2][0]) * RAD2DEG。而采用
atan2f(rMat[2][0], rMat[2][2]) * RAD2DEG
原因是应用不同,我们只要是要知道遥控器的倾斜角度 */
rpy[0] = asinf(rMat[2][0]) * RAD2DEG; /* -asinf(rMat[2][0]) * RAD2DEG; */
rpy[1] = atan2f(rMat[2][1], rMat[2][2]) * RAD2DEG;
rpy[2] = atan2f(rMat[1][0], rMat[0][0]) * RAD2DEG;
if(0)//(!isGravityCalibrated) /* 未校准 */
{
accBuf[2] = tempacc[0]* rMat[2][0] + tempacc[1] * rMat[2][1] + tempacc[2]
* rMat[2][2]; /*accz*/
imu_calbaseacc(accBuf); /* 计算静态加速度 */
}
}
从上述代码可以看出,首先通过互补滤波算法融合了陀螺仪和加速度计的数据,实时计算欧拉角。加速度计用于提供短期的稳定性修正,陀螺仪提供角速度,算法通过误差修正和积分更新姿态的四元数。最终,通过旋转矩阵计算得到欧拉角。因为转换的过程涉及较多的数学理论,因此在这里不分析具体的转换原理,感兴趣的读者可以自行通过搜索引擎了解。
在图2.1.2.2中,myiic.c 和 myiic.h 是开发板与六轴陀螺仪模块通讯而使用的模拟 IIC 驱动文件,关于模拟 IIC 的驱动介绍,请查看正点原子各个开发板对应的开发指南中模拟 IIC 对应的章节。
实验的测试代码为文件 demo.c,在工程目录下的 User 子目录中。测试代码的入口函数为 demo_run(),具体的代码,如下所示:
/**
* @brief 例程演示入口函数
* @param 无
* @retval 无
*/
void demo_run(void)
{
uint8_t t = 0;
uint8_t key;
uint8_t report = 0;
float gyro[3];
float accel[3];
float euler_angle[3] = {0, 0, 0};
key_init(); /* 初始化按键 */
while(atk_qmi8658_init()) /* 检测不到qmi8658 */
{
printf("QMI8658A Check Failed!\r\n");
delay_ms(500);
LED0_TOGGLE(); /* 红灯闪烁 */
}
printf("QMI8658A Ready!\r\n");
while (1)
{
atk_qmi8658_read_xyz(accel, gyro);
key = key_scan(0);
if (key == KEY0_PRES)
{
/* 切换匿名数据上报开关 */
report = !report;
if (report == 0)
{
printf("UPLOAD OFF \r\n");
}
else
{
printf("UPLOAD ON \r\n");
}
}
if (g_imu_init)
{
/* 获取并显示欧拉角 */
imu_get_eulerian_angles(accel, gyro, euler_angle, IMU_DELTA_T);
if (report != 0)
{
/* 上报匿名状态帧 */
qmi8658_send_data(accel[0], accel[1], accel[2], gyro[0], gyro[1],
gyro[2]); /* 发送加速度+陀螺仪原始数据 */
usart1_report_imu((int)(euler_angle[1] * 100),
(int)(euler_angle[0] * 100), (int)(euler_angle[2] * 100), 0, 0, 0);
}
else
{
/* 获取并显示温度 */
printf("Temp : %.2f ℃\r\n", atk_qmi8658_get_temperature());
printf("Pitch: %.2f°\r\n", euler_angle[0]);
printf("Roll : %.2f°\r\n", euler_angle[1]);
printf("Yaw : %.2f°\r\n", euler_angle[2]);
printf("\r\n");
}
}
if(++t == 50)
{
t = 0;
LED0_TOGGLE();
}
delay_ms(10);
}
}
从上面的代码中可以看出,整个测试代码的逻辑还是比较简单的,就是要注意的是,因为与匿名地面站 V4 通讯的时候,需要传输大量的数据,因此串口的通讯波特率设置为了500000,而与串口调试助手通讯的时候,串口的波特率依然是默认的 115200,因此在进行实验的时候,需要就串口调试助手和匿名地面站做相应的串口配置,才能够正常通讯。
将六轴陀螺仪模块按照第一节“硬件连接”中介绍的连接方式与开发板连接,并将实验代码编译烧录至开发板中,本实验使用串口输出调试信息,因此需将开发板的 PA9连接至 DAP 虚拟串口(或 USB 转 TTL 模块)的 RX引脚。并通过串口调试助手查看实验信息输出,如下图所示:

图2.1.3.1 串口调试助手显示内容
以上就是通过串口调试助手显示六轴陀螺仪中各传感器采集的数据信息。同时,本实验也支持通过匿名地面站 V4,以图形化的形式,展示传感器的数据和 3D 姿态,只需按下按键KEY0,就能够更改串口波特率为500000,然后通过串口上传数据,如下图所示:

图2.1.3.2 匿名地面站V4 3D姿态显示
上图为匿名地面站 V4 展示的 3D 姿态显示图,左上角的 3D 模型,能够根据开发板上报的角度数据,实时地展示开发板的 3D 姿态,并且也可通过右上角的罗盘和下方的数据,实时观察开发板上报的各种姿态数据。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70040860/viewspace-3080098/ ,如需转载,请注明出处,否则将追究法律责任。