Skip to content
This repository was archived by the owner on Jan 24, 2026. It is now read-only.

Commit 21ff469

Browse files
committed
feat(qmi8658c): add QMI8658C sensor driver implementation and configuration files
1 parent 46eed11 commit 21ff469

File tree

6 files changed

+496
-0
lines changed

6 files changed

+496
-0
lines changed

components/qmi8658c/.eil.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: qmi8658c
2+
description: Driver for QMI8658C 6-axis IMU sensor
3+
version: 0.5.0
4+
groups:
5+
- imu
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+
- esp32c3
18+
- esp32c6
19+
license: BSD-3
20+
copyrights:
21+
- name: xyzroe
22+
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: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/**
2+
* @file qmi8658c.c
3+
* @brief QMI8658C sensor driver
4+
* @author xyzroe
5+
* ESP-IDF driver for QMI8658C sensor
6+
*
7+
* Datasheet: https://qstcorp.com/upload/pdf/202202/QMI8658C%20datasheet%20rev%200.9.pdf
8+
*
9+
* Copyright (c) 2024 xyzroe <i@xyzroe.cc>
10+
* Licensed as described in the file LICENSE
11+
*/
12+
13+
#include <stdio.h>
14+
#include <freertos/FreeRTOS.h>
15+
#include <esp_log.h>
16+
#include <esp_idf_lib_helpers.h>
17+
#include "qmi8658c.h"
18+
19+
static const char *TAG = "qmi8658c";
20+
21+
#define CHECK(x) \
22+
do \
23+
{ \
24+
esp_err_t __; \
25+
if ((__ = x) != ESP_OK) \
26+
return __; \
27+
} \
28+
while (0)
29+
#define CHECK_ARG(VAL) \
30+
do \
31+
{ \
32+
if (!(VAL)) \
33+
return ESP_ERR_INVALID_ARG; \
34+
} \
35+
while (0)
36+
37+
static qmi_ctx_t qmi_ctx;
38+
39+
inline static esp_err_t send_command_nolock(i2c_dev_t *dev, uint8_t reg, uint8_t value)
40+
{
41+
uint8_t data[2] = { reg, value };
42+
return i2c_dev_write(dev, NULL, 0, data, 2);
43+
}
44+
45+
static esp_err_t send_command(i2c_dev_t *dev, uint8_t reg, uint8_t value)
46+
{
47+
I2C_DEV_TAKE_MUTEX(dev);
48+
I2C_DEV_CHECK(dev, send_command_nolock(dev, reg, value));
49+
I2C_DEV_GIVE_MUTEX(dev);
50+
51+
return ESP_OK;
52+
}
53+
54+
#define REPLY_DELAY_MS 25
55+
56+
static esp_err_t read_register(i2c_dev_t *dev, uint8_t reg, uint8_t *value)
57+
{
58+
I2C_DEV_TAKE_MUTEX(dev);
59+
60+
I2C_DEV_CHECK(dev, i2c_dev_read_reg(dev, reg, value, 1));
61+
62+
vTaskDelay(pdMS_TO_TICKS(REPLY_DELAY_MS));
63+
64+
I2C_DEV_GIVE_MUTEX(dev);
65+
66+
return ESP_OK;
67+
}
68+
69+
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)
70+
{
71+
CHECK_ARG(dev);
72+
73+
dev->port = port;
74+
dev->addr = addr;
75+
dev->cfg.sda_io_num = sda_gpio;
76+
dev->cfg.scl_io_num = scl_gpio;
77+
#if HELPER_TARGET_IS_ESP32
78+
dev->cfg.master.clk_speed = QMI8658C_I2C_FREQ_HZ;
79+
#endif
80+
return i2c_dev_create_mutex(dev);
81+
}
82+
83+
esp_err_t qmi8658c_free_desc(i2c_dev_t *dev)
84+
{
85+
CHECK_ARG(dev);
86+
return i2c_dev_delete_mutex(dev);
87+
/*
88+
uint8_t ctrl7, ctrl1;
89+
90+
// Read QMI8658_CTRL7 register
91+
CHECK(read_register(dev, QMI8658_CTRL7, &ctrl7));
92+
93+
// Disable accelerometer, gyroscope, magnetometer and attitude engine
94+
ctrl7 &= 0xF0;
95+
CHECK(send_command(dev, QMI8658_CTRL7, ctrl7));
96+
97+
// Disable sensor by turning off the internal 2 MHz oscillator
98+
CHECK(read_register(dev, QMI8658_CTRL1, &ctrl1));
99+
ctrl1 |= (1 << 0);
100+
CHECK(send_command(dev, QMI8658_CTRL1, ctrl1));
101+
102+
// Read these two registers again to verify
103+
CHECK(read_register(dev, QMI8658_CTRL7, &ctrl7));
104+
CHECK(read_register(dev, QMI8658_CTRL1, &ctrl1));
105+
106+
// Verify if the sensor is properly disabled
107+
if (!(ctrl7 & 0x0F) && (ctrl1 & 0x01))
108+
{
109+
return i2c_dev_delete_mutex(dev);
110+
}
111+
else
112+
{
113+
return ESP_FAIL;
114+
}
115+
*/
116+
}
117+
118+
esp_err_t qmi8658c_reset(i2c_dev_t *dev)
119+
{
120+
CHECK_ARG(dev);
121+
122+
return send_command(dev, QMI8658_RESET, 0xB0);
123+
}
124+
125+
/* Accelerometer sensitivity table */
126+
uint16_t acc_scale_sensitivity_table[4] = {
127+
ACC_SCALE_SENSITIVITY_2G, // Sensitivity for ±2g range.
128+
ACC_SCALE_SENSITIVITY_4G, // Sensitivity for ±4g range.
129+
ACC_SCALE_SENSITIVITY_8G, // Sensitivity for ±8g range.
130+
ACC_SCALE_SENSITIVITY_16G // Sensitivity for ±16g range.
131+
};
132+
133+
/* Gyroscope sensitivity table */
134+
uint16_t gyro_scale_sensitivity_table[8] = {
135+
GYRO_SCALE_SENSITIVITY_16DPS, // Sensitivity for ±16 degrees per second range.
136+
GYRO_SCALE_SENSITIVITY_32DPS, // Sensitivity for ±32 degrees per second range.
137+
GYRO_SCALE_SENSITIVITY_64DPS, // Sensitivity for ±64 degrees per second range.
138+
GYRO_SCALE_SENSITIVITY_128DPS, // Sensitivity for ±128 degrees per second range.
139+
GYRO_SCALE_SENSITIVITY_256DPS, // Sensitivity for ±256 degrees per second range.
140+
GYRO_SCALE_SENSITIVITY_512DPS, // Sensitivity for ±512 degrees per second range.
141+
GYRO_SCALE_SENSITIVITY_1024DPS, // Sensitivity for ±1024 degrees per second range.
142+
GYRO_SCALE_SENSITIVITY_2048DPS // Sensitivity for ±2048 degrees per second range.
143+
};
144+
145+
esp_err_t qmi8658c_setup(i2c_dev_t *dev, qmi8658c_config_t *config)
146+
{
147+
CHECK_ARG(dev && config);
148+
149+
// reset device
150+
qmi8658c_reset(dev);
151+
152+
// set mode
153+
uint8_t ctrl7;
154+
CHECK(read_register(dev, QMI8658_CTRL7, &ctrl7));
155+
ctrl7 = (ctrl7 & 0xFC) | config->mode;
156+
CHECK(send_command(dev, QMI8658_CTRL7, ctrl7));
157+
158+
// set accelerometr scale and ODR
159+
uint8_t ctrl2;
160+
CHECK(read_register(dev, QMI8658_CTRL2, &ctrl2));
161+
ctrl2 = (ctrl2 & 0xF0) | config->acc_odr;
162+
ctrl2 = (ctrl2 & 0x8F) | (config->acc_scale << 4);
163+
CHECK(send_command(dev, QMI8658_CTRL2, ctrl2));
164+
165+
// set accelerometer scale and sensitivity
166+
qmi_ctx.acc_scale = config->acc_scale;
167+
qmi_ctx.acc_sensitivity = acc_scale_sensitivity_table[config->acc_scale];
168+
169+
// set gyroscope scale and ODR
170+
uint8_t ctrl3;
171+
CHECK(read_register(dev, QMI8658_CTRL3, &ctrl3));
172+
ctrl3 = (ctrl3 & 0xF0) | config->gyro_odr;
173+
ctrl3 = (ctrl3 & 0x8F) | (config->gyro_scale << 4);
174+
CHECK(send_command(dev, QMI8658_CTRL3, ctrl3));
175+
176+
// set gyroscope scale and sensitivity
177+
qmi_ctx.gyro_scale = config->gyro_scale;
178+
qmi_ctx.gyro_sensitivity = gyro_scale_sensitivity_table[config->gyro_scale];
179+
180+
// read device ID and revision ID
181+
CHECK(read_register(dev, QMI8658_WHO_AM_I, &qmi_ctx.who_am_i));
182+
CHECK(read_register(dev, QMI8658_REVISION, &qmi_ctx.revision));
183+
184+
ESP_LOGW(TAG, "device ID: 0x%02X, revision: 0x%02X", qmi_ctx.who_am_i, qmi_ctx.revision);
185+
186+
// Verify mode setting
187+
uint8_t qmi8658_ctrl7;
188+
CHECK(read_register(dev, QMI8658_CTRL7, &qmi8658_ctrl7));
189+
if ((qmi8658_ctrl7 & 0x03) != config->mode)
190+
{
191+
ESP_LOGE(TAG, "Mode setting verification failed");
192+
return ESP_FAIL;
193+
}
194+
195+
return ESP_OK;
196+
}
197+
198+
esp_err_t qmi8658c_read_data(i2c_dev_t *dev, qmi8658c_data_t *data)
199+
{
200+
CHECK_ARG(dev && data);
201+
202+
// Read accelerometer data
203+
uint8_t acc_x_l, acc_x_h, acc_y_l, acc_y_h, acc_z_l, acc_z_h;
204+
CHECK(read_register(dev, QMI8658_ACC_X_L, &acc_x_l));
205+
CHECK(read_register(dev, QMI8658_ACC_X_H, &acc_x_h));
206+
CHECK(read_register(dev, QMI8658_ACC_Y_L, &acc_y_l));
207+
CHECK(read_register(dev, QMI8658_ACC_Y_H, &acc_y_h));
208+
CHECK(read_register(dev, QMI8658_ACC_Z_L, &acc_z_l));
209+
CHECK(read_register(dev, QMI8658_ACC_Z_H, &acc_z_h));
210+
211+
// 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);
212+
213+
int16_t acc_x = (int16_t)((acc_x_h << 8) | acc_x_l);
214+
int16_t acc_y = (int16_t)((acc_y_h << 8) | acc_y_l);
215+
int16_t acc_z = (int16_t)((acc_z_h << 8) | acc_z_l);
216+
217+
// ESP_LOGW(TAG, "acc_x: %d, acc_y: %d, acc_z: %d", acc_x, acc_y, acc_z);
218+
219+
data->acc.x = (float)acc_x / qmi_ctx.acc_sensitivity;
220+
data->acc.y = (float)acc_y / qmi_ctx.acc_sensitivity;
221+
data->acc.z = (float)acc_z / qmi_ctx.acc_sensitivity;
222+
223+
// Read gyroscope data
224+
uint8_t gyr_x_l, gyr_x_h, gyr_y_l, gyr_y_h, gyr_z_l, gyr_z_h;
225+
CHECK(read_register(dev, QMI8658_GYR_X_L, &gyr_x_l));
226+
CHECK(read_register(dev, QMI8658_GYR_X_H, &gyr_x_h));
227+
CHECK(read_register(dev, QMI8658_GYR_Y_L, &gyr_y_l));
228+
CHECK(read_register(dev, QMI8658_GYR_Y_H, &gyr_y_h));
229+
CHECK(read_register(dev, QMI8658_GYR_Z_L, &gyr_z_l));
230+
CHECK(read_register(dev, QMI8658_GYR_Z_H, &gyr_z_h));
231+
232+
// 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);
233+
234+
int16_t gyr_x = (int16_t)((gyr_x_h << 8) | gyr_x_l);
235+
int16_t gyr_y = (int16_t)((gyr_y_h << 8) | gyr_y_l);
236+
int16_t gyr_z = (int16_t)((gyr_z_h << 8) | gyr_z_l);
237+
238+
// ESP_LOGW(TAG, "gyr_x: %d, gyr_y: %d, gyr_z: %d", gyr_x, gyr_y, gyr_z);
239+
240+
data->gyro.x = (float)gyr_x / qmi_ctx.gyro_sensitivity;
241+
data->gyro.y = (float)gyr_y / qmi_ctx.gyro_sensitivity;
242+
data->gyro.z = (float)gyr_z / qmi_ctx.gyro_sensitivity;
243+
244+
// Read temperature data
245+
uint8_t temp_l, temp_h;
246+
CHECK(read_register(dev, QMI8658_TEMP_L, &temp_l));
247+
CHECK(read_register(dev, QMI8658_TEMP_H, &temp_h));
248+
249+
// ESP_LOGE(TAG, "temp_l: %d, temp_h: %d", temp_l, temp_h);
250+
251+
int16_t temp = (int16_t)((temp_h << 8) | temp_l);
252+
// ESP_LOGW(TAG, "temp: %d", temp);
253+
254+
data->temperature = (float)temp / TEMPERATURE_SENSOR_RESOLUTION;
255+
256+
// ESP_LOGW(TAG, "Acc: x=%f, y=%f, z=%f; Gyro: x=%f, y=%f, z=%f; Temp: %f",
257+
// data->acc.x, data->acc.y, data->acc.z, data->gyro.x, data->gyro.y, data->gyro.z, data->temperature);
258+
259+
return ESP_OK;
260+
}

0 commit comments

Comments
 (0)