PX4 Firmware
PX4 Autopilot Software http://px4.io
FXOS8701CQ.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2017-2019 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 fxos8701cq.cpp
36  * Driver for the NXP FXOS8701CQ 6-axis sensor with integrated linear accelerometer and
37  * magnetometer connected via SPI.
38  */
39 
40 #include "FXOS8701CQ.hpp"
41 
42 using namespace time_literals;
43 
44 /*
45  list of registers that will be checked in check_registers(). Note
46  that ADDR_WHO_AM_I must be first in the list.
47  */
48 const uint8_t FXOS8701CQ::_checked_registers[FXOS8701C_NUM_CHECKED_REGISTERS] = {
54 };
55 
56 FXOS8701CQ::FXOS8701CQ(int bus, uint32_t device, enum Rotation rotation) :
57  SPI("FXOS8701CQ", nullptr, bus, device, SPIDEV_MODE0, 1 * 1000 * 1000),
58  ScheduledWorkItem(MODULE_NAME, px4::device_bus_to_wq(get_device_id())),
59  _px4_accel(get_device_id(), ORB_PRIO_LOW, rotation),
60 #if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
61  _px4_mag(get_device_id(), ORB_PRIO_LOW, rotation),
62  _mag_sample_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": mag read")),
63 #endif
64  _accel_sample_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": acc read")),
65  _bad_registers(perf_alloc(PC_COUNT, MODULE_NAME": bad reg")),
66  _accel_duplicates(perf_alloc(PC_COUNT, MODULE_NAME": acc dupe"))
67 {
68  set_device_type(DRV_ACC_DEVTYPE_FXOS8701C);
69 
71 
72 #if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
74  _px4_mag.set_scale(0.001f);
75 #endif
76 }
77 
79 {
80  // make sure we are truly inactive
81  stop();
82 
83 #if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
85 #endif
86 
87  // delete the perf counter
91 }
92 
93 int
95 {
96  // do SPI init (and probe) first
97  int ret = SPI::init();
98 
99  if (ret != OK) {
100  PX4_ERR("SPI init failed");
101  return ret;
102  }
103 
104  reset();
105 
106  start();
107 
108  return PX4_OK;
109 }
110 
111 void
113 {
114  // enable accel set it To Standby
117 
118  // Use hybird mode to read Accel and Mag
120 
121  // Use the hybird auto increment mode to read all the data at the same time
123 
126 
127  // enable set it To Standby mode at 800 Hz which becomes 400 Hz due to hybird mode
129 }
130 
131 int
133 {
134  // verify that the device is attached and functioning
135  uint8_t whoami = read_reg(FXOS8701CQ_WHOAMI);
136  bool success = (whoami == FXOS8700CQ_WHOAMI_VAL) || (whoami == FXOS8701CQ_WHOAMI_VAL);
137 
138  if (success) {
139  _checked_values[0] = whoami;
140  return OK;
141  }
142 
143  return -EIO;
144 }
145 
146 uint8_t
147 FXOS8701CQ::read_reg(unsigned reg)
148 {
149  uint8_t cmd[3];
150 
151  cmd[0] = DIR_READ(reg);
152  cmd[1] = ADDR_7(reg);
153  cmd[2] = 0;
154 
155  transfer(cmd, cmd, sizeof(cmd));
156 
157  return cmd[2];
158 }
159 
160 void
161 FXOS8701CQ::write_reg(unsigned reg, uint8_t value)
162 {
163  uint8_t cmd[3];
164 
165  cmd[0] = DIR_WRITE(reg);
166  cmd[1] = ADDR_7(reg);
167  cmd[2] = value;
168 
169  transfer(cmd, nullptr, sizeof(cmd));
170 }
171 
172 void
173 FXOS8701CQ::write_checked_reg(unsigned reg, uint8_t value)
174 {
175  write_reg(reg, value);
176 
177  for (uint8_t i = 0; i < FXOS8701C_NUM_CHECKED_REGISTERS; i++) {
178  if (reg == _checked_registers[i]) {
179  _checked_values[i] = value;
180  }
181  }
182 }
183 
184 void
185 FXOS8701CQ::modify_reg(unsigned reg, uint8_t clearbits, uint8_t setbits)
186 {
187  uint8_t val = read_reg(reg);
188  val &= ~clearbits;
189  val |= setbits;
190  write_checked_reg(reg, val);
191 }
192 
193 int
195 {
196  uint8_t setbits = 0;
197  float lsb_per_g;
198 
199  if (max_g == 0 || max_g > 8) {
200  max_g = 8;
201  }
202 
203  if (max_g > 4) { // 8g
204  setbits = XYZ_DATA_CFG_FS_8G;
205  lsb_per_g = 1024;
206  //max_accel_g = 8;
207 
208  } else if (max_g > 2) { // 4g
209  setbits = XYZ_DATA_CFG_FS_4G;
210  lsb_per_g = 2048;
211  //max_accel_g = 4;
212 
213  } else { // 2g
214  setbits = XYZ_DATA_CFG_FS_2G;
215  lsb_per_g = 4096;
216  //max_accel_g = 2;
217  }
218 
219  float accel_range_scale = (CONSTANTS_ONE_G / lsb_per_g);
220 
222 
223  _px4_accel.set_scale(accel_range_scale);
224 
225  return OK;
226 }
227 
228 #if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
229 int
231 {
232  // mag_range_ga = 12;
233  float mag_range_scale = 0.001f;
234 
235  _px4_mag.set_scale(mag_range_scale);
236 
237  return OK;
238 }
239 #endif
240 
241 int
243 {
244  uint8_t setbits = 0;
245 
246  // The selected ODR is reduced by a factor of two when the device is operated in hybrid mode.
247  uint8_t active = read_reg(FXOS8701CQ_CTRL_REG1) & CTRL_REG1_ACTIVE;
248 
249  if (frequency == 0) {
250  frequency = FXOS8701C_ACCEL_DEFAULT_RATE;
251  }
252 
253  if (frequency <= 25) {
254  setbits = CTRL_REG1_DR(4); // Use 50 as it is 50 / 2
255  _accel_samplerate = 25;
256 
257  } else if (frequency <= 50) {
258  setbits = CTRL_REG1_DR(3); // Use 100 as it is 100 / 2
259  _accel_samplerate = 50;
260 
261  } else if (frequency <= 100) {
262  setbits = CTRL_REG1_DR(2); // Use 200 as it is 200 / 2
263  _accel_samplerate = 100;
264 
265  } else if (frequency <= 200) {
266  setbits = CTRL_REG1_DR(1); // Use 400 as it is 400 / 2;
267  _accel_samplerate = 200;
268 
269  } else if (frequency <= 400) {
270  setbits = CTRL_REG1_DR(0); // Use 800 as it is 800 / 2;
271  _accel_samplerate = 400;
272 
273  } else {
274  return -EINVAL;
275  }
276 
279  modify_reg(FXOS8701CQ_CTRL_REG1, 0, active);
280 
281  return OK;
282 }
283 
284 void
286 {
287  // make sure we are stopped first
288  stop();
289 
290  // start polling at the specified rate
291  ScheduleOnInterval(1000000 / (FXOS8701C_ACCEL_DEFAULT_RATE) - FXOS8701C_TIMER_REDUCTION, 10000);
292 }
293 
294 void
296 {
297  ScheduleClear();
298 }
299 
300 void
302 {
303  uint8_t v;
304 
305  if ((v = read_reg(_checked_registers[_checked_next])) != _checked_values[_checked_next]) {
306  /*
307  if we get the wrong value then we know the SPI bus
308  or sensor is very sick. We set _register_wait to 20
309  and wait until we have seen 20 good values in a row
310  before we consider the sensor to be OK again.
311  */
313 
314  /*
315  try to fix the bad register value. We only try to
316  fix one per loop to prevent a bad sensor hogging the
317  bus. We skip zero as that is the WHO_AM_I, which
318  is not writeable
319  */
320  if (_checked_next != 0) {
321  write_reg(_checked_registers[_checked_next], _checked_values[_checked_next]);
322  }
323 
324  _register_wait = 20;
325  }
326 
327  _checked_next = (_checked_next + 1) % FXOS8701C_NUM_CHECKED_REGISTERS;
328 }
329 
330 void
332 {
333  // start the performance counter
335 
336  // status register and data as read back from the device
337 #pragma pack(push, 1)
338  struct {
339  uint8_t cmd[2];
340  uint8_t status;
341  int16_t x;
342  int16_t y;
343  int16_t z;
344  int16_t mx;
345  int16_t my;
346  int16_t mz;
347  } raw_accel_mag_report{};
348 #pragma pack(pop)
349 
350  check_registers();
351 
352  if (_register_wait != 0) {
353  // we are waiting for some good transfers before using
354  // the sensor again.
355  _register_wait--;
357  return;
358  }
359 
360  /* fetch data from the sensor */
361  raw_accel_mag_report.cmd[0] = DIR_READ(FXOS8701CQ_DR_STATUS);
362  raw_accel_mag_report.cmd[1] = ADDR_7(FXOS8701CQ_DR_STATUS);
363  const hrt_abstime timestamp_sample = hrt_absolute_time();
364  transfer((uint8_t *)&raw_accel_mag_report, (uint8_t *)&raw_accel_mag_report, sizeof(raw_accel_mag_report));
365 
366  if (!(raw_accel_mag_report.status & DR_STATUS_ZYXDR)) {
369  return;
370  }
371 
372  /*
373  * Eight-bit 2’s complement sensor temperature value with 0.96 °C/LSB sensitivity.
374  * Temperature data is only valid between –40 °C and 125 °C. The temperature sensor
375  * output is only valid when M_CTRL_REG1[m_hms] > 0b00. Please note that the
376  * temperature sensor is uncalibrated and its output for a given temperature will vary from
377  * one device to the next
378  */
379  float temperature = (read_reg(FXOS8701CQ_TEMP)) * 0.96f;
380 
381  _px4_accel.set_temperature(temperature);
382 
383  // report the error count as the sum of the number of bad
384  // register reads and bad values. This allows the higher level
385  // code to decide if it should use this sensor based on
386  // whether it has had failures
388 
389  int16_t x = swap16RightJustify14(raw_accel_mag_report.x);
390  int16_t y = swap16RightJustify14(raw_accel_mag_report.y);
391  int16_t z = swap16RightJustify14(raw_accel_mag_report.z);
392  _px4_accel.update(timestamp_sample, x, y, z);
393 
394 #if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
395 
396  if (hrt_elapsed_time(&_mag_last_measure) >= 10_ms) {
397  int16_t mag_x = swap16(raw_accel_mag_report.mx);
398  int16_t mag_y = swap16(raw_accel_mag_report.my);
399  int16_t mag_z = swap16(raw_accel_mag_report.mz);
400  _px4_mag.update(timestamp_sample, mag_x, mag_y, mag_z);
401  }
402 
403 #endif
404 
405  // stop the perf counter
407 }
408 
409 void
411 {
413 
414 #if !defined(BOARD_HAS_NOISY_FXOS8700_MAG)
416 #endif
419 
420  ::printf("checked_next: %u\n", _checked_next);
421 
422  for (uint8_t i = 0; i < FXOS8701C_NUM_CHECKED_REGISTERS; i++) {
423  uint8_t v = read_reg(_checked_registers[i]);
424 
425  if (v != _checked_values[i]) {
426  ::printf("reg %02x:%02x should be %02x\n",
427  (unsigned)_checked_registers[i],
428  (unsigned)v,
429  (unsigned)_checked_values[i]);
430  }
431  }
432 }
433 
434 void
436 {
437  const struct {
438  uint8_t reg;
439  const char *name;
440  } regmap[] = {
452  };
453 
454  for (uint8_t i = 0; i < sizeof(regmap) / sizeof(regmap[0]); i++) {
455  printf("0x%02x %s\n", read_reg(regmap[i].reg), regmap[i].name);
456  }
457 }
458 
459 void
461 {
462  // trigger an error
464 }
#define XYZ_DATA_CFG_FS_2G
Definition: FXOS8701CQ.hpp:67
uint8_t read_reg(unsigned reg)
Read a register from the FXOS8701C.
Definition: FXOS8701CQ.cpp:147
#define swap16RightJustify14(w)
Definition: FXOS8701CQ.hpp:57
void write_reg(unsigned reg, uint8_t value)
Write a register in the FXOS8701C.
Definition: FXOS8701CQ.cpp:161
#define CTRL_REG2_AUTO_INC
Definition: FXOS8701CQ.hpp:81
virtual int init()
Definition: FXOS8701CQ.cpp:94
static struct vehicle_status_s status
Definition: Commander.cpp:138
#define DRV_ACC_DEVTYPE_FXOS8701C
Definition: drv_sensor.h:103
#define XYZ_DATA_CFG_FS_4G
Definition: FXOS8701CQ.hpp:68
measure the time elapsed performing an event
Definition: perf_counter.h:56
perf_counter_t _accel_duplicates
Definition: FXOS8701CQ.hpp:218
void Run() override
Definition: FXOS8701CQ.cpp:331
void set_device_type(uint8_t devtype)
#define FXOS8701CQ_CTRL_REG2
Definition: FXOS8701CQ.hpp:80
void stop()
Definition: FXOS8701CQ.cpp:295
#define FXOS8701CQ_M_CTRL_REG1
Definition: FXOS8701CQ.hpp:87
void update(hrt_abstime timestamp, float x, float y, float z)
#define FXOS8701CQ_WHOAMI
Definition: FXOS8701CQ.hpp:71
PX4Magnetometer _px4_mag
Definition: FXOS8701CQ.hpp:209
#define FXOS8701CQ_M_OUT_X_MSB
Definition: FXOS8701CQ.hpp:85
#define CTRL_REG1_DR_MASK
Definition: FXAS21002C.cpp:130
void print_registers()
Definition: FXOS8701CQ.cpp:435
void start()
Definition: FXOS8701CQ.cpp:285
#define FXOS8701C_ACCEL_DEFAULT_RANGE_G
Definition: FXOS8701CQ.hpp:103
static constexpr int FXOS8701C_NUM_CHECKED_REGISTERS
Definition: FXOS8701CQ.hpp:225
#define FXOS8701CQ_DR_STATUS
Definition: FXOS8701CQ.hpp:59
#define DEF_REG(r)
Definition: FXAS21002C.cpp:157
uint8_t _checked_next
Definition: FXOS8701CQ.hpp:228
Namespace encapsulating all device framework classes, functions and data.
Definition: CDev.cpp:47
count the number of times an event occurs
Definition: perf_counter.h:55
virtual int probe()
Definition: FXOS8701CQ.cpp:132
static constexpr float CONSTANTS_ONE_G
Definition: geo.h:51
void set_error_count(uint64_t error_count)
#define M_CTRL_REG1_OS(n)
Definition: FXOS8701CQ.hpp:95
perf_counter_t _mag_sample_perf
Definition: FXOS8701CQ.hpp:211
FXOS8701CQ(int bus, uint32_t device, enum Rotation rotation)
Definition: FXOS8701CQ.cpp:56
#define FXOS8701CQ_TEMP
Definition: FXOS8701CQ.hpp:86
#define DIR_READ
Definition: bmp388_spi.cpp:46
void write_checked_reg(unsigned reg, uint8_t value)
Write a register in the FXOS8701C, updating _checked_values.
Definition: FXOS8701CQ.cpp:173
void modify_reg(unsigned reg, uint8_t clearbits, uint8_t setbits)
Modify a register in the FXOS8701C.
Definition: FXOS8701CQ.cpp:185
perf_counter_t _accel_sample_perf
Definition: FXOS8701CQ.hpp:216
void perf_count(perf_counter_t handle)
Count a performance event.
#define FXOS8701CQ_CTRL_REG1
Definition: FXOS8701CQ.hpp:75
#define DR_STATUS_ZYXDR
Definition: FXAS21002C.cpp:54
void perf_free(perf_counter_t handle)
Free a counter.
void init()
Activates/configures the hardware registers.
void set_temperature(float temperature)
#define perf_alloc(a, b)
Definition: px4io.h:59
void update(hrt_abstime timestamp, int16_t x, int16_t y, int16_t z)
#define CTRL_REG1_DR(n)
Definition: FXOS8701CQ.hpp:79
perf_counter_t _bad_registers
Definition: FXOS8701CQ.hpp:217
Rotation
Enum for board and external compass rotations.
Definition: rotation.h:51
#define FXOS8701CQ_WHOAMI_VAL
Definition: FXOS8701CQ.hpp:73
#define FXOS8700CQ_WHOAMI_VAL
Definition: FXOS8701CQ.hpp:72
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
static hrt_abstime hrt_elapsed_time(const hrt_abstime *then)
Compute the delta between a timestamp taken in the past and now.
Definition: drv_hrt.h:102
#define ADDR_7(a)
Definition: FXOS8701CQ.hpp:55
hrt_abstime _mag_last_measure
Definition: FXOS8701CQ.hpp:210
void perf_end(perf_counter_t handle)
End a performance event.
#define FXOS8701C_ACCEL_DEFAULT_RATE
Definition: FXOS8701CQ.hpp:104
void reset()
Definition: FXOS8701CQ.cpp:112
#define FXOS8701CQ_OUT_X_MSB
Definition: FXOS8701CQ.hpp:62
void check_registers()
check key registers for correct values
Definition: FXOS8701CQ.cpp:301
#define FXOS8701C_TIMER_REDUCTION
Definition: FXOS8701CQ.hpp:112
Driver for the NXP FXOS8701CQ 6-axis sensor with integrated linear accelerometer and magnetometer con...
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
unsigned _accel_samplerate
Definition: FXOS8701CQ.hpp:214
void set_scale(float scale)
void set_scale(float scale)
#define DIR_WRITE
Definition: bmp388_spi.cpp:47
const char * name
Definition: tests_main.c:58
#define swap16(w)
Definition: FXAS21002C.cpp:39
#define XYZ_DATA_CFG_FS_MASK
Definition: FXOS8701CQ.hpp:66
#define FXOS8701CQ_M_CTRL_REG3
Definition: FXOS8701CQ.hpp:98
uint8_t _checked_values[FXOS8701C_NUM_CHECKED_REGISTERS]
Definition: FXOS8701CQ.hpp:227
#define FXOS8701CQ_M_CTRL_REG2
Definition: FXOS8701CQ.hpp:97
#define M_CTRL_REG1_HMS_AM
Definition: FXOS8701CQ.hpp:92
void perf_print_counter(perf_counter_t handle)
Print one performance counter to stdout.
uint64_t perf_event_count(perf_counter_t handle)
Return current event_count.
#define CTRL_REG1_ACTIVE
Definition: FXAS21002C.cpp:139
Definition: bst.cpp:62
int accel_set_samplerate(unsigned frequency)
Set the FXOS8701C internal accel and mag sampling frequency.
Definition: FXOS8701CQ.cpp:242
uint8_t _register_wait
Definition: FXOS8701CQ.hpp:220
#define OK
Definition: uavcan_main.cpp:71
#define XYZ_DATA_CFG_FS_8G
Definition: FXOS8701CQ.hpp:69
virtual ~FXOS8701CQ()
Definition: FXOS8701CQ.cpp:78
void set_device_type(uint8_t devtype)
static const uint8_t _checked_registers[FXOS8701C_NUM_CHECKED_REGISTERS]
Definition: FXOS8701CQ.hpp:226
int accel_set_range(unsigned max_g)
Set the FXOS8701C accel measurement range.
Definition: FXOS8701CQ.cpp:194
PX4Accelerometer _px4_accel
Definition: FXOS8701CQ.hpp:206
void perf_begin(perf_counter_t handle)
Begin a performance event.
#define FXOS8701CQ_XYZ_DATA_CFG
Definition: FXOS8701CQ.hpp:64
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
void test_error()
Definition: FXOS8701CQ.cpp:460
int mag_set_range(unsigned max_g)
Set the FXOS8701C mag measurement range.
Definition: FXOS8701CQ.cpp:230
#define FXOS8701CQ_M_DR_STATUS
Definition: FXOS8701CQ.hpp:83
void print_info()
Definition: FXOS8701CQ.cpp:410