超级版主
 
- 积分
- 5691
- 金钱
- 5691
- 注册时间
- 2019-5-8
- 在线时间
- 1532 小时
|
|
第四十五章 QMI8658A六轴传感器实验
1)实验平台:正点原子STM32H7R7开发板
2)章节摘自【正点原子】STM32H7R7开发指南 V1.1
3)购买链接: https://detail.tmall.com/item.htm?id=820823382459
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32h7rx.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子STM32开发板技术交流群:756580169
本章,我们将介绍一款高性价比六轴(三轴角速度(陀螺仪)+三轴加速度)传感器:QMI8658A,该传感器可广泛用于四轴、平衡车和空中鼠标等设计,性价比极高,具有非常广泛的应用范围。正点原子STM32H7R7开发板自带了QMI8658A传感器。本章我们将使用STM32H7R7来驱动QMI8658A,读取其原始数据,并利用MPL库实现姿态解算,结合匿名四轴上位机软件和LCD显示,教大家如何使用这款功能强大的六轴传感器。
本章分为如下几个小节:
45.1 QMI8658A介绍
45.2 硬件设计
45.3 软件设计
45.4 下载验证
45.1 QMI8658A介绍
45.1.1 QMI8658A简介
QMI8658A是一款针对大众市场,高性价比的六轴传感器,具有:功耗低、噪声低、封装小的特点。
QMI8658A内部集成有3轴陀螺仪和3轴加速度计;可以通过I3C、I2C和3线或4线SPI的接口和单片机进行数据交互,传输速率可达400kHZ/s。陀螺仪的角速度测量范围最高达±2048°/s,具有良好的动态响应特性。加速度计的测量范围最大为±16g(g为重力加速度),静态测量精度高。广泛应用于智能手机、游戏控制器、遥控器和汽车安全系统等场景。
QMI8658A的特点包括:
① 陀螺仪13 mdps/Hz的低噪声,低延迟,宽带宽
②150μg/Hz的加速度计噪声
③自带一个温度数字传感器
④可编程数字滤波器
⑤集成计步器,轻拍,任意运动,移动,显著运动检测
⑥自带1536字节FIFO可用于缓冲传感器数据,降低系统功耗
⑦超小封装尺寸:2.5×3.0×0.86mm(LGA)
QMI8658A传感器的检测轴如图45.1.1.1所示:
图45.1.1.1QMI8658A检测轴及其方向
QMI8658A的内部框图如图45.1.1.2所示:
图45.1.1.2 QMI8658A框图
其中,SCL和SDA可以连接MCU的I2C、I3C和SPI接口,MCU通过这些接口来控制QMI8658A,我们例程中用的就是IIC接口。另外,SA0是从IIC接口(接MCU)的地址控制引脚,该引脚控制IIC地址的最低位。如果接GND,则QMI8658A的IIC地址是:0X6B,如果接VDD,则是0X6A,注意:这里的地址是不包含数据传输的最低位的(最低位用来表示读写)!!
在STM32开发板上,SA0是接VCC的,所以QMI8658A的IIC地址是0X6A(不含最低位),IIC通信的时序我们在之前已经介绍过(第三十六章,IIC实验),这里就不再细说了。
45.1.2 QMI8658A寄存器
在这里简单介绍一下本实验用到的QMI8658A比较重要的寄存器。
陀螺仪配置寄存器(Gyroscope Settings)
该寄存器地址为:0X04,各位描述如表45.1.2.1所示:
表45.1.2.1 陀螺仪配置寄存器
该寄存器我们关心gFS[2:0]和gODR[3:0]这些位,gFS[2:0]用于设置陀螺仪的满量程范围:000,±16dps;001,±32dps;010,±64dps;011,±128dps;100,±256dps;101,±512dps;110,±1024dps;111,±2048dps;我们一般设置为011,即±128dps,因为陀螺仪的ADC为16位分辨率,所以得到灵敏度为:65536/256=256LSB(dps)。
gODR[3:0]位用于设置陀螺仪输出数据速率,我们例程中设置为0100,即约500Hz的速率。
加速度传感器配置寄存器(Accelerometer Settings)
该寄存器地址为:0X03,各位描述如下表所示:
表45.1.2.2 加速度传感器配置寄存器
该寄存器我们关心aFS[2:0]和aODR[3:0]这些位,aFS[2:0]用于设置加速度计的满量程范围:000,±2g;001,±4g;010,±8g;011,±16g;我们一般设置为011,即±16g,因为加速度传感器的ADC也是16位分辨率,所以得到灵敏度为:65536/32=2048LSB/g。
aODR[3:0]位用于设置加速度计输出数据速率,我们例程中设置为0100,即约500Hz的速率。
传感器数据处理寄存器(Sensor Data Processing Settings)
该寄存器的地址为:0X06,各位描述如表45.1.2.3所示:
表45.1.2.3 传感器数据处理寄存器
这里,我们主要关心数字低通滤波器(LPF_MODE)的设置位,即:gLPF_MODE[1:0]和aLPF_MODE[1:0],陀螺仪和加速度传感器分别根据这两个位的配置进行过滤。首先看陀螺仪gLPF_MODE[1:0]不同配置对应的过滤情况如表45.1.2.4所示:
表45.1.2.4 gLPF_MODE配置表
再看加速度aLPF_MODE[1:0]对应的过滤情况如表45.1.2.5所示:
表45.1.2.5 aLPF_MODE配置表
在例程中,我们将加速度传感器和陀螺仪的低通过滤器模式选择位:gLPF_MODE[1:0]和aLPF_MODE[1:0]都设置为11,即ODR的13.37%。
温度传感器数据输出寄存器(Temp Sensor Output)
该寄存器地址为:0X33-0X34,各位描述如表45.1.2.6所示:
表45.1.2.6 温度传感器数据输出寄存器
这个寄存器可以读取温度传感器的值,可以通过读取0X33(高8位)和0X34(低8位)寄存器得到,温度换算公式为:
T= TEMP_H + (TEMP_L/256) 其中,T为计算得到的温度值,单位为℃,TEMP_H(L)为从0X33和0X34读到的温度传感器值。
接下来,加速度传感器数据输出寄存器,也有6个,地址为:0X35~0X3A,通过读取这6个寄存器,就可以读到加速度传感器x/y/z轴的值,比如读x轴的数据,可以通过读取0X35(高8位)和0X36(低8位)寄存器得到,其他轴以此类推。
同样,我们看看陀螺仪数据输出寄存器,总共由6个寄存器组成,地址为:0X3B~0X40,通过读取0X3B(高8位)和0X3C(低8位)寄存器得到,其他轴以此类推。
关于QMI8658A的基础介绍,我们就介绍到这。QMI8658A的详细资料和相关寄存器介绍,请参考光盘:7,硬件资料→2,芯片资料→C3021082_姿态传感器-陀螺仪_QMI8658A_规格书_WJ411228.pdf这个文档,供大家参考学习。
45.2 硬件设计
1. 例程功能
本实验采用STM32H7R7的2个普通IO连接QMI8658A(IIC),本章实验功能简介:程序先初始化QMI8658A,然后利用imu姿态解算的计算,最后,在死循环里面不停读取:温度传感器、加速度传感器、陀螺仪、姿态解算后的欧拉角等数据,通过串口上报给上位机(温度不上报),利用上位机软件(ANO_TC匿名科创地面站v4.exe),可以实时显示QMI8658A的传感器状态曲线,并显示3D姿态,可以通过KEY0按键开启/关闭数据上传功能。同时,在LCD模块上面显示温度和欧拉角等信息。DS0来指示程序正在运行。
2. 硬件资源
1)LED灯
LED0 – PD14
2 ) 独立按键
KEY0 – PE9
3)串口1 (波特率:500000,PB14/PB15连接在板载USB转串口芯片CH340上面)
4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)
5)QMI8658A传感器
3. 原理图
QMI8658A与STM32的连接关系,如下图所示:
图45.2.1 QMI8658A与STM32连接原理图
从上图可以看出,QMI8658A的SCL和SDA与STM32H7R7开发板的PF1和PF0连接。图中,SA0接的VCC,所以QMI8658A的器件地址是:0X6A。
45.3 程序设计
45.3.1 程序解析
1. QMI8658A驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。QMI8658A驱动源码包括两个文件:qmi8658.c和qmi8658.h。
qmi8658.h的文件是一些函数的定义,我们直接看qmi8658.c文件。
下面来看一下qmi8658_read_nbytes函数,实现QMI8658A芯片指定地址读取N字节数据,代码如下:
- /**
- * @brief 从QMI8568读取N字节数据
- * [url=home.php?mod=space&uid=271674]@param[/url] reg: 寄存器地址
- * @param date: 数据存储buf
- * @param len: 数据长度
- * @retval 读出结果
- * @retval 0, 操作成功
- * 其他, 操作失败
- */
- int qmi8568_read_nbytes(uint8_t reg, uint8_t *date, uint8_t len)
- {
- uint8_t i;
-
- iic_start();
- iic_send_byte((QMI8658_ADDR << 1) | 0x00);
- if (iic_wait_ack() != 0)
- {
- iic_stop();
- return 1;
- }
- iic_send_byte(reg);
- if (iic_wait_ack() != 0)
- {
- iic_stop();
- return 1;
- }
-
- iic_start();
- iic_send_byte((QMI8658_ADDR << 1) | 0x01);
- if (iic_wait_ack() != 0)
- {
- iic_stop();
- return 1;
- }
- for (i = 0; i < len; i++)
- {
- date[i] = iic_read_byte((i == (len - 1)) ? 0 : 1);
- }
- iic_stop();
-
- return 0;
- }
复制代码 该函数首先调用iic_start函数产生起始信号,然后调用iic_send_byte函数发送第1个字节数据设备写地址,等待QMI8658A设备返回应答信号;收到应答信号后,继续发送第2个1字节数据内存地址reg;等待接收应答后,发送第3个1字节数据设备读地址;最后QMI8658A最先收到的是状态字节(一个字节),随后是读取的数据(数据都是两个字节),返回应答信号,主机调用iic_stop函数产生停止信号终止数据传输,等待QMI8658A读取完毕。
接下来看一下qmi8658_write_nbytes函数,实现从QMI8658A芯片指定地址写入数据,其定义如下:
- /**
- * @brief QMI8568写入N字节数据
- * @param reg: 寄存器地址
- * @param data: 写入数据
- * @param len: 数据长度
- * @retval 写入结果
- * @arg 0: 成功
- * @arg 1: 失败
- */
- uint8_t qmi8658_write_nbytes(uint8_t reg, uint8_t* data, uint8_t len)
- {
- uint8_t i;
-
- iic_start();
- iic_send_byte((QMI8658_ADDR << 1) | 0x00);
- if (iic_wait_ack() != 0)
- {
- iic_stop();
- return 1;
- }
- iic_send_byte(reg);
- if (iic_wait_ack() != 0)
- {
- iic_stop();
- return 1;
- }
- for (i = 0; i < len; i++)
- {
- iic_send_byte(data[i]);
- if (iic_wait_ack() != 0)
- {
- iic_stop();
- return 1;
- }
- }
- iic_stop();
-
- return 0;
- }
复制代码 该函数首先调用iic_start函数产生起始信号,然后调用iic_send_byte函数发送第1个字节数据设备写地址,等待QMI8658A设备返回应答信号;收到应答信号后,继续发送第2个1字节写入数据内存地址reg;等待接收应答后,发送第3个1字节数据设备写地址;QMI8658A设备写完数据,返回应答信号,主机调用iic_stop函数产生停止信号终止数据传输,等待QMI8658A写入完毕。
最后,我们介绍一下读取数据函数,代码如下:
- /**
- * @brief 获取传感器温度
- * @param 无
- * @retval 温度值,单位为℃
- */
- float qmi8658_get_temperature(void)
- {
- short temp = 0;
- float temp_f = 0;
- uint8_t buf[2];
- 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 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)
- qmi8568_read_nbytes(Register_StatusInt, &status, 1);
- if (status & 0x01)
- {
- delay_us(12); /* delay 12us <=500Hz */
- }
- if ((status & 0x01) || (status & 0x03))
- {
- data_ready = 1;
- break;
- }
- #else
- /* 检查加速度计和陀螺仪数据是否可用 */
- qmi8568_read_nbytes(Register_Status0, &status, 1);
- if (status & 0x03)
- {
- data_ready = 1;
- break;
- }
- #endif
- }
- if (data_ready)
- {
- 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"); /* 调试使用 */
- }
- }
复制代码 qmi8658_get_temperature函数:读取温度数据。
qmi8658_read_sensor_data函数:读取补偿后加速度计和陀螺仪的x,y,z轴原始数据。
下面看一下QMI8658A的初始化函数,其定义如下:
- /**
- * @brief 初始化QMI8658
- * @param 无
- * @retval 初始化结果
- * @arg 0: 成功
- * @arg 1: 失败
- */
- uint8_t qmi8658_init(void)
- {
- iic_init();
- qmi8658_reset(); /* 复位传感器 */
-
- if(qmi8658_check_whoami()) /* 检查设备ID是否正确 */
- {
- return 1;
- }
- if(qmi8658_calibration())
- {
- return 1;
- }
- printf("校准成功!!\r\n");
- qmi8658_write_one_byte(Register_Ctrl1, 0x60); /* I2C驱动 */
- qmi8658_write_one_byte(Register_Ctrl7, 0x00); /* 关闭陀螺仪、加速度计 */
-
- qmi8658_config_reg(0);
- /* 配置陀螺仪和加速度计的量程和数据输出速率等参数 */
- qmi8658_enablesensors(g_imu.cfg.ensensors); /* 使能陀螺仪、加速度计 */
- return 0;
- }
复制代码 在初始化函数中,我们主要对该模块的加速度、陀螺仪,还有温度传感器的使能和设置。
2. main.c代码
- /**
- * @brief 通过串口1上报结算后的姿态数据给电脑
- * @param roll : 横滚角.单位0.1度。 -9000 -> 9000 对应 -90.00 -> 90.00度
- * @param pitch : 俯仰角.单位 0.1度。-18000 -> 18000 对应 -180.00 -> 180.00 度
- * @param yaw : 航向角.单位为0.1度 -18000 -> 18000 对应 -180.00 -> 180.00度
- * @param prs : 气压计高度,单位:cm
- * @param fly_mode : 飞行模式
- * @param armed : 锁定状态
- * @retval 无
- */
- void usart1_report_imu(short roll, short pitch, short yaw, int prs, uint8_t fly_mode, uint8_t armed)
- {
- uint8_t tbuf[12];
-
- tbuf[0] = (roll >> 8) & 0XFF;
- tbuf[1] = roll & 0XFF;
- tbuf[2] = (pitch >> 8) & 0XFF;
- tbuf[3] = pitch & 0XFF;
- tbuf[4] = (yaw >> 8) & 0XFF;
- tbuf[5] = yaw & 0XFF;
- tbuf[6] = (prs >> 24) & 0XFF;
- tbuf[7] = (prs >> 16) & 0XFF;
- tbuf[8] = (prs >> 8) & 0XFF;
- tbuf[9] = prs & 0XFF;
- tbuf[10] = fly_mode;
- tbuf[11] = armed;
- usart1_niming_report(0X01, tbuf, 12); /* 状态帧,0X01 */
- }
- int main(void)
- {
- uint8_t t = 0;
- uint8_t key;
- uint8_t report = 0;
- float temperature;
- float gyro[3];
- float accel[3];
- float euler_angle[3] = {0, 0, 0};
-
- sys_mpu_config(); /* 配置MPU */
- sys_cache_enable(); /* 使能Cache */
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(300, 6, 2); /* 配置时钟,600MHz */
- delay_init(600); /* 初始化延时 */
- usart_init(500000); /* 初始化串口 */
- led_init(); /* 初始化LED */
- key_init(); /* 初始化按键 */
- hyperram_init(); /* 初始化HyperRAM */
- lcd_init(); /* 初始化LCD */
-
- lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
- lcd_show_string(30, 70, 200, 16, 16, "QMI8658A TEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
- lcd_show_string(30, 110, 200, 16, 16, "KEY0:UPLOAD ON/OFF", RED);
-
- while (qmi8658_init() != 0) /* 初始化QMI8658A */
- {
- lcd_show_string(30, 130, 200, 16, 16, "QMI8658A Error!", RED);
- delay_ms(500);
- lcd_show_string(30, 130, 200, 16, 16, "Please Check! ", RED);
- delay_ms(500);
- LED0_TOGGLE();
- }
-
- lcd_show_string(30, 130, 200, 16, 16, "QMI8658A Ready!", RED);
- lcd_show_string(30, 150, 200, 16, 16, "UPLOAD OFF", BLUE);
- lcd_show_string(30, 170, 200, 16, 16, "Temp : . C", BLUE);
- lcd_show_string(30, 190, 200, 16, 16, "Pitch: . C", BLUE);
- lcd_show_string(30, 210, 200, 16, 16, "Roll : . C", BLUE);
- lcd_show_string(30, 230, 200, 16, 16, "Yaw : . C", BLUE);
- while (1)
- {
- qmi8658_read_xyz(accel, gyro);
- key = key_scan(0);
- if (key == KEY0_PRES)
- {
- /* 切换匿名数据上报开关 */
- report = !report;
- if (report == 0)
- {
- lcd_show_string(30, 150, 200, 16, 16, "UPLOAD OFF", BLUE);
- }
- else
- {
- lcd_show_string(30, 150, 200, 16, 16, "UPLOAD ON ", BLUE);
- }
- }
-
- /* 获取并显示温度 */
- show_data(30, 170, qmi8658_get_temperature());
-
- /* 获取并显示欧拉角 */
- if (g_imu_init)
- {
- imu_get_eulerian_angles(accel, gyro, euler_angle, IMU_DELTA_T);
- show_data(30, 190, euler_angle[0]);
- show_data(30, 210, euler_angle[1]);
- show_data(30, 230, euler_angle[2]);
-
- 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); /* Pitch和Roll角位置调换 */
- }
- }
- if (++t == 100)
- {
- t = 0;
- LED0_TOGGLE();
- }
-
- delay_ms(10);
- }
- }
复制代码 此部分代码除了main函数,还有几个函数,用于上报数据给上位机软件,可以通过上位机软件显示六轴传感器波形,以及3D姿态显示,有助于更好的调试QMI8658A。上位机软件使用ANO_TC匿名科创地面站v4.exe,该软件在:开发板光盘→6,软件资料→软件→5,其他软件→匿名地面站文件夹里可以找到,该软件的使用方法,见该文件夹下的:飞控通信协议v1.3-0720.pdf,这里我们不做介绍。其中,usart1_niming_report函数用于将数据打包、计算校验和,然后上报给匿名地面站上位机软件。qmi8658_send_data函数用于上报加速度和陀螺仪的原始数据,可用于波形显示传感器数据,通过传感器帧(0X02)发送。而uasrt1_report_imu函数,则用于发送匿名状态帧,可以实时3D显示QMI8658A的姿态,传感器数据等,通过状态帧(0X01)发送。
下面我们来看main函数,利用上位机软件(ANO_TC匿名科创地面站v4.exe),可以实时显示QMI8658A的传感器状态曲线,并显示3D姿态,可以通过KEY0按键开启/关闭数据上传功能。同时,在LCD模块上面显示温度和欧拉角等信息。
45.4 下载验证
将程序下载到开发板后,可以看到LCD显示的内容如图45.4.1所示:
图45.4.1 程序运行效果
屏幕显示了QMI8658A的温度、俯仰角(Pitch)、横滚角(Roll)和航向角(Yaw)的数值。然后,我们可以晃动开发板,看看各角度的变化。
另外,模块默认是关闭状态,通过按下KEY0可以开启或关闭数据上报,在开启状态下,我们可以打开:ANO_TC匿名科创地面站v4.exe这个软件,接收STM32H7R7上传的数据,从而图形化显示飞行姿态,如图45.4.2所示:
图45.4.2 飞控状态显示 |
|