Skip to content

Commit 5255765

Browse files
authored
Merge pull request #705 from xyzroe/feat-qmi8658c
feat(qmi8658c): add driver for QMI8658C 6-axis IMU sensor
2 parents b7e4592 + fd5897c commit 5255765

File tree

15 files changed

+673
-0
lines changed

15 files changed

+673
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/example
248248
| **hmc5883l** | Driver for 3-axis digital compass HMC5883L and HMC5983L | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3, esp32s3, esp32c2, esp32c6, esp32h2, esp32p4, esp32c5, esp32c61 | yes |
249249
| **lsm303** | Driver for LSM303 3-axis accelerometer and magnetometer sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3, esp32s3, esp32c2, esp32c6, esp32h2, esp32p4, esp32c5, esp32c61 | yes |
250250
| **qmc5883l** | Driver for QMC5883L 3-axis magnetic sensor | BSD-3-Clause | esp32, esp8266, esp32s2, esp32c3, esp32s3, esp32c2, esp32c6, esp32h2, esp32p4, esp32c5, esp32c61 | yes |
251+
| **qmi8658c** | Driver for QMI8658C 6-axis IMU sensor | BSD-3-Clause | esp32, esp32c3, esp8266, esp32s2, esp32c3, esp32c6 | yes |
251252

252253

253254

