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