簡介
QMI8658A 是上海矽睿(QST)推出的一款高性能 6 軸慣性測量單元(IMU)芯片,集成了一個 3-軸加速度計和一個 3-軸陀螺儀,用于精確感知和追蹤設備的運動與姿態。它支持 I3C、I2C 和 SPI 通信接口,具有 低噪聲、高分辨率(16 位)、寬動態范圍和低功耗 的特點,并內置了 FIFO 緩存及溫度傳感器,適合智能穿戴、無人機、機器人、AR/VR、游戲控制器及其他需要運動檢測的消費電子與工業應用。

QMI8658A和QMI8658C區別
QMI8658系列有兩個版本的芯片,QMI8658A是低噪音版本,QMI8658C是標準版本。
| 芯片 | QMI8658A | QMI8658C |
|---|---|---|
| 傳感器類型 | 6軸(3軸加速度+3軸陀螺儀) | 6軸(3軸加速度+3軸陀螺儀) |
| 陀螺儀量程 | ±16/32/64/128/256/512/1024/2048°/s | ±16/32/64/128/256/512/1024/2048°/s |
| 加速度量程 | ±2/4/8/16 g | ±2/4/8/16 g |
| 最大輸出速率 | 1000Hz | 1000Hz |
| 內置DSP | 有 運動協處理器,用于基本動作檢測和功能(如步數、Tap/動作事件)等 | 有高級DSP協處理器(AttitudeEngine),并可進行復雜運動編碼和傳感器融合 |
| 姿態融合算法 | 無內建高級 9D 融合算法 | 支持 XKF3 9D 融合算法(需外接磁力計) |
| FIFO容量 | 1536字節 | 1536字節 |
| 通訊協議 | I3C, I2C , 3-wire or 4-wire SPI | I3C, I2C3-wire or 4-wire SPI |
| 陀螺儀噪聲(典型) | 13 mdps/√Hz | 15 mdps/√Hz |
| 加速度計噪聲(典型) | 150 μg/√Hz | 200 μg/√Hz |
選型推薦:
如果你需要更低傳感器本底噪聲(更優的原始陀螺/加速度噪聲)并側重于低功耗動作檢測與原始傳感器數據處理,QMI8658A 更合適。
如果你需要板級/系統級的姿態輸出(內置高性能傳感器融合、AttitudeEngine 與指定的方向精度),并想減少主機處理負擔,QMI8658C 是更好的選擇。
QMI8658A引腳定義
上面表格是芯片支持的通訊協議,這里使用的是模塊進行測試,模塊的CS引腳已經拉高到3.3V,所以只能用IIC進行測試,沒法使用SPI通訊。
| 引腳 | 引腳定義 |
|---|---|
| VCC | 供電電壓3.3/5V |
| GND | 供電地 |
| SCL | 串行時鐘線 |
| SDA | 串行數據線 |
| XDA | 接從器件SDA,無從器件不接(或接VDDIO,GND) |
| XCL | 接從器件SCL,無從器件不接(或接VDDIO,GND) |
| ADO | 模塊默認下拉接地( AD0=0,IIC地址:0x6B;AD0=1,IIC地址:0x6A ) |
| INT | 接的是芯片的INT1,INT2上拉到3.3V |
以下是IIC的接線圖,這里模塊的布線已經把外圍電路處理好了,所以可以直接使用IIC進行通訊。
QMI8658A寄存器表
這里提供了兩個通用寄存器存放芯片ID以及版本ID。
1.地址00H的ID寄存器,讀取數據為0x05才是正常。
2.地址01H的Revision ID寄存器,讀取數據為0x7c才是正常。(這里版本號要看對應手冊,目前官網更新最新的是D版本的手冊是0x7c,有一些舊的手冊版本號是0x68,這個要根據自己購買的產品確認好)