@@ -411,6 +412,8 @@ or [GitLab examples](https://gitlab.com/UncleRus/esp-idf-lib/tree/master/example
411412

412413
- [Weslley Duarte](https://github.com/weslleymfd): `ads130e08`
413414

415+
- [xyzroe](https://github.com/xyzroe): `qmi8658c`
416+
414417
- [Zaltora](https://github.com/Zaltora): `ina3221`
415418

416419
- zeroday: `onewire`

components/qmi8658c/.eil.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: qmi8658c
2+
description: Driver for QMI8658C 6-axis IMU sensor
3+
version: 0.5.0
4+
groups:
5+
- magnetic
6+
code_owners: xyzroe
7+
depends:
8+
- i2cdev
9+
- log
10+
- esp_idf_lib_helpers
11+
thread_safe: yes
12+
targets:
13+
- esp32
14+
- esp32c3
15+
- esp8266
16+
- esp32s2
17+
- esp32c6
18+
license: BSD-3
19+
copyrights:
20+
- name: xyzroe
21+
year: 2024

components/qmi8658c/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
idf_component_register(
2+
SRCS qmi8658c.c
3+
INCLUDE_DIRS .
4+
REQUIRES i2cdev log esp_idf_lib_helpers
5+
)

components/qmi8658c/LICENSE

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Copyright (c) 2024 xyzroe <i@xyzroe.cc>
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are met:
5+
6+
1. Redistributions of source code must retain the above copyright notice,
7+
this list of conditions and the following disclaimer.
8+
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
12+
13+
3. Neither the name of the copyright holder nor the names of its contributors
14+
may be used to endorse or promote products derived from this software without
15+
specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

components/qmi8658c/component.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
COMPONENT_ADD_INCLUDEDIRS = .
2+
COMPONENT_DEPENDS = i2cdev log esp_idf_lib_helpers

components/qmi8658c/qmi8658c.c

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
/*
2+
* Copyright (c) 2024 xyzroe <i@xyzroe.cc>
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are met:
6+
*
7+
* 1. Redistributions of source code must retain the above copyright notice,
8+
* this list of conditions and the following disclaimer.
9+
*
10+
* 2. Redistributions in binary form must reproduce the above copyright notice,
11+
* this list of conditions and the following disclaimer in the documentation
12+
* and/or other materials provided with the distribution.
13+
*
14+
* 3. Neither the name of the copyright holder nor the names of its contributors
15+
* may be used to endorse or promote products derived from this software without
16+
* specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
*/
29+
30+
/**
31+
* @file qmi8658c.c
32+
* @brief QMI8658C sensor driver
33+
* @author xyzroe
34+
* ESP-IDF driver for QMI8658C sensor
35+
*
36+
* Datasheet: https://qstcorp.com/upload/pdf/202202/QMI8658C%20datasheet%20rev%200.9.pdf
37+
*
38+
* Copyright (c) 2024 xyzroe <i@xyzroe.cc>
39+
*/
40+
41+
#include <stdio.h>
42+
#include <freertos/FreeRTOS.h>
43+
#include <freertos/task.h>
44+
#include <esp_log.h>
45+
#include <esp_idf_lib_helpers.h>
46+
#include "qmi8658c.h"
47+
48+
static const char *TAG = "qmi8658c";
49+
50+
#define CHECK(x) \
51+
do \
52+
{ \
53+
esp_err_t __; \
54+
if ((__ = x) != ESP_OK) \
55+
return __; \
56+
} \
57+
while (0)
58+
#define CHECK_ARG(VAL) \
59+
do \
60+
{ \
61+
if (!(VAL)) \
62+
return ESP_ERR_INVALID_ARG; \
63+
} \
64+
while (0)
65+
66+
static qmi_ctx_t qmi_ctx;
67+
68+
inline static esp_err_t send_command_nolock(i2c_dev_t *dev, uint8_t reg, uint8_t value)
69+
{
70+
uint8_t data[2] = { reg, value };
71+
return i2c_dev_write(dev, NULL, 0, data, 2);
72+
}
73+
74+
static esp_err_t send_command(i2c_dev_t *dev, uint8_t reg, uint8_t value)
75+
{
76+
I2C_DEV_TAKE_MUTEX(dev);
77+
I2C_DEV_CHECK(dev, send_command_nolock(dev, reg, value));
78+
I2C_DEV_GIVE_MUTEX(dev);
79+
80+
return ESP_OK;
81+
}
82+
83+
#define REPLY_DELAY_MS 25
84+
85+
static esp_err_t read_register(i2c_dev_t *dev, uint8_t reg, uint8_t *value)
86+
{
87+
I2C_DEV_TAKE_MUTEX(dev);
88+
89+
I2C_DEV_CHECK(dev, i2c_dev_read_reg(dev, reg, value, 1));
90+
91+
vTaskDelay(pdMS_TO_TICKS(REPLY_DELAY_MS));
92+
93+
I2C_DEV_GIVE_MUTEX(dev);
94+
95+
return ESP_OK;
96+
}
97+
98+
esp_err_t qmi8658c_init_desc(i2c_dev_t *dev, uint8_t addr, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio)
99+
{
100+
CHECK_ARG(dev);
101+
102+
dev->port = port;
103+
dev->addr = addr;
104+
dev->cfg.sda_io_num = sda_gpio;
105+
dev->cfg.scl_io_num = scl_gpio;
106+
#if HELPER_TARGET_IS_ESP32
107+
dev->cfg.master.clk_speed = QMI8658C_I2C_FREQ_HZ;
108+
#endif
109+
return i2c_dev_create_mutex(dev);
110+
}
111+
112+
esp_err_t qmi8658c_free_desc(i2c_dev_t *dev)
113+
{
114+
CHECK_ARG(dev);
115+
return i2c_dev_delete_mutex(dev);
116+
/*
117+
uint8_t ctrl7, ctrl1;
118+
119+
// Read QMI8658_CTRL7 register
120+
CHECK(read_register(dev, QMI8658_CTRL7, &ctrl7));
121+
122+
// Disable accelerometer, gyroscope, magnetometer and attitude engine
123+
ctrl7 &= 0xF0;
124+
CHECK(send_command(dev, QMI8658_CTRL7, ctrl7));
125+
126+
// Disable sensor by turning off the internal 2 MHz oscillator
127+
CHECK(read_register(dev, QMI8658_CTRL1, &ctrl1));
128+
ctrl1 |= (1 << 0);
129+
CHECK(send_command(dev, QMI8658_CTRL1, ctrl1));
130+
131+
// Read these two registers again to verify
132+
CHECK(read_register(dev, QMI8658_CTRL7, &ctrl7));
133+
CHECK(read_register(dev, QMI8658_CTRL1, &ctrl1));
134+
135+
// Verify if the sensor is properly disabled
136+
if (!(ctrl7 & 0x0F) && (ctrl1 & 0x01))
137+
{
138+
return i2c_dev_delete_mutex(dev);
139+
}
140+
else
141+
{
142+
return ESP_FAIL;
143+
}
144+
*/
145+
}
146+
147+
esp_err_t qmi8658c_reset(i2c_dev_t *dev)
148+
{
149+
CHECK_ARG(dev);
150+
151+
return send_command(dev, QMI8658_RESET, 0xB0);
152+
}
153+
154+
/* Accelerometer sensitivity table */
155+
uint16_t acc_scale_sensitivity_table[4] = {
156+
ACC_SCALE_SENSITIVITY_2G, // Sensitivity for ±2g range.
157+
ACC_SCALE_SENSITIVITY_4G, // Sensitivity for ±4g range.
158+
ACC_SCALE_SENSITIVITY_8G, // Sensitivity for ±8g range.
159+
ACC_SCALE_SENSITIVITY_16G // Sensitivity for ±16g range.
160+
};
161+
162+
/* Gyroscope sensitivity table */
163+
uint16_t gyro_scale_sensitivity_table[8] = {
164+
GYRO_SCALE_SENSITIVITY_16DPS, // Sensitivity for ±16 degrees per second range.
165+
GYRO_SCALE_SENSITIVITY_32DPS, // Sensitivity for ±32 degrees per second range.
166+
GYRO_SCALE_SENSITIVITY_64DPS, // Sensitivity for ±64 degrees per second range.
167+
GYRO_SCALE_SENSITIVITY_128DPS, // Sensitivity for ±128 degrees per second range.
168+
GYRO_SCALE_SENSITIVITY_256DPS, // Sensitivity for ±256 degrees per second range.
169+
GYRO_SCALE_SENSITIVITY_512DPS, // Sensitivity for ±512 degrees per second range.
170+
GYRO_SCALE_SENSITIVITY_1024DPS, // Sensitivity for ±1024 degrees per second range.
171+
GYRO_SCALE_SENSITIVITY_2048DPS // Sensitivity for ±2048 degrees per second range.
172+
};
173+
174+
esp_err_t qmi8658c_setup(i2c_dev_t *dev, qmi8658c_config_t *config)
175+
{
176+
CHECK_ARG(dev && config);
177+
178+
// reset device
179+
qmi8658c_reset(dev);
180+
181+
// set mode
182+
uint8_t ctrl7;
183+
CHECK(read_register(dev, QMI8658_CTRL7, &ctrl7));
184+
ctrl7 = (ctrl7 & 0xFC) | config->mode;
185+
CHECK(send_command(dev, QMI8658_CTRL7, ctrl7));
186+
187+
// set accelerometr scale and ODR
188+
uint8_t ctrl2;
189+
CHECK(read_register(dev, QMI8658_CTRL2, &ctrl2));
190+
ctrl2 = (ctrl2 & 0xF0) | config->acc_odr;
191+
ctrl2 = (ctrl2 & 0x8F) | (config->acc_scale << 4);
192+
CHECK(send_command(dev, QMI8658_CTRL2, ctrl2));
193+
194+
// set accelerometer scale and sensitivity
195+
qmi_ctx.acc_scale = config->acc_scale;
196+
qmi_ctx.acc_sensitivity = acc_scale_sensitivity_table[config->acc_scale];
197+
198+
// set gyroscope scale and ODR
199+
uint8_t ctrl3;
200+
CHECK(read_register(dev, QMI8658_CTRL3, &ctrl3));
201+
ctrl3 = (ctrl3 & 0xF0) | config->gyro_odr;
202+
ctrl3 = (ctrl3 & 0x8F) | (config->gyro_scale << 4);
203+
CHECK(send_command(dev, QMI8658_CTRL3, ctrl3));
204+
205+
// set gyroscope scale and sensitivity
206+
qmi_ctx.gyro_scale = config->gyro_scale;
207+
qmi_ctx.gyro_sensitivity = gyro_scale_sensitivity_table[config->gyro_scale];
208+
209+
// read device ID and revision ID
210+
CHECK(read_register(dev, QMI8658_WHO_AM_I, &qmi_ctx.who_am_i));
211+
CHECK(read_register(dev, QMI8658_REVISION, &qmi_ctx.revision));
212+
213+
ESP_LOGW(TAG, "device ID: 0x%02X, revision: 0x%02X", qmi_ctx.who_am_i, qmi_ctx.revision);
214+
215+
// Verify mode setting
216+
uint8_t qmi8658_ctrl7;
217+
CHECK(read_register(dev, QMI8658_CTRL7, &qmi8658_ctrl7));
218+
if ((qmi8658_ctrl7 & 0x03) != config->mode)
219+
{
220+
ESP_LOGE(TAG, "Mode setting verification failed");
221+
return ESP_FAIL;
222+
}
223+
224+
return ESP_OK;
225+
}
226+
227+
esp_err_t qmi8658c_read_data(i2c_dev_t *dev, qmi8658c_data_t *data)
228+
{
229+
CHECK_ARG(dev && data);
230+
231+
// Read accelerometer data
232+
uint8_t acc_x_l, acc_x_h, acc_y_l, acc_y_h, acc_z_l, acc_z_h;
233+
CHECK(read_register(dev, QMI8658_ACC_X_L, &acc_x_l));
234+
CHECK(read_register(dev, QMI8658_ACC_X_H, &acc_x_h));
235+
CHECK(read_register(dev, QMI8658_ACC_Y_L, &acc_y_l));
236+
CHECK(read_register(dev, QMI8658_ACC_Y_H, &acc_y_h));
237+
CHECK(read_register(dev, QMI8658_ACC_Z_L, &acc_z_l));
238+
CHECK(read_register(dev, QMI8658_ACC_Z_H, &acc_z_h));
239+
240+
// ESP_LOGE(TAG, "acc_x_l: %d, acc_x_h: %d, acc_y_l: %d, acc_y_h: %d, acc_z_l: %d, acc_z_h: %d", acc_x_l, acc_x_h, acc_y_l, acc_y_h, acc_z_l, acc_z_h);
241+
242+
int16_t acc_x = (int16_t)((acc_x_h << 8) | acc_x_l);
243+
int16_t acc_y = (int16_t)((acc_y_h << 8) | acc_y_l);
244+
int16_t acc_z = (int16_t)((acc_z_h << 8) | acc_z_l);
245+
246+
// ESP_LOGW(TAG, "acc_x: %d, acc_y: %d, acc_z: %d", acc_x, acc_y, acc_z);
247+
248+
data->acc.x = (float)acc_x / qmi_ctx.acc_sensitivity;
249+
data->acc.y = (float)acc_y / qmi_ctx.acc_sensitivity;
250+
data->acc.z = (float)acc_z / qmi_ctx.acc_sensitivity;
251+
252+
// Read gyroscope data
253+
uint8_t gyr_x_l, gyr_x_h, gyr_y_l, gyr_y_h, gyr_z_l, gyr_z_h;
254+
CHECK(read_register(dev, QMI8658_GYR_X_L, &gyr_x_l));
255+
CHECK(read_register(dev, QMI8658_GYR_X_H, &gyr_x_h));
256+
CHECK(read_register(dev, QMI8658_GYR_Y_L, &gyr_y_l));
257+
CHECK(read_register(dev, QMI8658_GYR_Y_H, &gyr_y_h));
258+
CHECK(read_register(dev, QMI8658_GYR_Z_L, &gyr_z_l));
259+
CHECK(read_register(dev, QMI8658_GYR_Z_H, &gyr_z_h));
260+
261+
// ESP_LOGE(TAG, "gyr_x_l: %d, gyr_x_h: %d, gyr_y_l: %d, gyr_y_h: %d, gyr_z_l: %d, gyr_z_h: %d", gyr_x_l, gyr_x_h, gyr_y_l, gyr_y_h, gyr_z_l, gyr_z_h);
262+
263+
int16_t gyr_x = (int16_t)((gyr_x_h << 8) | gyr_x_l);
264+
int16_t gyr_y = (int16_t)((gyr_y_h << 8) | gyr_y_l);
265+
int16_t gyr_z = (int16_t)((gyr_z_h << 8) | gyr_z_l);
266+
267+
// ESP_LOGW(TAG, "gyr_x: %d, gyr_y: %d, gyr_z: %d", gyr_x, gyr_y, gyr_z);
268+
269+
data->gyro.x = (float)gyr_x / qmi_ctx.gyro_sensitivity;
270+
data->gyro.y = (float)gyr_y / qmi_ctx.gyro_sensitivity;
271+
data->gyro.z = (float)gyr_z / qmi_ctx.gyro_sensitivity;
272+
273+
// Read temperature data
274+
uint8_t temp_l, temp_h;
275+
CHECK(read_register(dev, QMI8658_TEMP_L, &temp_l));
276+
CHECK(read_register(dev, QMI8658_TEMP_H, &temp_h));
277+
278+
// ESP_LOGE(TAG, "temp_l: %d, temp_h: %d", temp_l, temp_h);
279+
280+
int16_t temp = (int16_t)((temp_h << 8) | temp_l);
281+
// ESP_LOGW(TAG, "temp: %d", temp);
282+
283+
data->temperature = (float)temp / TEMPERATURE_SENSOR_RESOLUTION;
284+
285+
// ESP_LOGW(TAG, "Acc: x=%f, y=%f, z=%f; Gyro: x=%f, y=%f, z=%f; Temp: %f",
286+
// data->acc.x, data->acc.y, data->acc.z, data->gyro.x, data->gyro.y, data->gyro.z, data->temperature);
287+
288+
return ESP_OK;
289+
}

0 commit comments

Comments
 (0)