PX4 Firmware
PX4 Autopilot Software http://px4.io
ICM20948_mag.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2016 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file mag.cpp
36  *
37  * Driver for the ak09916 magnetometer within the Invensense icm20948
38  *
39  * @author Robert Dickenson
40  *
41  */
42 
43 #include <px4_platform_common/px4_config.h>
44 #include <px4_platform_common/log.h>
45 #include <px4_platform_common/time.h>
46 #include <lib/perf/perf_counter.h>
47 #include <drivers/drv_hrt.h>
48 
49 #include "ICM20948_mag.h"
50 #include "icm20948.h"
51 
52 // If interface is non-null, then it will used for interacting with the device.
53 // Otherwise, it will passthrough the parent ICM20948
54 ICM20948_mag::ICM20948_mag(ICM20948 *parent, device::Device *interface, enum Rotation rotation) :
55  _interface(interface),
56  _px4_mag(parent->_interface->get_device_id(), (parent->_interface->external() ? ORB_PRIO_MAX : ORB_PRIO_HIGH),
57  rotation),
58  _parent(parent),
59  _mag_overruns(perf_alloc(PC_COUNT, MODULE_NAME": mag_overruns")),
60  _mag_overflows(perf_alloc(PC_COUNT, MODULE_NAME": mag_overflows")),
61  _mag_errors(perf_alloc(PC_COUNT, MODULE_NAME": mag_errors"))
62 {
65 }
66 
68 {
72 }
73 
74 void
76 {
77  const hrt_abstime timestamp_sample = hrt_absolute_time();
78 
79  uint8_t st1 = 0;
80  int ret = _interface->read(AK09916REG_ST1, &st1, sizeof(st1));
81 
82  if (ret != OK) {
84  return;
85  }
86 
87  /* Check if data ready is set.
88  * This is not described to be set in continuous mode according to the
89  * MPU9250 datasheet. However, the datasheet of the 8963 recommends to
90  * check data ready before doing the read and before triggering the
91  * next measurement by reading ST2. */
92  if (!(st1 & AK09916_ST1_DRDY)) {
93  return;
94  }
95 
96  /* Monitor if data overrun flag is ever set. */
97  if (st1 & 0x02) {
99  }
100 
101  ak09916_regs data{};
102  ret = _interface->read(AK09916REG_ST1, &data, sizeof(data));
103 
104  if (ret != OK) {
106  return;
107  }
108 
109  /* Monitor magnetic sensor overflow flag. */
110  if (data.st2 & 0x08) {
112  }
113 
114  _measure(timestamp_sample, data);
115 }
116 
117 void
119 {
120  /* Check if data ready is set.
121  * This is not described to be set in continuous mode according to the
122  * MPU9250 datasheet. However, the datasheet of the 8963 recommends to
123  * check data ready before doing the read and before triggering the
124  * next measurement by reading ST2.
125  *
126  * If _measure is used in passthrough mode, all the data is already
127  * fetched, however, we should still not use the data if the data ready
128  * is not set. This has lead to intermittent spikes when the data was
129  * being updated while getting read.
130  */
131  if (!(data.st1 & AK09916_ST1_DRDY)) {
132  return;
133  }
134 
137 
138  /*
139  * Align axes - Keeping consistent with the accel and gyro axes of the ICM20948 here, just aligning the magnetometer to them.
140  */
141  _px4_mag.update(timestamp_sample, data.y, data.x, -data.z);
142 }
143 
144 void
145 ICM20948_mag::set_passthrough(uint8_t reg, uint8_t size, uint8_t *out)
146 {
147  uint8_t addr;
148 
149  _parent->write_reg(ICMREG_20948_I2C_SLV0_CTRL, 0); // ensure slave r/w is disabled before changing the registers
150 
151  if (out) {
153  addr = AK09916_I2C_ADDR;
154 
155  } else {
157  }
158 
162 }
163 
164 void
165 ICM20948_mag::read_block(uint8_t reg, uint8_t *val, uint8_t count)
166 {
167  _parent->_interface->read(reg, val, count);
168 }
169 
170 void
171 ICM20948_mag::passthrough_read(uint8_t reg, uint8_t *buf, uint8_t size)
172 {
173  set_passthrough(reg, size);
174  px4_usleep(25 + 25 * size); // wait for the value to be read from slave
176  _parent->write_reg(ICMREG_20948_I2C_SLV0_CTRL, 0); // disable new reads
177 }
178 
179 uint8_t
180 ICM20948_mag::read_reg(unsigned int reg)
181 {
182  uint8_t buf{};
183 
184  if (_interface == nullptr) {
185  passthrough_read(reg, &buf, 0x01);
186 
187  } else {
188  _interface->read(reg, &buf, 1);
189  }
190 
191  return buf;
192 }
193 
194 bool
196 {
197  deviceid = read_reg(AK09916REG_WIA);
198 
199  return (AK09916_DEVICE_ID == deviceid);
200 }
201 
202 /*
203  * 400kHz I2C bus speed = 2.5us per bit = 25us per byte
204  */
205 void
206 ICM20948_mag::passthrough_write(uint8_t reg, uint8_t val)
207 {
208  set_passthrough(reg, 1, &val);
209  px4_usleep(50); // wait for the value to be written to slave
210  _parent->write_reg(ICMREG_20948_I2C_SLV0_CTRL, 0); // disable new writes
211 }
212 
213 void
214 ICM20948_mag::write_reg(unsigned reg, uint8_t value)
215 {
216  // general register transfer at low clock speed
217  if (_interface == nullptr) {
218  passthrough_write(reg, value);
219 
220  } else {
221  _interface->write(ICM20948_LOW_SPEED_OP(reg), &value, 1);
222  }
223 }
224 
225 int
227 {
228  // First initialize it to use the bus
229  int rv = ak09916_setup();
230 
231  if (rv == OK) {
232  // Now reset the mag
234 
235  // Then re-initialize the bus/mag
236  rv = ak09916_setup();
237  }
238 
239  return rv;
240 }
241 
242 bool
244 {
245  uint8_t response[3];
246  float ak09916_ASA[3];
247 
249  px4_usleep(50);
250 
251  if (_interface != nullptr) {
252  _interface->read(AK09916REG_ASAX, response, 3);
253 
254  } else {
255  passthrough_read(AK09916REG_ASAX, response, 3);
256  }
257 
259 
260  for (int i = 0; i < 3; i++) {
261  if (0 != response[i] && 0xff != response[i]) {
262  ak09916_ASA[i] = ((float)(response[i] - 128) / 256.0f) + 1.0f;
263 
264  } else {
265  return false;
266  }
267  }
268 
269  _px4_mag.set_sensitivity(ak09916_ASA[0], ak09916_ASA[1], ak09916_ASA[2]);
270 
271  return true;
272 }
273 
274 int
276 {
277  /* When _interface is null we are using SPI and must
278  * use the parent interface to configure the device to act
279  * in master mode (SPI to I2C bridge)
280  */
281  if (_interface == nullptr) {
282  // ICM20948 -> AK09916
284 
285  // WAIT_FOR_ES does not exist for ICM20948. Not sure how to replace this (or if that is needed)
287 
288  } else {
290  }
291 
292  return OK;
293 }
294 int
296 {
297  int retries = 10;
298 
299  do {
300 
303 
304  uint8_t id = 0;
305 
306  if (ak09916_check_id(id)) {
307  break;
308  }
309 
310  retries--;
311  PX4_WARN("AK09916: bad id %d retries %d", id, retries);
313  up_udelay(100);
314  } while (retries > 0);
315 
316  if (retries == 0) {
317  PX4_ERR("AK09916: failed to initialize, disabled!");
320  return -EIO;
321  }
322 
324 
325  if (_interface == nullptr) {
326  // Configure mpu' I2c Master interface to read ak09916 data into to fifo
328  }
329 
330  return OK;
331 }
#define ICM_BITS_I2C_MST_CLOCK_400HZ
Definition: icm20948.h:283
bool is_external()
Get the internal / external state.
Definition: icm20948.h:528
#define BIT_I2C_SLV0_EN
Definition: icm20948.h:152
perf_counter_t _mag_errors
Definition: ICM20948_mag.h:152
uint8_t read_reg(unsigned reg)
device::Device * _interface
Definition: ICM20948_mag.h:131
void set_device_type(uint8_t devtype)
#define ICMREG_20948_I2C_SLV0_REG
Definition: icm20948.h:230
void set_error_count(uint64_t error_count)
static constexpr float ICM20948_MAG_RANGE_GA
Definition: ICM20948_mag.h:41
ICM20948_mag(ICM20948 *parent, device::Device *interface, enum Rotation rotation)
#define ICM20948_LOW_SPEED_OP(r)
Definition: icm20948.h:339
#define ICMREG_20948_USER_CTRL
Definition: icm20948.h:211
void modify_reg(unsigned reg, uint8_t clearbits, uint8_t setbits)
Modify a register in the mpu.
Definition: icm20948.cpp:532
#define AK09916_FUZE_MODE
Definition: ICM20948_mag.h:57
void read_block(uint8_t reg, uint8_t *val, uint8_t count)
count the number of times an event occurs
Definition: perf_counter.h:55
#define AK09916_ST1_DRDY
Definition: ICM20948_mag.h:88
#define AK09916REG_ST1
Definition: ICM20948_mag.h:74
High-resolution timer with callouts and timekeeping.
#define ICMREG_20948_I2C_MST_CTRL
Definition: icm20948.h:228
void set_external(bool external)
PX4Magnetometer _px4_mag
Definition: ICM20948_mag.h:144
#define AK09916REG_WIA
Definition: ICM20948_mag.h:48
device::Device * _interface
Definition: icm20948.h:365
void write_reg(unsigned reg, uint8_t value)
Write a register in the mpu.
Definition: icm20948.cpp:524
void _measure(hrt_abstime timestamp_sample, ak09916_regs data)
void perf_count(perf_counter_t handle)
Count a performance event.
void set_sensitivity(float x, float y, float z)
void perf_free(perf_counter_t handle)
Free a counter.
void set_temperature(float temperature)
#define AK09916_POWERDOWN_MODE
Definition: ICM20948_mag.h:55
#define AK09916_CNTL2_CONTINOUS_MODE_100HZ
Definition: ICM20948_mag.h:85
#define perf_alloc(a, b)
Definition: px4io.h:59
void passthrough_write(uint8_t reg, uint8_t val)
void update(hrt_abstime timestamp, int16_t x, int16_t y, int16_t z)
Rotation
Enum for board and external compass rotations.
Definition: rotation.h:51
uint8_t * data
Definition: dataman.cpp:149
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
perf_counter_t _mag_overruns
Definition: ICM20948_mag.h:150
#define ICMREG_20948_I2C_SLV0_CTRL
Definition: icm20948.h:231
int ak09916_setup_master_i2c()
#define ICMREG_20948_EXT_SLV_SENS_DATA_00
Definition: icm20948.h:220
#define BIT_I2C_MST_EN
Definition: icm20948.h:146
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
virtual int write(unsigned address, void *data, unsigned count)
Write directly to the device.
Definition: Device.hpp:115
#define AK09916REG_CNTL3
Definition: ICM20948_mag.h:77
#define AK09916_I2C_ADDR
Definition: ICM20948_mag.h:45
void set_scale(float scale)
#define AK09916_16BIT_ADC
Definition: ICM20948_mag.h:58
float _last_temperature
Definition: icm20948.h:416
#define AK09916_RESET
Definition: ICM20948_mag.h:60
ICM20948 * _parent
Definition: ICM20948_mag.h:146
void modify_checked_reg(unsigned reg, uint8_t clearbits, uint8_t setbits)
Modify a checked register in the mpu.
Definition: icm20948.cpp:541
#define ICMREG_20948_I2C_SLV0_ADDR
Definition: icm20948.h:229
bool ak09916_read_adjustments()
#define AK09916REG_ASAX
Definition: ICM20948_mag.h:50
uint64_t perf_event_count(perf_counter_t handle)
Return current event_count.
Fundamental base class for all physical drivers (I2C, SPI).
Definition: Device.hpp:65
bool ak09916_check_id(uint8_t &id)
uint8_t st1
Definition: ICM20948_mag.h:95
#define BIT_I2C_READ_FLAG
Definition: icm20948.h:142
#define OK
Definition: uavcan_main.cpp:71
#define AK09916_DEVICE_ID
Definition: ICM20948_mag.h:46
void passthrough_read(uint8_t reg, uint8_t *buf, uint8_t size)
#define AK09916REG_CNTL2
Definition: ICM20948_mag.h:76
void set_passthrough(uint8_t reg, uint8_t size, uint8_t *out=NULL)
#define AK09916REG_CNTL1
Definition: ICM20948_mag.h:49
#define BIT_I2C_MST_P_NSR
Definition: icm20948.h:160
#define BIT_I2C_MST_RST
Definition: icm20948.h:149
void write_reg(unsigned reg, uint8_t value)
perf_counter_t _mag_overflows
Definition: ICM20948_mag.h:151
#define ICMREG_20948_I2C_SLV0_DO
Definition: icm20948.h:232
#define DRV_MAG_DEVTYPE_AK09916
Definition: drv_sensor.h:63
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
virtual int read(unsigned address, void *data, unsigned count)
Read directly from the device.
Definition: Device.hpp:103
Performance measuring tools.