JeVois  1.21
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
ICM20948.C
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2018 by Laurent Itti, the University of Southern
4// California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5//
6// This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7// redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8// Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10// License for more details. You should have received a copy of the GNU General Public License along with this program;
11// if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12//
13// Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14// Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16/*! \file */
17
18#include <jevois/Core/Engine.H>
21
22// This code inspired by:
23
24/* *****************************************************************************
25 * file ICM20648.cpp
26 * *****************************************************************************
27 * section License
28 * <b>(C) Copyright 2017 Silicon Labs, http://www.silabs.com</b>
29 * *****************************************************************************
30 *
31 * SPDX-License-Identifier: Apache-2.0
32 *
33 * Licensed under the Apache License, Version 2.0 (the "License"); you may
34 * not use this file except in compliance with the License.
35 * You may obtain a copy of the License at
36 *
37 * http://www.apache.org/licenses/LICENSE-2.0
38 *
39 * Unless required by applicable law or agreed to in writing, software
40 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
41 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42 * See the License for the specific language governing permissions and
43 * limitations under the License.
44 *
45 * ****************************************************************************/
46
47// motion event control reg
48#define JEVOIS_DMP_BAC_WEARABLE_EN 0x8000
49#define JEVOIS_DMP_PEDOMETER_EN 0x4000
50#define JEVOIS_DMP_PEDOMETER_INT_EN 0x2000
51#define JEVOIS_DMP_SMD_EN 0x0800
52#define JEVOIS_DMP_BTS_EN 0x0020
53#define JEVOIS_DMP_FLIP_PICKUP_EN 0x0010
54#define JEVOIS_DMP_GEOMAG_EN 0x0008
55#define JEVOIS_DMP_ACCEL_CAL_EN 0x0200
56#define JEVOIS_DMP_GYRO_CAL_EN 0x0100
57#define JEVOIS_DMP_COMPASS_CAL_EN 0x0080
58#define JEVOIS_DMP_NINE_AXIS_EN 0x0040
59#define JEVOIS_DMP_BRING_AND_LOOK_T0_SEE_EN 0x0004
60
61// ####################################################################################################
62jevois::ICM20948::ICM20948(std::string const & instance) :
63 jevois::Component(instance)
64{ }
65
66// ####################################################################################################
69
70// ####################################################################################################
71unsigned char jevois::ICM20948::readMagRegister(unsigned char magreg)
72{
73 // We use slave4, which is oneshot:
74 itsIMU->writeRegister(ICM20948_REG_I2C_SLV4_ADDR, ICM20948_BIT_I2C_READ | COMPASS_SLAVEADDR);
75 itsIMU->writeRegister(ICM20948_REG_I2C_SLV4_REG, magreg);
76 itsIMU->writeRegister(ICM20948_REG_I2C_SLV4_CTRL, ICM20948_BIT_I2C_SLV_EN);
77
78 waitForSlave4();
79
80 return itsIMU->readRegister(ICM20948_REG_I2C_SLV4_DI);
81}
82
83// ####################################################################################################
84void jevois::ICM20948::waitForSlave4()
85{
86 // Wait until the data is ready:
87 auto tooLate = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
88
89 do
90 {
91 std::this_thread::sleep_for(std::chrono::milliseconds(10));
92 unsigned char status = itsIMU->readRegister(ICM20948_REG_I2C_MST_STATUS);
93 if (status & ICM20948_BIT_SLV4_NACK) LFATAL("Failed to communicate with compass: NACK");
94 if (status & ICM20948_BIT_SLV4_DONE) return; // Transaction to slave is complete
95 }
96 while (std::chrono::steady_clock::now() < tooLate);
97
98 LFATAL("Failed to communicate with compass: timeout");
99}
100
101// ####################################################################################################
102void jevois::ICM20948::writeMagRegister(unsigned char magreg, unsigned char val)
103{
104 // We use slave4, which is oneshot:
105 itsIMU->writeRegister(ICM20948_REG_I2C_SLV4_ADDR, COMPASS_SLAVEADDR);
106 itsIMU->writeRegister(ICM20948_REG_I2C_SLV4_REG, magreg);
107 itsIMU->writeRegister(ICM20948_REG_I2C_SLV4_DO, val);
108 itsIMU->writeRegister(ICM20948_REG_I2C_SLV4_CTRL, ICM20948_BIT_I2C_SLV_EN);
109
110 waitForSlave4();
111}
112
113// ####################################################################################################
115{
116 // Make sure we have an IMU low-level driver:
117 itsIMU = engine()->imu();
118 if (!itsIMU) LFATAL("IMU chip was not detected but is required for this module -- ABORT");
119
120 // Warm up the connection...
121 ready(); ready();
122
123 // Make sure the chip has been detected:
124 if (ready() == false)
125 LFATAL("Cannot access ICM20948 inertial measurement unit (IMU) chip. This chip is only available on JeVois-A33 "
126 "with a modified Global Shutter OnSemi (Aptina) AR0135 camera sensor. It is not included with standard "
127 "JeVois-A33 cameras. It is included in JeVois-Pro.");
128
129 // Configure the compass:
130
131 // First, disable slaves:
132 itsIMU->writeRegister(ICM20948_REG_I2C_SLV0_CTRL, 0);
133 itsIMU->writeRegister(ICM20948_REG_I2C_SLV1_CTRL, 0);
134 itsIMU->writeRegister(ICM20948_REG_I2C_SLV2_CTRL, 0);
135 itsIMU->writeRegister(ICM20948_REG_I2C_SLV3_CTRL, 0);
136 itsIMU->writeRegister(ICM20948_REG_I2C_SLV4_CTRL, 0);
137
138 // Recommended target for I2C master clock:
139 itsIMU->writeRegister(ICM20948_REG_I2C_MST_CTRL, 0x07 | ICM20948_BIT_I2C_MST_P_NSR);
140
141 // Enable master I2C:
142 unsigned char v = itsIMU->readRegister(ICM20948_REG_USER_CTRL);
143 v |= ICM20948_BIT_I2C_MST_EN;
144 if (itsIMU->isSPI()) v |= ICM20948_BIT_I2C_IF_DIS;
145 itsIMU->writeRegister(ICM20948_REG_USER_CTRL, v);
146 std::this_thread::sleep_for(std::chrono::milliseconds(1));
147
148 // Check the who-am-I of the magnetometer, to make sure I2C master is working:
149 unsigned char wia2 = readMagRegister(REG_AK09916_WIA);
150 if (wia2 != VAL_AK09916_WIA) LFATAL("Cannot communicate with magnetometer");
151 LINFO("AK09916 magnetometer ok.");
152
153 // Enable 0x80 in ICM20948_REG_PWR_MGMT_2, needed by DMP:
154 itsIMU->writeRegister(ICM20948_REG_PWR_MGMT_2, itsIMU->readRegister(ICM20948_REG_PWR_MGMT_2) | 0x80);
155
156 // Enable FSYNC time counting:
157 itsIMU->writeRegister(ICM20948_REG_FSYNC_CONFIG, ICM20948_BIT_FSYNC_TIME_EN | ICM20948_BIT_FSYNC_DEGLITCH_EN |
158 ICM20948_BIT_FSYNC_EDGE_EN | ICM20948_BITS_FSYNC_SET_TEMP);
159 itsIMU->writeRegister(ICM20948_REG_INT_PIN_CFG, ICM20948_BIT_INT_FSYNC_EN); //| ICM20948_BIT_INT_ACTL_FSYNC);
160 itsIMU->writeRegister(ICM20948_REG_INT_ENABLE, ICM20948_BIT_FSYNC_INT_EN);
161}
162
163// ####################################################################################################
165{
166 // Configure the DMP if desired:
167 mode::freeze(true);
168 switch (mode::get())
169 {
170 case jevois::imu::Mode::DMP:
171 {
172 // The DMP code was loaded by the kernel driver, which also has set the start address. So here we just need to
173 // launch the DMP. First, disable FIFO and DMP:
174 unsigned char v = itsIMU->readRegister(ICM20948_REG_USER_CTRL);
175 v &= ~(ICM20948_BIT_DMP_EN | ICM20948_BIT_FIFO_EN); v |= ICM20948_BIT_DMP_RST;
176 if (itsIMU->isSPI()) v |= ICM20948_BIT_I2C_IF_DIS;
177 itsIMU->writeRegister(ICM20948_REG_USER_CTRL, v);
178 std::this_thread::sleep_for(std::chrono::milliseconds(1));
179
180 // Reset / setup the FIFO:
181 itsIMU->writeRegister(ICM20948_REG_FIFO_CFG, ICM20948_BIT_MULTI_FIFO_CFG); // FIFO Config
182 itsIMU->writeRegister(ICM20948_REG_FIFO_RST, 0x1f); // Reset all FIFOs.
183 itsIMU->writeRegister(ICM20948_REG_FIFO_RST, 0x1e); // Keep all but Gyro FIFO in reset.
184 itsIMU->writeRegister(ICM20948_REG_FIFO_EN_1, 0x0); // Slave FIFO turned off.
185 itsIMU->writeRegister(ICM20948_REG_FIFO_EN_2, 0x0); // Hardware FIFO turned off.
186
187 itsIMU->writeRegister(ICM20948_REG_SINGLE_FIFO_PRIORITY_SEL, 0xe4); // Use a single interrupt for FIFO
188
189 // Enable DMP interrupts:
190 itsIMU->writeRegister(ICM20948_REG_INT_ENABLE,
191 ICM20948_BIT_FSYNC_INT_EN | ICM20948_BIT_DMP_INT_EN); // Enable DMP Interrupt
192 itsIMU->writeRegister(ICM20948_REG_INT_ENABLE_1, ICM20948_BIT_RAW_DATA_0_RDY_EN | ICM20948_BIT_RAW_DATA_1_RDY_EN |
193 ICM20948_BIT_RAW_DATA_2_RDY_EN |
194 ICM20948_BIT_RAW_DATA_3_RDY_EN); // Enable raw data ready Interrupt
195 itsIMU->writeRegister(ICM20948_REG_INT_ENABLE_2, ICM20948_BIT_FIFO_OVERFLOW_EN_0); // Enable FIFO Overflow Interrupt
196
197 // Force an execution of our dmp callback:
198 dmp::set(dmp::get());
199
200 // Enable DMP:
201 v |= ICM20948_BIT_DMP_EN | ICM20948_BIT_FIFO_EN; v &= ~ICM20948_BIT_DMP_RST;
202 if (itsIMU->isSPI()) v |= ICM20948_BIT_I2C_IF_DIS;
203 itsIMU->writeRegister(ICM20948_REG_USER_CTRL, v);
204
205 LINFO("IMU Digital Motion Processor (DMP) enabled.");
206 }
207 break;
208
209 case jevois::imu::Mode::FIFO:
210 {
211 // Disable the DMP:
212 unsigned char v = itsIMU->readRegister(ICM20948_REG_USER_CTRL);
213 v &= ~ICM20948_BIT_DMP_EN; v |= ICM20948_BIT_DMP_RST;
214 if (itsIMU->isSPI()) v |= ICM20948_BIT_I2C_IF_DIS;
215 itsIMU->writeRegister(ICM20948_REG_USER_CTRL, v);
216
217 // Enable FIFO, set packet size, nuke any old FIFO data:
218 computeFIFOpktSize(-1.0F, -1.0F, -1);
219
220 // DMP is not available:
221 dmp::freeze(true);
222
223 LINFO("IMU FIFO mode enabled.");
224 }
225 break;
226
227 case jevois::imu::Mode::RAW:
228 {
229 // Disable the DMP and FIFO:
230 unsigned char v = itsIMU->readRegister(ICM20948_REG_USER_CTRL);
231 v &= ~(ICM20948_BIT_DMP_EN | ICM20948_BIT_FIFO_EN); v |= ICM20948_BIT_DMP_RST;
232 if (itsIMU->isSPI()) v |= ICM20948_BIT_I2C_IF_DIS;
233 itsIMU->writeRegister(ICM20948_REG_USER_CTRL, v);
234
235 // DMP is not available:
236 dmp::freeze(true);
237 pktdbg::freeze(true);
238
239 LINFO("IMU raw data mode enabled.");
240 }
241 break;
242 }
243}
244
245// ####################################################################################################
247{
248 mode::freeze(false);
249}
250
251// ####################################################################################################
253{
254 return (itsIMU->readRegister(ICM20948_REG_WHO_AM_I) == ICM20948_DEVICE_ID);
255}
256
257// ####################################################################################################
259{
260 switch(mode::get())
261 {
262 case jevois::imu::Mode::FIFO:
263 case jevois::imu::Mode::DMP:
264 {
265 unsigned char datalen[2];
266 itsIMU->readRegisterArray(ICM20948_REG_FIFO_COUNT_H, &datalen[0], 2);
267 return (datalen[0] << 8) + datalen[1];
268 }
269 case jevois::imu::Mode::RAW:
270 {
271 unsigned char val = itsIMU->readRegister(ICM20948_REG_INT_STATUS_1);
272 if (val & ICM20948_BIT_RAW_DATA_0_RDY_INT) return 1;
273 return 0;
274 }
275 }
276 return 0; // Keep compiler happy
277}
278
279// ####################################################################################################
281{
282 IMUrawData d;
283
284 switch(mode::get())
285 {
286 // ----------------------------------------------------------------------------------------------------
287 case jevois::imu::Mode::FIFO:
288 {
289 int siz = dataReady();
290
291 // Gobble up the FIFO if it is getting full, so that we never get out of sync:
292 if (siz > 500)
293 {
294 LERROR("IMU FIFO filling up. You need to call get() more often or reduce rate. Data will be lost.");
295 while (siz > 100)
296 {
297 unsigned char trash[itsFIFOpktSiz];
298 itsIMU->readRegisterArray(ICM20948_REG_FIFO_R_W, &trash[0], itsFIFOpktSiz);
299 siz = dataReady();
300 }
301 }
302
303 if (siz < itsFIFOpktSiz)
304 {
305 if (blocking == false) return d;
306
307 auto tooLate = std::chrono::steady_clock::now() + std::chrono::seconds(2);
308 do
309 {
310 std::this_thread::sleep_for(std::chrono::milliseconds(1));
311 if (std::chrono::steady_clock::now() > tooLate)
312 { LERROR("TIMEOUT waiting for IMU FIFO data - RETURNING BLANK"); return d; }
313 siz = dataReady();
314 }
315 while (siz < itsFIFOpktSiz);
316 }
317
318 // Read the packet:
319 unsigned char packet[itsFIFOpktSiz];
320 itsIMU->readRegisterArray(ICM20948_REG_FIFO_R_W, &packet[0], itsFIFOpktSiz);
321
322 // Decode the packet:
323 int off = 0;
324 if (arate::get() > 0.0F)
325 {
326 d.ax() = (packet[off + 0] << 8) | packet[off + 1];
327 d.ay() = (packet[off + 2] << 8) | packet[off + 3];
328 d.az() = (packet[off + 4] << 8) | packet[off + 5];
329 off += 6;
330 }
331 if (grate::get() > 0.0F)
332 {
333 d.gx() = (packet[off + 0] << 8) | packet[off + 1];
334 d.gy() = (packet[off + 2] << 8) | packet[off + 3];
335 d.gz() = (packet[off + 4] << 8) | packet[off + 5];
336 off += 6;
337 }
338 if (mrate::get() != jevois::imu::MagRate::Off && mrate::get() != jevois::imu::MagRate::Once)
339 {
340 d.mx() = (packet[off + 0] << 8) | packet[off + 1];
341 d.my() = (packet[off + 2] << 8) | packet[off + 3];
342 d.mz() = (packet[off + 4] << 8) | packet[off + 5];
343 d.mst2() = (packet[off + 6] << 8) | packet[off + 7];
344 off += 8;
345 }
346 d.temp() = 0; // temp not available in FIFO mode
347
348 // Debug raw packet dump:
349 if (pktdbg::get())
350 {
351 std::stringstream stream; stream << "RAW" << std::hex;
352 for (int i = 0; i < itsFIFOpktSiz; ++i) stream << ' ' << (unsigned int)(packet[i]);
353 LINFO(stream.str());
354 }
355 }
356 break;
357
358 // ----------------------------------------------------------------------------------------------------
359 case jevois::imu::Mode::RAW:
360 case jevois::imu::Mode::DMP:
361 {
362 // Grab the raw data register contents:
363 itsIMU->readRegisterArray(ICM20948_REG_ACCEL_XOUT_H_SH, reinterpret_cast<unsigned char *>(&d.v[0]), 11*2);
364
365 // The data from the sensor is in big endian, convert to little endian:
366 for (short & s : d.v) { short hi = (s & 0xff00) >> 8; short lo = s & 0x00ff; s = (lo << 8) | hi; }
367 }
368 break;
369 }
370
371 return d;
372}
373
374// ####################################################################################################
376{
377 return jevois::IMUdata(getRaw(blocking), jevois::imu::arange::get(), jevois::imu::grange::get());
378}
379
380// ####################################################################################################
382{
383 if (mode::get() != jevois::imu::Mode::DMP) LFATAL("getDMP() only available when mode=DMP, see params.cfg");
384 DMPdata d;
385
386 // Start parsing a new packet?
387 if (itsDMPsz < 4)
388 {
389 // We need at least 4 bytes in the FIFO to get going. Could be either two empty packets (may not exist), or the
390 // start of a non-empty packet, possibly with a header2 (which is why we want 4):
391 size_t got = getDMPsome(blocking, 4);
392 if (got < 4) return d;
393 }
394
395 // Parse the headers:
396 unsigned short ctl1 = (itsDMPpacket[0] << 8) | itsDMPpacket[1];
397 int off = 2; // offset to where we are in parsing the packet so far
398 unsigned short ctl2 = 0;
399 if (ctl1 & JEVOIS_DMP_HEADER2) { ctl2 = (itsDMPpacket[off] << 8) | itsDMPpacket[off + 1]; off += 2; }
400
401 // Compute how much data remains to be grabbed for this packet:
402 size_t need = jevois::DMPpacketSize(ctl1, ctl2) - itsDMPsz;
403
404 // Read the rest of the packet if needed:
405 while (need > 0)
406 {
407 // We can read out max 32 bytes at a time:
408 size_t need2 = std::min(need, size_t(32));
409 size_t got = getDMPsome(blocking, need2);
410 if (got < need2) return d;
411 need -= got;
412 }
413
414 // We have the whole packet, parse it:
415 d.parsePacket(itsDMPpacket, itsDMPsz);
416
417 // Debug raw packet dump:
418 if (pktdbg::get())
419 {
420 std::stringstream stream; stream << "RAW" << std::hex;
421 for (int i = 0; i < itsDMPsz; ++i) stream << ' ' << (unsigned int)(itsDMPpacket[i]);
422 LINFO(stream.str());
423 }
424
425 // Done with this packet, nuke it:
426 itsDMPsz = 0;
427
428 return d;
429}
430
431// ####################################################################################################
432size_t jevois::ICM20948::getDMPsome(bool blocking, size_t desired)
433{
434 size_t siz = dataReady();
435
436 if (siz < desired)
437 {
438 if (blocking == false) return 0;
439
440 auto tooLate = std::chrono::steady_clock::now() + std::chrono::seconds(2);
441 do
442 {
443 std::this_thread::sleep_for(std::chrono::milliseconds(1));
444
445 if (std::chrono::steady_clock::now() >= tooLate)
446 { LERROR("TIMEOUT waiting for IMU DMP data - RETURNING BLANK"); return 0; }
447
448 siz = dataReady();
449 }
450 while (siz < desired);
451 }
452
453 // We have enough data in the FIFO, read out what we wanted:
454 itsIMU->readRegisterArray(ICM20948_REG_FIFO_R_W, &itsDMPpacket[itsDMPsz], desired);
455 itsDMPsz += desired;
456
457 return desired;
458}
459
460
461// ####################################################################################################
463{
464 // Set H_RESET bit to initiate soft reset:
465 itsIMU->writeRegister(ICM20948_REG_PWR_MGMT_1, ICM20948_BIT_H_RESET | ICM20948_BIT_CLK_PLL);
466
467 // Wait a bit to complete the reset sequence:
468 std::this_thread::sleep_for(std::chrono::milliseconds(30));
469}
470
471// ####################################################################################################
472void jevois::ICM20948::onParamChange(jevois::imu::arate const &, float const & newval)
473{
474 // Disable or enable accelerometer:
475 uint8_t pwr = itsIMU->readRegister(ICM20948_REG_PWR_MGMT_2);
476 if (newval == 0.0F) pwr |= ICM20948_BIT_PWR_ACCEL_STBY; else pwr &= ~ICM20948_BIT_PWR_ACCEL_STBY;
477 itsIMU->writeRegister(ICM20948_REG_PWR_MGMT_2, pwr);
478
479 if (newval)
480 {
481 // Calculate the sample rate divider:
482 float accelSampleRate = (1125.0F / newval) - 1.0F;
483
484 // Check if it fits in the divider registers:
485 if (accelSampleRate > 4095.0F) accelSampleRate = 4095.0F; else if (accelSampleRate < 0.0F) accelSampleRate = 0.0F;
486
487 // Write the value to the registers:
488 uint16_t const accelDiv = uint16_t(accelSampleRate);
489 itsIMU->writeRegister(ICM20948_REG_ACCEL_SMPLRT_DIV_1, uint8_t(accelDiv >> 8) );
490 itsIMU->writeRegister(ICM20948_REG_ACCEL_SMPLRT_DIV_2, uint8_t(accelDiv & 0xFF) );
491
492 // Calculate the actual sample rate from the divider value:
493 LINFO("Accelerometer sampling rate set to " << 1125.0F / (accelDiv + 1.0F) << " Hz");
494 }
495
496 computeFIFOpktSize(newval, -1.0F, -1);
497}
498
499// ####################################################################################################
500void jevois::ICM20948::onParamChange(jevois::imu::grate const &, float const & newval)
501{
502 // Disable or enable gyro:
503 uint8_t pwr = itsIMU->readRegister(ICM20948_REG_PWR_MGMT_2);
504 if (newval == 0.0F) pwr |= ICM20948_BIT_PWR_GYRO_STBY; else pwr &= ~ICM20948_BIT_PWR_GYRO_STBY;
505 itsIMU->writeRegister(ICM20948_REG_PWR_MGMT_2, pwr);
506
507 if (newval)
508 {
509 // Calculate the sample rate divider:
510 float gyroSampleRate = (1125.0F / newval) - 1.0F; // FIXME: code says 1125, datasheet says 1100
511
512 // Check if it fits in the divider registers:
513 if (gyroSampleRate > 255.0F) gyroSampleRate = 255.0F; else if (gyroSampleRate < 0.0F) gyroSampleRate = 0.0F;
514
515 // Write the value to the register:
516 uint8_t const gyroDiv = uint8_t(gyroSampleRate);
517 itsIMU->writeRegister(ICM20948_REG_GYRO_SMPLRT_DIV, gyroDiv);
518
519 // Calculate the actual sample rate from the divider value:
520 LINFO("Gyroscope sampling rate set to " << 1125.0F / (gyroDiv + 1.0F) << " Hz");
521 }
522 else std::this_thread::sleep_for(std::chrono::microseconds(22)); // Per datasheet: wait 22us after disabling gyro
523
524 computeFIFOpktSize(-1.0F, newval, -1);
525}
526
527// ####################################################################################################
528void jevois::ICM20948::onParamChange(jevois::imu::mrate const &, jevois::imu::MagRate const & newval)
529{
530 // Turn off the magnetometer:
531 writeMagRegister(REG_AK09916_CNTL2, VAL_AK09916_CNTL2_PD);
532
533 // Set the mode value:
534 unsigned char mod;
535 switch (newval)
536 {
537 case jevois::imu::MagRate::Off: mod = VAL_AK09916_CNTL2_PD; break;
538 case jevois::imu::MagRate::Once: mod = VAL_AK09916_CNTL2_SNGL; break;
539 case jevois::imu::MagRate::M10Hz: mod = VAL_AK09916_CNTL2_MOD1; break;
540 case jevois::imu::MagRate::M20Hz: mod = VAL_AK09916_CNTL2_MOD2; break;
541 case jevois::imu::MagRate::M50Hz: mod = VAL_AK09916_CNTL2_MOD3; break;
542 case jevois::imu::MagRate::M100Hz: mod = VAL_AK09916_CNTL2_MOD4; break;
543 default: LFATAL("Invalid mode value: " << newval);
544 }
545
546 // Wait until mag is down:
547 std::this_thread::sleep_for(std::chrono::milliseconds(10));
548
549 // Now turn it back on in the specified mode:
550 writeMagRegister(REG_AK09916_CNTL2, mod);
551
552 // Set output data rate (but is overridden by gyro ODR wen gyro is on):
553 itsIMU->writeRegister(ICM20948_REG_I2C_MST_ODR_CONFIG, 0x04); // Rate is 1.1kHz/(2^value)
554
555 // Wait for magnetometer to be on:
556 std::this_thread::sleep_for(std::chrono::milliseconds(10));
557
558 computeFIFOpktSize(-1.0F, -1.0F, mod);
559
560 // Setup to transfer 8 bytes (RAW, FIFO) or 6 bytes (DMP) of data from magnetometer to ICM20948 main:
561 itsIMU->writeRegister(ICM20948_REG_I2C_SLV0_ADDR, ICM20948_BIT_I2C_READ | COMPASS_SLAVEADDR);
562 itsIMU->writeRegister(ICM20948_REG_I2C_SLV0_REG, REG_AK09916_HXL);
563 int siz = (mode::get() == jevois::imu::Mode::DMP) ? 6 : 8;
564 itsIMU->writeRegister(ICM20948_REG_I2C_SLV0_CTRL, // Enable, byteswap, odd-grouping, and read 8 or 6 bytes
565 ICM20948_BIT_I2C_SLV_EN | ICM20948_BIT_I2C_BYTE_SW | ICM20948_BIT_I2C_GRP | siz);
566}
567
568// ####################################################################################################
569void jevois::ICM20948::onParamChange(jevois::imu::abw const &, unsigned int const & newval)
570{
571 uint8_t reg = itsIMU->readRegister(ICM20948_REG_ACCEL_CONFIG);
572
573 switch (newval)
574 {
575 case 0: reg &= ~ICM20948_BIT_ACCEL_FCHOICE; break; // turn off low-pass filter
576 case 6: reg &= ~ICM20948_MASK_ACCEL_BW; reg |= ICM20948_ACCEL_BW_6HZ; break;
577 case 12: reg &= ~ICM20948_MASK_ACCEL_BW; reg |= ICM20948_ACCEL_BW_12HZ; break;
578 case 24: reg &= ~ICM20948_MASK_ACCEL_BW; reg |= ICM20948_ACCEL_BW_24HZ; break;
579 case 50: reg &= ~ICM20948_MASK_ACCEL_BW; reg |= ICM20948_ACCEL_BW_50HZ; break;
580 case 111: reg &= ~ICM20948_MASK_ACCEL_BW; reg |= ICM20948_ACCEL_BW_111HZ; break;
581 case 246: reg &= ~ICM20948_MASK_ACCEL_BW; reg |= ICM20948_ACCEL_BW_246HZ; break;
582 case 470: reg &= ~ICM20948_MASK_ACCEL_BW; reg |= ICM20948_ACCEL_BW_470HZ; break;
583 case 1210: reg &= ~ICM20948_MASK_ACCEL_BW; reg |= ICM20948_ACCEL_BW_1210HZ; break;
584 default: LFATAL("Invalid value");
585 }
586
587 itsIMU->writeRegister(ICM20948_REG_ACCEL_CONFIG, reg);
588}
589
590// ####################################################################################################
591void jevois::ICM20948::onParamChange(jevois::imu::gbw const &, unsigned int const & newval)
592{
593 uint8_t reg = itsIMU->readRegister(ICM20948_REG_GYRO_CONFIG_1);
594
595 switch (newval)
596 {
597 case 0: reg &= ~ICM20948_BIT_GYRO_FCHOICE; break; // turn off low-pass filter
598 case 6: reg &= ~ICM20948_MASK_GYRO_BW; reg |= ICM20948_GYRO_BW_6HZ; break;
599 case 12: reg &= ~ICM20948_MASK_GYRO_BW; reg |= ICM20948_GYRO_BW_12HZ; break;
600 case 24: reg &= ~ICM20948_MASK_GYRO_BW; reg |= ICM20948_GYRO_BW_24HZ; break;
601 case 51: reg &= ~ICM20948_MASK_GYRO_BW; reg |= ICM20948_GYRO_BW_51HZ; break;
602 case 120: reg &= ~ICM20948_MASK_GYRO_BW; reg |= ICM20948_GYRO_BW_120HZ; break;
603 case 150: reg &= ~ICM20948_MASK_GYRO_BW; reg |= ICM20948_GYRO_BW_150HZ; break;
604 case 200: reg &= ~ICM20948_MASK_GYRO_BW; reg |= ICM20948_GYRO_BW_200HZ; break;
605 case 360: reg &= ~ICM20948_MASK_GYRO_BW; reg |= ICM20948_GYRO_BW_360HZ; break;
606 case 12100: reg &= ~ICM20948_MASK_GYRO_BW; reg |= ICM20948_GYRO_BW_12100HZ; break;
607 default: LFATAL("Invalid value");
608 }
609
610 itsIMU->writeRegister(ICM20948_REG_GYRO_CONFIG_1, reg);
611}
612
613// ####################################################################################################
614void jevois::ICM20948::onParamChange(jevois::imu::tbw const &, unsigned int const & newval)
615{
616 // Disable or enable temperature:
617 uint8_t pwr = itsIMU->readRegister(ICM20948_REG_PWR_MGMT_1);
618 if (newval == 0) pwr |= ICM20948_BIT_TEMP_DIS; else pwr &= ~ICM20948_BIT_TEMP_DIS;
619 itsIMU->writeRegister(ICM20948_REG_PWR_MGMT_1, pwr);
620
621 if (newval == 0) return;
622
623 switch (newval)
624 {
625 case 9: itsIMU->writeRegister(ICM20948_REG_TEMP_CONFIG, 6); break;
626 case 17: itsIMU->writeRegister(ICM20948_REG_TEMP_CONFIG, 5); break;
627 case 34: itsIMU->writeRegister(ICM20948_REG_TEMP_CONFIG, 4); break;
628 case 66: itsIMU->writeRegister(ICM20948_REG_TEMP_CONFIG, 3); break;
629 case 123: itsIMU->writeRegister(ICM20948_REG_TEMP_CONFIG, 2); break;
630 case 218: itsIMU->writeRegister(ICM20948_REG_TEMP_CONFIG, 1); break;
631 case 7932: itsIMU->writeRegister(ICM20948_REG_TEMP_CONFIG, 0); break;
632 default: LFATAL("Invalid value");
633 }
634}
635
636// ####################################################################################################
637void jevois::ICM20948::onParamChange(jevois::imu::arange const &, unsigned int const & newval)
638{
639 uint8_t reg = itsIMU->readRegister(ICM20948_REG_ACCEL_CONFIG) & ~ICM20948_MASK_ACCEL_FULLSCALE;
640
641 switch (newval)
642 {
643 case 2: reg |= ICM20948_ACCEL_FULLSCALE_2G; break;
644 case 4: reg |= ICM20948_ACCEL_FULLSCALE_4G; break;
645 case 8: reg |= ICM20948_ACCEL_FULLSCALE_8G; break;
646 case 16: reg |= ICM20948_ACCEL_FULLSCALE_16G; break;
647 default: LFATAL("Invalid value");
648 }
649 itsIMU->writeRegister(ICM20948_REG_ACCEL_CONFIG, reg);
650}
651
652// ####################################################################################################
653void jevois::ICM20948::onParamChange(jevois::imu::grange const &, unsigned int const & newval)
654{
655 uint8_t reg = itsIMU->readRegister(ICM20948_REG_GYRO_CONFIG_1) & ~ICM20948_MASK_GYRO_FULLSCALE;
656
657 switch (newval)
658 {
659 case 250: reg |= ICM20948_GYRO_FULLSCALE_250DPS; break;
660 case 500: reg |= ICM20948_GYRO_FULLSCALE_500DPS; break;
661 case 1000: reg |= ICM20948_GYRO_FULLSCALE_1000DPS; break;
662 case 2000: reg |= ICM20948_GYRO_FULLSCALE_2000DPS; break;
663 default: LFATAL("Invalid value");
664 }
665 itsIMU->writeRegister(ICM20948_REG_GYRO_CONFIG_1, reg);
666}
667
668// ####################################################################################################
669void jevois::ICM20948::sleep(bool enable)
670{
671 uint8_t reg = itsIMU->readRegister(ICM20948_REG_PWR_MGMT_1);
672
673 if (enable) reg |= ICM20948_BIT_SLEEP;
674 else reg &= ~(ICM20948_BIT_SLEEP);
675
676 reg |= ICM20948_BIT_CLK_PLL;
677
678 itsIMU->writeRegister(ICM20948_REG_PWR_MGMT_1, reg);
679
680 std::this_thread::sleep_for(std::chrono::milliseconds(2));
681}
682
683// ####################################################################################################
684void jevois::ICM20948::cycle(bool enable)
685{
686 uint8_t reg = ICM20948_REG_LP_CONFIG;
687 uint8_t const mask = ICM20948_BIT_I2C_MST_CYCLE | ICM20948_BIT_ACCEL_CYCLE | ICM20948_BIT_GYRO_CYCLE;
688
689 if (enable) reg |= mask; else reg &= ~mask;
690
691 itsIMU->writeRegister(ICM20948_REG_LP_CONFIG, reg);
692}
693
694// ####################################################################################################
696{ return itsIMU->readRegister(ICM20948_REG_WHO_AM_I); }
697
698// ####################################################################################################
699void jevois::ICM20948::onParamChange(imu::dmp const &, std::string const & newval)
700{
701 unsigned short ctl1 = 0, ctl2 = 0, mec = 0;
702 bool a = false, g = false, m = false, h2 = false;
703
704 LINFO("Setting dmp parameter to " << newval);
705
706 for (unsigned char c : newval)
707 switch(c)
708 {
709 case 'A': ctl1 |= JEVOIS_DMP_ACCEL; a = true; break;
710 case 'G': ctl1 |= JEVOIS_DMP_GYRO; g = true; break;
711 case 'M': ctl1 |= JEVOIS_DMP_CPASS; m = true; break;
712 case 'R': ctl1 |= JEVOIS_DMP_QUAT6; a = true; g = true; break;
713 case 'Q': ctl1 |= JEVOIS_DMP_QUAT9; a = true; g = true; m = true; mec |= JEVOIS_DMP_NINE_AXIS_EN; break;
714 case 'E': ctl1 |= JEVOIS_DMP_GEOMAG; a = true; g = true; m = true; mec |= JEVOIS_DMP_GEOMAG_EN; break;
715 case 'g': ctl1 |= JEVOIS_DMP_GYRO_CALIBR; g = true; break;
716 case 'm': ctl1 |= JEVOIS_DMP_CPASS_CALIBR; m = true; break;
717 case 'S': ctl1 |= JEVOIS_DMP_PED_STEPDET; a = true; mec |= JEVOIS_DMP_PEDOMETER_EN; break;
718 case 'b': ctl2 |= JEVOIS_DMP_ACCEL_ACCURACY; h2 = true; a = true; break;
719 case 'h': ctl2 |= JEVOIS_DMP_GYRO_ACCURACY; h2 = true; g = true; break;
720 case 'n': ctl2 |= JEVOIS_DMP_CPASS_ACCURACY; h2 = true; m = true; break;
721 case 'P': ctl2 |= JEVOIS_DMP_FLIP_PICKUP; h2 = true; a = true; g = true; mec |= JEVOIS_DMP_FLIP_PICKUP_EN; break;
722 case 'T': ctl2 |= JEVOIS_DMP_ACT_RECOG; h2 = true; a = true; g = true;
725 case 'w': mec |= JEVOIS_DMP_BAC_WEARABLE_EN; break;
726 case 'F': h2 = true; ctl2 |= JEVOIS_DMP_FSYNC; break;
727
728 default: LERROR("Phony character '" << c << "' ignored while parsing parameter dmp.");
729 }
730
731 // Apply a few dependencies:
732 if (a) { h2 = true; ctl2 |= JEVOIS_DMP_ACCEL_ACCURACY; mec |= JEVOIS_DMP_ACCEL_CAL_EN; }
733 if (g) { h2 = true; ctl2 |= JEVOIS_DMP_GYRO_ACCURACY; mec |= JEVOIS_DMP_GYRO_CAL_EN; }
734 if (m) { h2 = true; ctl2 |= JEVOIS_DMP_CPASS_ACCURACY; mec |= JEVOIS_DMP_COMPASS_CAL_EN; }
735 if (h2) ctl1 |= JEVOIS_DMP_HEADER2;
736
737 // Set the two control parameters in the IMU:
738 itsIMU->writeDMPregister(DMP_DATA_OUT_CTL1, ctl1);
739 itsIMU->writeDMPregister(DMP_DATA_OUT_CTL2, ctl2);
740 itsIMU->writeDMPregister(DMP_FIFO_WATERMARK, 800);
741
742 // Setup the DMP:
743 itsIMU->writeDMPregister(DMP_DATA_INTR_CTL, ctl1);
744 itsIMU->writeDMPregister(DMP_MOTION_EVENT_CTL, mec);
745 itsIMU->writeDMPregister(DMP_DATA_RDY_STATUS, (a ? 0x02 : 0x00) | (g ? 0x01 : 0x00) | (m ? 0x08 : 0x00));
746}
747
748// ####################################################################################################
749void jevois::ICM20948::computeFIFOpktSize(float ar, float gr, int mm)
750{
751 itsFIFOpktSiz = 0;
752
753 // If we are in FIFO mode, make sure we send that data to the FIFO:
754 if (mode::get() == jevois::imu::Mode::FIFO)
755 {
756 // Since this is called from within parameter callbacks, we need a bit of trickery to get the new rate:
757 float acc; if (ar < 0.0F) acc = arate::get(); else acc = ar;
758 float gyr; if (gr < 0.0F) gyr = grate::get(); else gyr = gr;
759 jevois::imu::MagRate mag; if (mm == -1) mag = mrate::get(); else mag = jevois::imu::MagRate::M100Hz; // any value ok
760
761 // Packet size may change. We need to nuke the FIFO to avoid mixed packets:
762 unsigned char ctl = itsIMU->readRegister(ICM20948_REG_USER_CTRL);
763 ctl &= ~ICM20948_BIT_FIFO_EN;
764 if (itsIMU->isSPI()) ctl |= ICM20948_BIT_I2C_IF_DIS;
765 itsIMU->writeRegister(ICM20948_REG_USER_CTRL, ctl);
766
767 itsIMU->writeRegister(ICM20948_REG_FIFO_CFG, ICM20948_BIT_SINGLE_FIFO_CFG); // FIFO Config
768 itsIMU->writeRegister(ICM20948_REG_FIFO_RST, 0x1f); // Reset all FIFOs.
769 itsIMU->writeRegister(ICM20948_REG_FIFO_EN_1, 0x0); // Slave FIFO turned off.
770 itsIMU->writeRegister(ICM20948_REG_FIFO_EN_2, 0x0); // Hardware FIFO turned off.
771
772 std::this_thread::sleep_for(std::chrono::milliseconds(100)); // FIXME: should wait more at low rates...
773
774 int siz = dataReady(); unsigned char trash[16];
775 while (siz)
776 {
777 itsIMU->readRegisterArray(ICM20948_REG_FIFO_R_W, &trash[0], std::min(siz, 16));
778 siz = dataReady();
779 }
780
781 itsIMU->writeRegister(ICM20948_REG_FIFO_RST, 0x00); // Stop FIFO reset
782
783 unsigned char v = itsIMU->readRegister(ICM20948_REG_FIFO_EN_2);
784 if (acc > 0.0F) { v |= ICM20948_BIT_ACCEL_FIFO_EN; itsFIFOpktSiz += 6; } // 3 axes, short int values
785 else v &= ~ICM20948_BIT_ACCEL_FIFO_EN;
786 if (gyr > 0.0F) { v |= ICM20948_BITS_GYRO_FIFO_EN; itsFIFOpktSiz += 6; } // 3 axes, short int values
787 else v &= ~ICM20948_BITS_GYRO_FIFO_EN;
788 itsIMU->writeRegister(ICM20948_REG_FIFO_EN_2, v);
789
790 v = itsIMU->readRegister(ICM20948_REG_FIFO_EN_1);
791 if (mag != jevois::imu::MagRate::Off && mag != jevois::imu::MagRate::Once)
792 { v |= ICM20948_BIT_SLV_0_FIFO_EN; itsFIFOpktSiz += 8; } // 3 axes, short int values + short status
793 else v &= ~ICM20948_BIT_SLV_0_FIFO_EN;
794 itsIMU->writeRegister(ICM20948_REG_FIFO_EN_1, v);
795
796 // Enable FIFO:
797 ctl |= ICM20948_BIT_FIFO_EN;
798 if (itsIMU->isSPI()) ctl |= ICM20948_BIT_I2C_IF_DIS;
799 itsIMU->writeRegister(ICM20948_REG_USER_CTRL, ctl);
800 }
801}
802
#define JEVOIS_DMP_PEDOMETER_EN
Definition ICM20948.C:49
#define JEVOIS_DMP_BRING_AND_LOOK_T0_SEE_EN
Definition ICM20948.C:59
#define JEVOIS_DMP_NINE_AXIS_EN
Definition ICM20948.C:58
#define JEVOIS_DMP_FLIP_PICKUP_EN
Definition ICM20948.C:53
#define JEVOIS_DMP_ACCEL_CAL_EN
Definition ICM20948.C:55
#define JEVOIS_DMP_GEOMAG_EN
Definition ICM20948.C:54
#define JEVOIS_DMP_BTS_EN
Definition ICM20948.C:52
#define JEVOIS_DMP_GYRO_CAL_EN
Definition ICM20948.C:56
#define JEVOIS_DMP_BAC_WEARABLE_EN
Definition ICM20948.C:48
#define JEVOIS_DMP_SMD_EN
Definition ICM20948.C:51
#define JEVOIS_DMP_COMPASS_CAL_EN
Definition ICM20948.C:57
#define DMP_DATA_OUT_CTL1
#define DMP_DATA_INTR_CTL
#define DMP_FIFO_WATERMARK
#define DMP_MOTION_EVENT_CTL
#define DMP_DATA_OUT_CTL2
#define DMP_DATA_RDY_STATUS
A component of a model hierarchy.
Definition Component.H:182
void sleep(bool enable)
Turn on/off sleep mode.
Definition ICM20948.C:669
DMPdata getDMP(bool blocking=true)
Get one packet of DMP data.
Definition ICM20948.C:381
void cycle(bool enable)
Turn on/off cycle mode vs continuous for accel, gyro and compass.
Definition ICM20948.C:684
void reset()
Reset the IMU chip - not recommended in normal operation.
Definition ICM20948.C:462
virtual ~ICM20948()
Virtual destructor for safe inheritance.
Definition ICM20948.C:67
bool ready()
Returns true if this camera indeed has a working ICM20948.
Definition ICM20948.C:252
void preInit() override
Connect to and initialize the IMU chip.
Definition ICM20948.C:114
void postInit() override
Configure RAW vs DMP mode:
Definition ICM20948.C:164
IMUdata get(bool blocking=true)
Get one round of scaled raw data.
Definition ICM20948.C:375
uint32_t devid()
Read device ID.
Definition ICM20948.C:695
IMUrawData getRaw(bool blocking=true)
Get one round of raw data.
Definition ICM20948.C:280
void preUninit() override
Unfreeze any previously frozen parameters.
Definition ICM20948.C:246
ICM20948(std::string const &instance)
Constructor, low-level communication driver is null.
Definition ICM20948.C:62
int dataReady()
Returns the amount of new data that had not previously been obtained.
Definition ICM20948.C:258
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition Log.H:211
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition Log.H:194
#define JEVOIS_DMP_GYRO_CALIBR
Definition IMUdata.H:109
#define JEVOIS_DMP_HEADER2
Definition IMUdata.H:112
#define JEVOIS_DMP_ACT_RECOG
Definition IMUdata.H:121
#define JEVOIS_DMP_ACCEL
Definition IMUdata.H:100
#define JEVOIS_DMP_QUAT6
Definition IMUdata.H:104
#define JEVOIS_DMP_GEOMAG
Definition IMUdata.H:107
#define JEVOIS_DMP_PED_STEPDET
Definition IMUdata.H:111
#define JEVOIS_DMP_GYRO
Definition IMUdata.H:101
#define JEVOIS_DMP_GYRO_ACCURACY
Definition IMUdata.H:116
#define JEVOIS_DMP_CPASS_ACCURACY
Definition IMUdata.H:117
#define JEVOIS_DMP_CPASS_CALIBR
Definition IMUdata.H:110
#define JEVOIS_DMP_ACCEL_ACCURACY
Definition IMUdata.H:115
#define JEVOIS_DMP_CPASS
Definition IMUdata.H:102
#define JEVOIS_DMP_FLIP_PICKUP
Definition IMUdata.H:119
#define JEVOIS_DMP_QUAT9
Definition IMUdata.H:105
#define JEVOIS_DMP_FSYNC
Definition IMUdata.H:118
size_t DMPpacketSize(unsigned short ctl1, unsigned short ctl2)
Helper function to determine DMP packet size depending on options.
Definition IMUdata.C:52
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2
DMP data (Digital Motion Processor)
Definition IMUdata.H:130
void parsePacket(unsigned char const *packet, size_t siz)
Populate our fields from a packet received from the DMP.
Definition IMUdata.C:84
IMU data.
Definition IMUdata.H:68
Raw IMU data.
Definition IMUdata.H:37
short v[11]
The values: ax, ay, az, gy, gy, gz, temp, mx, my, mz, mst2.
Definition IMUdata.H:38
short & gy()
Definition IMUdata.H:48
short & az()
Definition IMUdata.H:44
short & mst2()
Definition IMUdata.H:60
short & mz()
Definition IMUdata.H:58
short & gx()
Definition IMUdata.H:46
short & my()
Definition IMUdata.H:56
short & temp()
Definition IMUdata.H:52
short & ay()
Definition IMUdata.H:42
short & gz()
Definition IMUdata.H:50
short & ax()
Definition IMUdata.H:40
short & mx()
Definition IMUdata.H:54