代碼驅動
接線
| STM32F103C8T6 | QMI8658 | OLED |
|---|---|---|
| PB10 | -- | SCL |
| PB11 | -- | SDA |
| PB8 | SCL | -- |
| PB9 | SDA | -- |
| 3.3V | VCC | VCC |
| GND | GND | GND |
代碼
mian.c
uint8_t ID,R_ID; //定義用于存放ID號的變量
int16_t AX, AY, AZ, GX, GY, GZ; //定義用于存放各個數據的變量
int main(void)
{
/*模塊初始化*/
OLED_Init(); //OLED初始化
qmi8658_init(); //QMI8658初始化
/*顯示ID號*/
OLED_ShowString(1, 1, "ID:"); //顯示靜態字符串
ID = QMI8658_GetID(); //獲取QMI8658的ID號
OLED_ShowHexNum(1, 4, ID, 2); //OLED顯示ID號
/*顯示REVISION_ID號*/
OLED_ShowString(1, 8, "R_ID:"); //顯示靜態字符串
R_ID = QMI8658_GetRID(); //獲取QMI8658的ID號
OLED_ShowHexNum(1, 13, R_ID, 2); //OLED顯示ID號
Delay_ms(2000);
/*顯示加速度和陀螺儀數據*/
OLED_ShowString(1, 1, "Acc ");
OLED_ShowString(1, 8, "Gyro ");
while (1)
{
QMI8658_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); //獲取QMI8658的數據
OLED_ShowSignedNum(2, 1, AX, 5); //OLED顯示數據
OLED_ShowSignedNum(3, 1, AY, 5);
OLED_ShowSignedNum(4, 1, AZ, 5);
OLED_ShowSignedNum(2, 8, GX, 5);
OLED_ShowSignedNum(3, 8, GY, 5);
OLED_ShowSignedNum(4, 8, GZ, 5);
}
}
QMI8658.c
#include "stm32f10x.h"
#include "math.h"
#include "MyI2C.h"
#include "qmi8658.h"
#include "Delay.h"
/* 全局變量緩存區 */
qmi8658_state g_imu;
/**
* 函 數:QMI8658寫寄存器
* 參 數:RegAddress 寄存器地址,范圍:參考QMI8658手冊的寄存器描述
* 參 數:Data 要寫入寄存器的數據,范圍:0x00~0xFF
* 返 回 值:無
*/
void qmi8658_write_one_byte(uint8_t RegAddress, uint8_t Data)
{
MyI2C_Start(); //I2C起始
MyI2C_SendByte((QMI8658_ADDRESS < 1) | 0x00); //發送從機地址,讀寫位為0,表示即將寫入
MyI2C_ReceiveAck(); //接收應答
MyI2C_SendByte(RegAddress); //發送寄存器地址
MyI2C_ReceiveAck(); //接收應答
MyI2C_SendByte(Data); //發送要寫入寄存器的數據
MyI2C_ReceiveAck(); //接收應答
MyI2C_Stop(); //I2C終止
}
/**
* 函 數:QMI8658讀寄存器
* 參 數:RegAddress 寄存器地址,范圍:參考QMI8658手冊的寄存器描述
* 返 回 值:讀取寄存器的數據,范圍:0x00~0xFF
*/
uint8_t qmi8658_read_one_byte(uint8_t RegAddress)
{
uint8_t Data;
MyI2C_Start(); //I2C起始
MyI2C_SendByte((QMI8658_ADDRESS < 1) | 0x00); //發送從機地址,讀寫位為0,表示即將寫入
MyI2C_ReceiveAck(); //接收應答
MyI2C_SendByte(RegAddress); //發送寄存器地址
MyI2C_ReceiveAck(); //接收應答
MyI2C_Start(); //I2C重復起始
MyI2C_SendByte((QMI8658_ADDRESS < 1) | 0x01); //發送從機地址,讀寫位為1,表示即將讀取
MyI2C_ReceiveAck(); //接收應答
Data = MyI2C_ReceiveByte(); //接收指定寄存器的數據
MyI2C_SendAck(1); //發送應答,給從機非應答,終止從機的數據輸出
MyI2C_Stop(); //I2C終止
return Data;
}
/* 寫多個連續寄存器(從 reg 開始,寫 len 個字節)
* reg: 寄存器地址
* data: 要寫入的字節
* data 指向長度至少為 len 的緩沖區
* 返回 0 = 成功
*/
uint8_t qmi8568_write_nbytes(uint8_t reg, uint8_t *data, uint16_t len)
{
uint16_t i;
MyI2C_Start(); //I2C起始
MyI2C_SendByte((QMI8658_ADDRESS < 1) | 0x00); //發送從機地址,讀寫位為0,表示即將寫入
MyI2C_ReceiveAck(); //接收應答
MyI2C_SendByte(reg); //發送寄存器地址
MyI2C_ReceiveAck(); //接收應答
for (i = 0; i < len; i++)
{
MyI2C_SendByte(data[i]);
MyI2C_ReceiveAck();
}
MyI2C_Stop();
return 0;
}
/* 連續讀多個寄存器(從 reg 開始,讀取 len 字節)
* out_buf: 指向接收緩沖區,至少 len 長
*/
uint8_t qmi8568_read_nbytes(uint8_t reg, uint8_t *out_buf, uint16_t len)
{
uint16_t i;
/* 寫寄存器地址 */
MyI2C_Start();
MyI2C_SendByte((QMI8658_ADDRESS < 1) | 0x00);
MyI2C_ReceiveAck();
MyI2C_SendByte(reg);
MyI2C_ReceiveAck();
/* 重復起始,切換到讀模式 */
MyI2C_Start();
MyI2C_SendByte((QMI8658_ADDRESS < 1) | 0x01);
MyI2C_ReceiveAck();
/* 依次讀取 len 字節;對最后一個字節發送 NACK */
for (i = 0; i < len; i++) {
if (i == (len - 1))
{ out_buf[i] = MyI2C_ReceiveByte(); /* 最后一個字節 NACK */
MyI2C_SendAck(1);
}
else
{ out_buf[i] = MyI2C_ReceiveByte(); /* 發送 ACK,繼續接收 */
MyI2C_SendAck(0);
}
}
MyI2C_Stop();
return 0;
}
/**
* 函 數:QMI8658獲取ID號
* 參 數:無
* 返 回 值:QMI8658的ID號
*/
uint8_t QMI8658_GetID(void)
{
return qmi8658_read_one_byte(Register_WhoAmI); //返回WHO_AM_I寄存器的值
}
/**
* 函 數:QMI8658獲取Revision ID號
* 參 數:無
* 返 回 值:QMI8658的Revision ID號
*/
uint8_t QMI8658_GetRID(void)
{
return qmi8658_read_one_byte(Register_Revision); //返回Revision寄存器的值
}
/**
* @brief 陀螺儀校準
* @param 無
* @retval 檢查結果
* @arg 0: 校準成功
* @arg 1: 校準失敗
*/
uint8_t qmi8658_calibration(void)
{
uint8_t sta = 0;
qmi8658_write_one_byte(Register_Ctrl7, 0x00); /* 關閉陀螺儀、加速度計 */
qmi8658_write_one_byte(Register_Ctrl9, 0xA2);
Delay_ms(2000);
sta = qmi8658_read_one_byte(Register_COD_Status);
if(sta == 0x00)
{
return 0;
}else return 1;
}
/**
* @brief 傳感器軟件復位
* @param 無
* @retval 無
*/
void qmi8658_reset(void)
{
qmi8658_write_one_byte(Register_Reset, 0xB0); /* 復位QMI8658 */
Delay_ms(150);
}
/**
* @brief 讀取初始狀態寄存器
* @param 無
* @retval 讀取結果
*/
uint8_t qmi8658_read_statusint(void)
{
return qmi8658_read_one_byte(Register_StatusInt);
}
/**
* @brief 讀取狀態寄存器0
* @param 無
* @retval 讀取結果
*/
uint8_t qmi8658_read_status0(void)
{
return qmi8658_read_one_byte(Register_Status0);
}
/**
* @brief 讀取狀態寄存器1
* @param 無
* @retval 讀取結果
*/
uint8_t qmi8658_read_status1(void)
{
return qmi8658_read_one_byte(Register_Status1);
}
/*****************************************************************************************************************/
/**
* @brief 配置加速度計參數
* @param range :量程
* @param odr :odr輸出速率
* @param lpfEnable :低通濾波器 :Qmi8658Lpf_Enable 打開,Qmi8658Lpf_Disable 關閉
* @param stEnable :陀螺儀自檢 :Qmi8658St_Enable 自檢,Qmi8658St_Disable 不自檢
* @retval 無
*/
void qmi8658_config_acc(enum qmi8658_accrange range, enum qmi8658_accodr odr, enum qmi8658_LpfConfig lpfEnable, enum qmi8658_StConfig stEnable)
{
unsigned char ctl_dada;
switch (range)
{
case Qmi8658accrange_2g:
g_imu.ssvt_a = (1 < < 14);
break;
case Qmi8658accrange_4g:
g_imu.ssvt_a = (1 < < 13);
break;
case Qmi8658accrange_8g:
g_imu.ssvt_a = (1 < < 12);
break;
case Qmi8658accrange_16g:
g_imu.ssvt_a = (1 < < 11);
break;
default:
range = Qmi8658accrange_8g;
g_imu.ssvt_a = (1 < < 12);
break;
}
if (stEnable == Qmi8658St_Enable)
{
ctl_dada = (unsigned char)range | (unsigned char)odr | 0x80;
}
else
{
ctl_dada = (unsigned char)range | (unsigned char)odr;
}
qmi8658_write_one_byte(Register_Ctrl2, ctl_dada);
/* set LPF & HPF */
qmi8568_read_nbytes(Register_Ctrl5, &ctl_dada, 1);
ctl_dada &= 0xf0;
if (lpfEnable == Qmi8658Lpf_Enable)
{
ctl_dada |= A_LSP_MODE_3;
ctl_dada |= 0x01;
}
else
{
ctl_dada &= ~0x01;
}
qmi8658_write_one_byte(Register_Ctrl5, ctl_dada);
}
/**
* @brief 配置陀螺儀參數
* @param range :量程
* @param odr :odr輸出速率
* @param lpfEnable :低通濾波器 :Qmi8658Lpf_Enable 打開,Qmi8658Lpf_Disable 關閉
* @param stEnable :陀螺儀自檢 :Qmi8658St_Enable 自檢,Qmi8658St_Disable 不自檢
* @retval 無
*/
void qmi8658_config_gyro(enum qmi8658_gyrrange range, enum qmi8658_gyrodr odr, enum qmi8658_LpfConfig lpfEnable, enum qmi8658_StConfig stEnable)
{
/* Set the CTRL3 register to configure dynamic range and ODR */
unsigned char ctl_dada;
/* Store the scale factor for use when processing raw data */
switch (range)
{
case Qmi8658gyrrange_16dps:
g_imu.ssvt_g = 2048;
break;
case Qmi8658gyrrange_32dps:
g_imu.ssvt_g = 1024;
break;
case Qmi8658gyrrange_64dps:
g_imu.ssvt_g = 512;
break;
case Qmi8658gyrrange_128dps:
g_imu.ssvt_g = 256;
break;
case Qmi8658gyrrange_256dps:
g_imu.ssvt_g = 128;
break;
case Qmi8658gyrrange_512dps:
g_imu.ssvt_g = 64;
break;
case Qmi8658gyrrange_1024dps:
g_imu.ssvt_g = 32;
break;
case Qmi8658gyrrange_2048dps:
g_imu.ssvt_g = 16;
break;
default:
range = Qmi8658gyrrange_512dps;
g_imu.ssvt_g = 64;
break;
}
if (stEnable == Qmi8658St_Enable)
{
ctl_dada = (unsigned char)range | (unsigned char)odr | 0x80;
}
else
{
ctl_dada = (unsigned char)range | (unsigned char)odr;
}
qmi8658_write_one_byte(Register_Ctrl3, ctl_dada);
/* Conversion from degrees/s to rad/s if necessary */
/* set LPF & HPF */
qmi8568_read_nbytes(Register_Ctrl5, &ctl_dada, 1);
ctl_dada &= 0x0f;
if (lpfEnable == Qmi8658Lpf_Enable)
{
ctl_dada |= G_LSP_MODE_3;
ctl_dada |= 0x10;
}
else
{
ctl_dada &= ~0x10;
}
qmi8658_write_one_byte(Register_Ctrl5, ctl_dada);
}
/**
* @brief 使能陀螺儀、加速度計
* @param enableFlags :
* QMI8658_DISABLE_ALL : 都不使能
* QMI8658_ACC_ENABLE : 使能加速度計
* QMI8658_GYR_ENABLE : 使能陀螺儀
* QMI8658_ACCGYR_ENABLE: 使能陀螺儀、加速度計
* @retval 無
*/
void qmi8658_enablesensors(unsigned char enableFlags)
{
#if defined(QMI8658_SYNC_SAMPLE_MODE)
g_imu.cfg.syncsample = 1;
qmi8658_enable_ahb_clock(0);
qmi8658_write_one_byte(Register_Ctrl7, enableFlags | 0x80);
#else
qmi8658_write_one_byte(Register_Ctrl7, enableFlags);
#endif
g_imu.cfg.ensensors = enableFlags & 0x03;
Delay_ms(2);
}
/**
* @brief 配置QMI8658陀螺儀和加速度計的量程、輸出頻率參數等
* @param low_power : 0: 正常模式 1:低功耗模式
* @retval 無
*/
void qmi8658_config_reg(unsigned char low_power)
{
qmi8658_enablesensors(QMI8658_DISABLE_ALL);
if (low_power)
{
g_imu.cfg.ensensors = QMI8658_ACC_ENABLE;
g_imu.cfg.accrange = Qmi8658accrange_8g;
g_imu.cfg.accodr = Qmi8658accodr_LowPower_21Hz;
g_imu.cfg.gyrrange = Qmi8658gyrrange_1024dps;
g_imu.cfg.gyrodr = Qmi8658gyrodr_250Hz;
}
else
{
g_imu.cfg.ensensors = QMI8658_ACCGYR_ENABLE; /* 使能陀螺儀、加速度計 */
g_imu.cfg.accrange = Qmi8658accrange_16g; /* ±16g */
g_imu.cfg.accodr = Qmi8658accodr_500Hz; /* 500Hz采樣 */
g_imu.cfg.gyrrange = Qmi8658gyrrange_128dps; /* ±128dps */
g_imu.cfg.gyrodr = Qmi8658gyrodr_500Hz; /* 500Hz采樣 */
}
if (g_imu.cfg.ensensors & QMI8658_ACC_ENABLE)
{
qmi8658_config_acc(g_imu.cfg.accrange, g_imu.cfg.accodr, Qmi8658Lpf_Enable, Qmi8658St_Enable); /* 設置參數并開啟加速度計自檢和低通濾波器 */
}
if (g_imu.cfg.ensensors & QMI8658_GYR_ENABLE)
{
qmi8658_config_gyro(g_imu.cfg.gyrrange, g_imu.cfg.gyrodr, Qmi8658Lpf_Enable, Qmi8658St_Enable);/* 設置參數并開啟陀螺儀自檢和低通濾波器 */
}
}
/**
* @brief 初始化QMI8658
* @param 無
* @retval 初始化結果
* @arg 0: 成功
* @arg 1: 失敗
*/
uint8_t qmi8658_init(void)
{
MyI2C_Init(); //先初始化底層的I2C
qmi8658_reset(); /* 復位傳感器 */
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;
}
/**
* 函 數:QMI8658獲取數據
* 參 數:AccX AccY AccZ 加速度計X、Y、Z軸的數據,使用輸出參數的形式返回,范圍:-32768~32767
* 參 數:GyroX GyroY GyroZ 陀螺儀X、Y、Z軸的數據,使用輸出參數的形式返回,范圍:-32768~32767
* 返 回 值:無
*/
void QMI8658_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
uint8_t DataH, DataL; //定義數據高8位和低8位的變量
DataH = qmi8658_read_one_byte(Register_Ax_H); //讀取加速度計X軸的高8位數據
DataL = qmi8658_read_one_byte(Register_Ax_L); //讀取加速度計X軸的低8位數據
*AccX = (DataH < < 8) | DataL; //數據拼接,通過輸出參數返回
DataH = qmi8658_read_one_byte(Register_Ay_H); //讀取加速度計Y軸的高8位數據
DataL = qmi8658_read_one_byte(Register_Ay_L); //讀取加速度計Y軸的低8位數據
*AccY = (DataH < < 8) | DataL; //數據拼接,通過輸出參數返回
DataH = qmi8658_read_one_byte(Register_Az_H); //讀取加速度計Z軸的高8位數據
DataL = qmi8658_read_one_byte(Register_Az_L); //讀取加速度計Z軸的低8位數據
*AccZ = (DataH < < 8) | DataL; //數據拼接,通過輸出參數返回
DataH = qmi8658_read_one_byte(Register_Gx_H); //讀取陀螺儀X軸的高8位數據
DataL = qmi8658_read_one_byte(Register_Gx_L); //讀取陀螺儀X軸的低8位數據
*GyroX = (DataH < < 8) | DataL; //數據拼接,通過輸出參數返回
DataH = qmi8658_read_one_byte(Register_Gy_H); //讀取陀螺儀Y軸的高8位數據
DataL = qmi8658_read_one_byte(Register_Gy_L); //讀取陀螺儀Y軸的低8位數據
*GyroY = (DataH < < 8) | DataL; //數據拼接,通過輸出參數返回
DataH = qmi8658_read_one_byte(Register_Gz_H); //讀取陀螺儀Z軸的高8位數據
DataL = qmi8658_read_one_byte(Register_Gz_L); //讀取陀螺儀Z軸的低8位數據
*GyroZ = (DataH < < 8) | DataL; //數據拼接,通過輸出參數返回
}
現象

總結
這里顯示的是實時從寄存器獲取的數據,如果要更精確的數據,需要對代碼進行加算法去獲取更準確的數據再開發。
需要代碼的可以在評論區留言郵箱哦!
審核編輯 黃宇
-
STM32F103
+關注
關注
34文章
495瀏覽量
67706
發布評論請先 登錄
無人機IMU 雙核心深度解析:加速度計與陀螺儀的分工、價值及融合邏輯
STM32H5開發陀螺儀LSM6DSV16X(1)----輪詢獲取陀螺儀數據
什么是光纖陀螺儀?陀螺儀有哪些作用?
MEMS陀螺儀正在取代光纖陀螺儀?
ADXRS622角速度陀螺儀技術手冊
ADXRS620角速度陀螺儀技術手冊
ADXRS624角速度陀螺儀技術手冊
ADXRS623角速度陀螺儀技術手冊
ADXRS453高性能數字輸出陀螺儀技術手冊
基于STM32F103驅動QMI8658A輸出加速度陀螺儀數據
評論