ACwing FC firmware
STM32WB55: attitude IEKF, altitude Kalman filter, flight core, protocol v2
Loading...
Searching...
No Matches
imu_lsm6dsv16x.c
Go to the documentation of this file.
1
8#include "imu_lsm6dsv16x.h"
9#include <string.h>
10
11/* ---- LIS2MDL (registers from datasheet / lis2mdl-pid) ---- */
12#define LIS2MDL_ADDR_8BIT 0x3Du /* same as LIS2MDL_I2C_ADD in lis2mdl_reg.h */
13#define LIS2MDL_WHO_AM_I 0x4Fu
14#define LIS2MDL_ID 0x40u
15#define LIS2MDL_CFG_A 0x60u
16#define LIS2MDL_CFG_B 0x61u
17#define LIS2MDL_CFG_C 0x62u
18#define LIS2MDL_OUTX_L 0x68u
19#define LIS2MDL_UT_PER_LSB 0.15f
20
21/* ---- LPS22DF (IKS4A1) / LPS22HB (ACwing FC rev A), behind the same hub ---- */
22#define LPS22_ADDR_8BIT_H 0xBAu /* SA0 = 1 -> 0x5D */
23#define LPS22_ADDR_8BIT_L 0xB8u /* SA0 = 0 -> 0x5C */
24#define LPS22_WHO_AM_I 0x0Fu
25#define LPS22DF_ID 0xB4u
26#define LPS22HB_ID 0xB1u
27#define LPS22_CTRL_REG1 0x10u
28#define LPS22_CTRL_REG2 0x11u
29#define LPS22_PRESS_OUT_XL 0x28u /* PRESS XL, L, H, TEMP L, H */
30
31#define DEG2RAD_F 0.017453292519943295f
32#define SH_POLL_MAX 50u /* x 5 ms */
33
34static void dly(imu_t *imu, uint32_t ms) { if (imu->bus.delay_ms) imu->bus.delay_ms(ms); }
35
36/* Waits for the sensor hub to complete one cycle (triggered by the XL at 120 Hz) */
37static int32_t sh_run_once(imu_t *imu)
38{
39 lsm6dsv16x_data_ready_t drdy;
40 lsm6dsv16x_status_master_t ms;
41 int16_t raw[3];
42 int32_t ret = 0;
43
44 ret |= lsm6dsv16x_sh_master_set(&imu->ctx, PROPERTY_ENABLE);
45 ret |= lsm6dsv16x_xl_data_rate_set(&imu->ctx, LSM6DSV16X_ODR_AT_120Hz);
46 ret |= lsm6dsv16x_acceleration_raw_get(&imu->ctx, raw);
47 if (ret) return IMU_ERR_BUS;
48
49 uint32_t i = 0;
50 do { dly(imu, 5); if (lsm6dsv16x_flag_data_ready_get(&imu->ctx, &drdy)) return IMU_ERR_BUS; }
51 while (!drdy.drdy_xl && ++i < SH_POLL_MAX);
52 i = 0;
53 do { dly(imu, 5); if (lsm6dsv16x_sh_status_get(&imu->ctx, &ms)) return IMU_ERR_BUS; }
54 while (!ms.sens_hub_endop && ++i < SH_POLL_MAX);
55
56 ret |= lsm6dsv16x_sh_master_set(&imu->ctx, PROPERTY_DISABLE);
57 ret |= lsm6dsv16x_xl_data_rate_set(&imu->ctx, LSM6DSV16X_ODR_OFF);
58 if (ret) return IMU_ERR_BUS;
59 return ms.sens_hub_endop ? IMU_OK : IMU_ERR_SH_TIMEOUT;
60}
61
62static int32_t sh_write(imu_t *imu, uint8_t addr8, uint8_t reg, uint8_t val)
63{
64 lsm6dsv16x_sh_cfg_write_t w = { addr8, reg, val };
65 if (lsm6dsv16x_xl_data_rate_set(&imu->ctx, LSM6DSV16X_ODR_OFF)) return IMU_ERR_BUS;
66 if (lsm6dsv16x_sh_cfg_write(&imu->ctx, &w)) return IMU_ERR_BUS;
67 if (lsm6dsv16x_sh_write_mode_set(&imu->ctx, LSM6DSV16X_ONLY_FIRST_CYCLE)) return IMU_ERR_BUS;
68 return sh_run_once(imu);
69}
70
71static int32_t sh_read(imu_t *imu, uint8_t addr8, uint8_t reg, uint8_t *buf, uint8_t len)
72{
73 lsm6dsv16x_sh_cfg_read_t r = { addr8, reg, len };
74 if (lsm6dsv16x_xl_data_rate_set(&imu->ctx, LSM6DSV16X_ODR_OFF)) return IMU_ERR_BUS;
75 if (lsm6dsv16x_sh_slv_cfg_read(&imu->ctx, 0, &r)) return IMU_ERR_BUS;
76 if (lsm6dsv16x_sh_slave_connected_set(&imu->ctx, LSM6DSV16X_SLV_0)) return IMU_ERR_BUS;
77 int32_t st = sh_run_once(imu);
78 if (st) return st;
79 return lsm6dsv16x_sh_read_data_raw_get(&imu->ctx, buf, len) ? IMU_ERR_BUS : IMU_OK;
80}
81
82static int32_t mag_setup(imu_t *imu, uint8_t fast)
83{
84 uint8_t id = 0;
85 int32_t st = sh_read(imu, LIS2MDL_ADDR_8BIT, LIS2MDL_WHO_AM_I, &id, 1);
86 if (st) return st;
87 if (id != LIS2MDL_ID) return IMU_ERR_MAG_ID;
88
89 if ((st = sh_write(imu, LIS2MDL_ADDR_8BIT, LIS2MDL_CFG_A, 0x20u))) return st; /* SOFT_RST */
90 dly(imu, 10);
91 if ((st = sh_write(imu, LIS2MDL_ADDR_8BIT, LIS2MDL_CFG_C, 0x10u))) return st; /* BDU */
92 if ((st = sh_write(imu, LIS2MDL_ADDR_8BIT, LIS2MDL_CFG_B, 0x03u))) return st; /* OFF_CANC | LPF */
93 /* COMP_TEMP_EN | ODR 50 or 100 Hz | continuous. The sensor hub re-reads at 60 or
94 * 120 Hz: ~1 read in 6 is a duplicate, discarded by the parser. */
95 return sh_write(imu, LIS2MDL_ADDR_8BIT, LIS2MDL_CFG_A, fast ? 0x8Cu : 0x88u);
96}
97
98/* Optional barometer: a missing or silent part is not an error (the IMU and
99 * the attitude estimate must start without it). 0 = found and configured. */
100static int32_t baro_setup(imu_t *imu)
101{
102 static const uint8_t addr[2] = { LPS22_ADDR_8BIT_H, LPS22_ADDR_8BIT_L };
103 for (unsigned k = 0; k < 2u; ++k) {
104 uint8_t id = 0;
105 if (sh_read(imu, addr[k], LPS22_WHO_AM_I, &id, 1) != IMU_OK) continue;
106 if (id == LPS22DF_ID) {
107 /* CTRL_REG2: SWRESET, then BDU | EN_LPFP (ODR/4); CTRL_REG1: ODR 50 Hz, AVG 16 */
108 if (sh_write(imu, addr[k], LPS22_CTRL_REG2, 0x04u)) return -1;
109 dly(imu, 5);
110 if (sh_write(imu, addr[k], LPS22_CTRL_REG2, 0x18u)) return -1;
111 if (sh_write(imu, addr[k], LPS22_CTRL_REG1, 0x2Au)) return -1;
113 } else if (id == LPS22HB_ID) {
114 /* CTRL_REG2: SWRESET (IF_ADD_INC kept); CTRL_REG1: ODR 50 Hz | EN_LPFP | BDU */
115 if (sh_write(imu, addr[k], LPS22_CTRL_REG2, 0x14u)) return -1;
116 dly(imu, 5);
117 if (sh_write(imu, addr[k], LPS22_CTRL_REG1, 0x4Au)) return -1;
119 } else {
120 continue;
121 }
122 imu->baro_addr8 = addr[k];
123 return 0;
124 }
125 return -1;
126}
127
128int32_t imu_init(imu_t *imu, const sensor_bus_t *bus, const imu_config_t *cfg)
129{
130 if (!imu || !bus || !cfg || !bus->read || !bus->write) return IMU_ERR_ARG;
131 memset(imu, 0, sizeof *imu);
132 imu->bus = *bus;
133 imu->ctx.write_reg = bus->write;
134 imu->ctx.read_reg = bus->read;
135 imu->ctx.mdelay = bus->delay_ms;
136 imu->ctx.handle = bus->handle;
137
138 uint8_t id = 0;
139 if (lsm6dsv16x_device_id_get(&imu->ctx, &id)) return IMU_ERR_BUS;
140 if (id != LSM6DSV16X_ID) return IMU_ERR_ID;
141 if (lsm6dsv16x_sw_por(&imu->ctx)) return IMU_ERR_BUS;
142 dly(imu, 10);
143 if (lsm6dsv16x_block_data_update_set(&imu->ctx, PROPERTY_ENABLE)) return IMU_ERR_BUS;
144
145 /* 1) magnetometer behind the sensor hub */
146 if (cfg->mag_enable) {
147 int32_t st = mag_setup(imu, cfg->mag_fast);
148 if (st) return st;
149 imu->mag_enabled = 1;
150 }
151 if (cfg->baro_enable && baro_setup(imu) == 0) imu->baro_enabled = 1;
152 /* hub slots are consecutive from 0: mag first, then baro */
153 imu->baro_slot = imu->mag_enabled ? 1u : 0u;
154
155 /* 2) full scales and filters */
156 int32_t ret = 0;
157 ret |= lsm6dsv16x_gy_full_scale_set(&imu->ctx, LSM6DSV16X_2000dps);
158 ret |= lsm6dsv16x_xl_full_scale_set(&imu->ctx, LSM6DSV16X_16g);
159 ret |= lsm6dsv16x_filt_gy_lp1_set(&imu->ctx, PROPERTY_ENABLE);
160 ret |= lsm6dsv16x_filt_gy_lp1_bandwidth_set(&imu->ctx,
161 (lsm6dsv16x_filt_gy_lp1_bandwidth_t)cfg->gy_lpf1_bw);
162 ret |= lsm6dsv16x_filt_xl_lp2_set(&imu->ctx, cfg->xl_lpf2_enable ? PROPERTY_ENABLE : PROPERTY_DISABLE);
163 if (cfg->xl_lpf2_enable)
164 ret |= lsm6dsv16x_filt_xl_lp2_bandwidth_set(&imu->ctx,
165 (lsm6dsv16x_filt_xl_lp2_bandwidth_t)cfg->xl_lpf2_bw);
166 if (ret) return IMU_ERR_BUS;
167
168 /* 3) FIFO in stream mode, gyro + acc + (mag) */
169 const int fast = (cfg->odr == IMU_ODR_1920HZ);
170 ret |= lsm6dsv16x_fifo_watermark_set(&imu->ctx, cfg->watermark_words);
171 ret |= lsm6dsv16x_fifo_xl_batch_set(&imu->ctx, fast ? LSM6DSV16X_XL_BATCHED_AT_1920Hz
172 : LSM6DSV16X_XL_BATCHED_AT_960Hz);
173 ret |= lsm6dsv16x_fifo_gy_batch_set(&imu->ctx, fast ? LSM6DSV16X_GY_BATCHED_AT_1920Hz
174 : LSM6DSV16X_GY_BATCHED_AT_960Hz);
175 if (imu->mag_enabled) ret |= lsm6dsv16x_fifo_sh_batch_slave_set(&imu->ctx, 0, PROPERTY_ENABLE);
176 if (imu->baro_enabled) ret |= lsm6dsv16x_fifo_sh_batch_slave_set(&imu->ctx, imu->baro_slot, PROPERTY_ENABLE);
177 ret |= lsm6dsv16x_fifo_mode_set(&imu->ctx, LSM6DSV16X_STREAM_MODE);
178 if (ret) return IMU_ERR_BUS;
179
180 /* 4) sensor hub: continuous read of LIS2MDL OUTX_L..OUTZ_H and of the
181 * barometer PRESS_OUT_XL..TEMP_OUT_H */
182 if (imu->mag_enabled || imu->baro_enabled) {
183 if (imu->mag_enabled) {
184 lsm6dsv16x_sh_cfg_read_t r = { LIS2MDL_ADDR_8BIT, LIS2MDL_OUTX_L, 6 };
185 ret |= lsm6dsv16x_sh_slv_cfg_read(&imu->ctx, 0, &r);
186 }
187 if (imu->baro_enabled) {
188 lsm6dsv16x_sh_cfg_read_t r = { imu->baro_addr8, LPS22_PRESS_OUT_XL, 5 };
189 ret |= lsm6dsv16x_sh_slv_cfg_read(&imu->ctx, imu->baro_slot, &r);
190 }
191 ret |= lsm6dsv16x_sh_data_rate_set(&imu->ctx, cfg->mag_fast ? LSM6DSV16X_SH_120Hz
192 : LSM6DSV16X_SH_60Hz);
193 ret |= lsm6dsv16x_sh_slave_connected_set(&imu->ctx,
194 (imu->mag_enabled && imu->baro_enabled) ? LSM6DSV16X_SLV_0_1 : LSM6DSV16X_SLV_0);
195 ret |= lsm6dsv16x_sh_master_set(&imu->ctx, PROPERTY_ENABLE);
196 if (ret) return IMU_ERR_BUS;
197 }
198
199 /* 5) FIFO threshold on INT1 */
200 lsm6dsv16x_pin_int_route_t pin = { 0 };
201 pin.fifo_th = PROPERTY_ENABLE;
202 if (lsm6dsv16x_pin_int1_route_set(&imu->ctx, &pin)) return IMU_ERR_BUS;
203
204 /* 6) start: high-performance mode, same ODR */
205 const lsm6dsv16x_data_rate_t odr = fast ? LSM6DSV16X_ODR_AT_1920Hz : LSM6DSV16X_ODR_AT_960Hz;
206 ret |= lsm6dsv16x_xl_mode_set(&imu->ctx, LSM6DSV16X_XL_HIGH_PERFORMANCE_MD);
207 ret |= lsm6dsv16x_gy_mode_set(&imu->ctx, LSM6DSV16X_GY_HIGH_PERFORMANCE_MD);
208 ret |= lsm6dsv16x_xl_data_rate_set(&imu->ctx, odr);
209 ret |= lsm6dsv16x_gy_data_rate_set(&imu->ctx, odr);
210 if (ret) return IMU_ERR_BUS;
211
212 imu->odr_hz = fast ? 1920.0f : 960.0f;
213 return IMU_OK;
214}
215
216int32_t imu_fifo_level(imu_t *imu, uint16_t *words)
217{
218 lsm6dsv16x_fifo_status_t s;
219 if (lsm6dsv16x_fifo_status_get(&imu->ctx, &s)) return IMU_ERR_BUS;
220 *words = s.fifo_level;
221 return IMU_OK;
222}
223
224int32_t imu_fifo_read_burst(imu_t *imu, uint8_t *raw, uint16_t words)
225{
226 if (words > IMU_FIFO_MAX_WORDS) words = IMU_FIFO_MAX_WORDS;
227 return imu->bus.read(imu->bus.handle, IMU_FIFO_REG, raw,
228 (uint16_t)(words * IMU_FIFO_WORD_BYTES)) ? IMU_ERR_BUS : IMU_OK;
229}
230
231static int16_t le16(const uint8_t *p) { return (int16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8)); }
232
233size_t imu_fifo_parse(imu_t *imu, const uint8_t *raw, uint16_t words,
234 imu_event_t *ev, size_t cap, imu_parse_stats_t *st)
235{
236 /* 2000 dps -> 70 mdps/LSB ; 16 g -> 0.488 mg/LSB (as lsm6dsv16x_from_fs*_) */
237 const float kg = 70.0e-3f * DEG2RAD_F;
238 const float ka = 0.488e-3f;
239 size_t n = 0;
240
241 for (uint16_t w = 0; w < words && n < cap; ++w) {
242 const uint8_t *p = &raw[w * IMU_FIFO_WORD_BYTES];
243 const uint8_t tag = (uint8_t)(p[0] >> 3); /* TAG_SENSOR[4:0] = bit 7..3 */
244 const uint8_t *d = &p[1];
245 if (st) st->words++;
246
247 switch (tag) {
248 case LSM6DSV16X_GY_NC_TAG:
249 ev[n].type = IMU_EV_GYRO;
250 for (int i = 0; i < 3; ++i) ev[n].v[i] = (float)le16(&d[2*i]) * kg;
251 n++; if (st) st->gyro++;
252 break;
253 case LSM6DSV16X_XL_NC_TAG:
254 ev[n].type = IMU_EV_ACC;
255 for (int i = 0; i < 3; ++i) ev[n].v[i] = (float)le16(&d[2*i]) * ka;
256 n++; if (st) st->acc++;
257 break;
258 case LSM6DSV16X_SENSORHUB_SLAVE0_TAG:
259 case LSM6DSV16X_SENSORHUB_SLAVE1_TAG: {
260 const uint8_t slot = (tag == LSM6DSV16X_SENSORHUB_SLAVE0_TAG) ? 0u : 1u;
261 if (imu->baro_enabled && slot == imu->baro_slot) {
262 /* the hub re-reads at 60/120 Hz, the baro converts at 50 Hz */
263 if (imu->have_baro && memcmp(d, imu->last_baro_raw, 5) == 0) { if (st) st->baro_dup++; break; }
264 memcpy(imu->last_baro_raw, d, 5);
265 imu->have_baro = 1;
266 int32_t pr = (int32_t)((uint32_t)d[0] | ((uint32_t)d[1] << 8) | ((uint32_t)d[2] << 16));
267 if (pr & 0x00800000) pr -= 0x01000000; /* 24-bit two's complement */
268 ev[n].type = IMU_EV_BARO;
269 ev[n].v[0] = (float)pr * (100.0f / 4096.0f); /* Pa (LSB = 1/4096 hPa) */
270 ev[n].v[1] = (float)le16(&d[3]) * 0.01f; /* degC */
271 ev[n].v[2] = 0.0f;
272 n++; if (st) st->baro++;
273 break;
274 }
275 if (!imu->mag_enabled || slot != 0u) { if (st) st->unknown++; break; }
276 if (imu->have_mag && memcmp(d, imu->last_mag_raw, 6) == 0) {
277 if (st) st->mag_dup++; /* re-read of the same sample */
278 break;
279 }
280 memcpy(imu->last_mag_raw, d, 6);
281 imu->have_mag = 1;
282 ev[n].type = IMU_EV_MAG;
283 for (int i = 0; i < 3; ++i) ev[n].v[i] = (float)le16(&d[2*i]) * LIS2MDL_UT_PER_LSB;
284 n++; if (st) st->mag++;
285 break;
286 }
287 case LSM6DSV16X_SENSORHUB_NACK_TAG:
288 if (st) st->sh_nack++;
289 break;
290 case LSM6DSV16X_FIFO_EMPTY:
291 if (st) st->empty++;
292 break;
293 default:
294 if (st) st->unknown++;
295 break;
296 }
297 }
298 return n;
299}
static entry_t * slot(void)
Definition ack_cache.c:21
static batt_state_t s
Definition battery.c:21
uint32_t n
Definition calib_mgr.c:35
float cap[6][3]
Definition calib_mgr.c:36
#define LIS2MDL_OUTX_L
static int32_t mag_setup(imu_t *imu, uint8_t fast)
static int32_t sh_run_once(imu_t *imu)
#define LPS22_PRESS_OUT_XL
static void dly(imu_t *imu, uint32_t ms)
#define LPS22_CTRL_REG1
size_t imu_fifo_parse(imu_t *imu, const uint8_t *raw, uint16_t words, imu_event_t *ev, size_t cap, imu_parse_stats_t *st)
Decodes 'words' words read in a burst from IMU_FIFO_REG.
#define LPS22DF_ID
#define SH_POLL_MAX
static int32_t sh_read(imu_t *imu, uint8_t addr8, uint8_t reg, uint8_t *buf, uint8_t len)
int32_t imu_init(imu_t *imu, const sensor_bus_t *bus, const imu_config_t *cfg)
#define LPS22HB_ID
#define DEG2RAD_F
#define LIS2MDL_UT_PER_LSB
#define LIS2MDL_WHO_AM_I
static int16_t le16(const uint8_t *p)
#define LPS22_ADDR_8BIT_L
#define LIS2MDL_CFG_B
int32_t imu_fifo_level(imu_t *imu, uint16_t *words)
Number of 7-byte words in the FIFO.
#define LIS2MDL_CFG_A
#define LIS2MDL_ADDR_8BIT
#define LPS22_CTRL_REG2
#define LPS22_WHO_AM_I
#define LIS2MDL_ID
static int32_t sh_write(imu_t *imu, uint8_t addr8, uint8_t reg, uint8_t val)
#define LIS2MDL_CFG_C
int32_t imu_fifo_read_burst(imu_t *imu, uint8_t *raw, uint16_t words)
Blocking read via sensor_bus (for tests or when DMA is not used)
#define LPS22_ADDR_8BIT_H
static int32_t baro_setup(imu_t *imu)
IMU library: LSM6DSV16X (gyro + acc) with the LIS2MDL and the barometer (LPS22DF on the X-NUCLEO-IKS4...
@ IMU_OK
@ IMU_ERR_ARG
@ IMU_ERR_BUS
@ IMU_ERR_MAG_ID
@ IMU_ERR_ID
@ IMU_ERR_SH_TIMEOUT
@ IMU_BARO_LPS22DF
@ IMU_BARO_LPS22HB
@ IMU_EV_BARO
@ IMU_EV_ACC
@ IMU_EV_GYRO
@ IMU_EV_MAG
#define IMU_FIFO_REG
0x78
#define IMU_FIFO_WORD_BYTES
@ IMU_ODR_1920HZ
#define IMU_FIFO_MAX_WORDS
burst buffer: 336 bytes
static uint32_t addr(uint16_t sect)
Definition log_store.c:49
uint16_t ms
Definition log_task.c:42
uint8_t mag_fast
0: LIS2MDL 50 Hz / hub 60 Hz; 1: 100 Hz / 120 Hz
uint8_t xl_lpf2_bw
lsm6dsv16x_filt_xl_lp2_bandwidth_t
uint8_t watermark_words
4 = 2 gyro + 2 acc -> one interrupt every 2 samples
uint8_t baro_enable
uint8_t mag_enable
LIS2MDL via sensor hub (requires IKS4A1 Mode 3)
uint8_t gy_lpf1_bw
lsm6dsv16x_filt_gy_lp1_bandwidth_t
imu_odr_t odr
same ODR for gyro and acc
uint8_t xl_lpf2_enable
uint8_t type
imu_ev_type_t
uint8_t baro_addr8
uint8_t baro_model
stmdev_ctx_t ctx
float odr_hz
uint8_t have_mag
uint8_t have_baro
uint8_t last_baro_raw[5]
uint8_t mag_enabled
sensor_bus_t bus
uint8_t last_mag_raw[6]
to discard sensor-hub duplicates
uint8_t baro_slot
uint8_t baro_enabled
bus_delay_fn delay_ms
optional (used only in init/reset)
Definition sensor_bus.h:22
bus_write_fn write
Definition sensor_bus.h:20
void * handle
opaque context passed to read/write
Definition sensor_bus.h:23
bus_read_fn read
Definition sensor_bus.h:21
static USBD_StatusTypeDef st(HAL_StatusTypeDef h)
Definition usbd_conf.c:86