PX4 Firmware
PX4 Autopilot Software http://px4.io
LSM303AGR.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2018 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 LSM303agr.cpp
36  * Driver for the ST LSM303AGR MEMS accelerometer / magnetometer connected via SPI.
37  * NOTE: currently only the mag is implemented
38  */
39 
40 #include "LSM303AGR.hpp"
41 
42 #include <px4_platform_common/px4_config.h>
43 #include <px4_platform_common/defines.h>
44 #include <ecl/geo/geo.h>
45 
46 /* SPI protocol address bits */
47 #define DIR_READ (1<<7)
48 #define DIR_WRITE (0<<7)
49 #define ADDR_INCREMENT (1<<6)
50 
51 /* Max measurement rate is 100Hz */
52 #define CONVERSION_INTERVAL (1000000 / 100) /* microseconds */
53 
54 static constexpr uint8_t LSM303AGR_WHO_AM_I_M = 0x40;
55 
56 /*
57  we set the timer interrupt to run a bit faster than the desired
58  sample rate and then throw away duplicates using the data ready bit.
59  This time reduction is enough to cope with worst case timing jitter
60  due to other timers
61  */
62 #define LSM303AGR_TIMER_REDUCTION 200
63 
64 LSM303AGR::LSM303AGR(int bus, const char *path, uint32_t device, enum Rotation rotation) :
65  SPI("LSM303AGR", path, bus, device, SPIDEV_MODE3, 8 * 1000 * 1000),
66  ScheduledWorkItem(MODULE_NAME, px4::device_bus_to_wq(get_device_id())),
67  _mag_sample_perf(perf_alloc(PC_ELAPSED, "LSM303AGR_mag_read")),
68  _bad_registers(perf_alloc(PC_COUNT, "LSM303AGR_bad_reg")),
69  _bad_values(perf_alloc(PC_COUNT, "LSM303AGR_bad_val")),
70  _rotation(rotation)
71 {
72  /* Prime _mag with parents devid. */
73  _device_id.devid = _device_id.devid;
74  _device_id.devid_s.devtype = DRV_MAG_DEVTYPE_LSM303AGR;
75 
76  _mag_scale.x_offset = 0.0f;
77  _mag_scale.x_scale = 1.0f;
78  _mag_scale.y_offset = 0.0f;
79  _mag_scale.y_scale = 1.0f;
80  _mag_scale.z_offset = 0.0f;
81  _mag_scale.z_scale = 1.0f;
82 }
83 
85 {
86  /* make sure we are truly inactive */
87  stop();
88 
89  if (_class_instance != -1) {
90  unregister_class_devname(MAG_BASE_DEVICE_PATH, _class_instance);
91  }
92 
93  /* delete the perf counter */
97 }
98 
99 int
101 {
102  int ret = PX4_OK;
103 
104  /* do SPI init (and probe) first */
105  if (SPI::init() != OK) {
106  PX4_ERR("SPI init failed");
107  return PX4_ERROR;
108  }
109 
110  _class_instance = register_class_devname(MAG_BASE_DEVICE_PATH);
111 
112  reset();
113 
114  self_test();
115 
116  reset();
117 
118  /* fill report structures */
119  measure();
120 
121  return ret;
122 }
123 
124 int
126 {
127  // Single mode
128  // Output data rate configuration: 100Hz
130 
131  // Enable low pass filter
133 
134  // Disable I2C
136 
137  return PX4_OK;
138 }
139 
140 bool
142 {
143  // Magnetometer self-test procedure (LSM303AGR DocID027765 Rev 5 page 25/68)
144 
145  uint8_t status_m = 0;
146 
147  write_reg(CFG_REG_A_M, 0x0C);
148  write_reg(CFG_REG_B_M, 0x02);
149  write_reg(CFG_REG_C_M, 0x10);
150 
151  // sleep 20ms
152  usleep(20000);
153 
154  uint16_t OUTX_NOST = 0;
155  uint16_t OUTY_NOST = 0;
156  uint16_t OUTZ_NOST = 0;
157 
158  // Check Zyxda 50 times and discard
159  // average x, y, z
160  for (int i = 0; i < 50; i++) {
161 
162  status_m = read_reg(STATUS_REG_M);
163 
164  OUTX_NOST += read_reg(OUTX_L_REG_M) + (read_reg(OUTX_H_REG_M) << 8);
165  OUTY_NOST += read_reg(OUTY_L_REG_M) + (read_reg(OUTY_H_REG_M) << 8);
166  OUTZ_NOST += read_reg(OUTZ_L_REG_M) + (read_reg(OUTZ_H_REG_M) << 8);
167  }
168 
169  // enable self-test
170  write_reg(CFG_REG_C_M, 0x12);
171 
172  // wait for 60ms
173  usleep(60000);
174 
175  // Check Zyxda
176  status_m = read_reg(STATUS_REG_M);
177 
178  // Read mag x, y, z to clear Zyxda bit
185 
186  uint16_t OUTX_ST = 0;
187  uint16_t OUTY_ST = 0;
188  uint16_t OUTZ_ST = 0;
189 
190  // Read the output registers after checking the Zyxda bit 50 times
191  // average x, y, z
192  for (int i = 0; i < 50; i++) {
193 
194  status_m = read_reg(STATUS_REG_M);
195 
196  OUTX_NOST += read_reg(OUTX_L_REG_M) + (read_reg(OUTX_H_REG_M) << 8);
197  OUTY_NOST += read_reg(OUTY_L_REG_M) + (read_reg(OUTY_H_REG_M) << 8);
198  OUTZ_NOST += read_reg(OUTZ_L_REG_M) + (read_reg(OUTZ_H_REG_M) << 8);
199  }
200 
201  const uint16_t abs_x = abs(OUTX_ST - OUTX_NOST);
202  const uint16_t abs_y = abs(OUTY_ST - OUTY_NOST);
203  const uint16_t abs_z = abs(OUTZ_ST - OUTZ_NOST);
204 
205  // TODO: proper ranges?
206  const bool x_valid = (abs_x > 0 && abs_x < UINT16_MAX);
207  const bool y_valid = (abs_y > 0 && abs_y < UINT16_MAX);
208  const bool z_valid = (abs_z > 0 && abs_z < UINT16_MAX);
209 
210  if (!x_valid || !y_valid || !z_valid) {
211 
212  PX4_ERR("self-test failed");
213 
214  PX4_INFO("STATUS_M: %X", status_m);
215  PX4_INFO("ABS(OUTX_ST - OUTX_NOST) = %d", abs_x);
216  PX4_INFO("ABS(OUTY_ST - OUTY_NOST) = %d", abs_y);
217  PX4_INFO("ABS(OUTZ_ST - OUTZ_NOST) = %d", abs_z);
218  }
219 
220  // disable self test
221  write_reg(CFG_REG_C_M, 0x10);
222 
223  // Idle mode
224  write_reg(CFG_REG_A_M, 0x03);
225 
226  return true;
227 }
228 
229 int
231 {
232  /* verify that the device is attached and functioning */
233  bool success = (read_reg(WHO_AM_I_M) == LSM303AGR_WHO_AM_I_M);
234 
235  if (success) {
236  return OK;
237  }
238 
239  return -EIO;
240 }
241 
242 int
243 LSM303AGR::ioctl(struct file *filp, int cmd, unsigned long arg)
244 {
245  switch (cmd) {
246  case SENSORIOCSPOLLRATE: {
247  switch (arg) {
248 
249  /* zero would be bad */
250  case 0:
251  return -EINVAL;
252 
253  /* set default polling rate */
255  /* do we need to start internal polling? */
256  bool want_start = (_measure_interval == 0);
257 
258  /* set interval for next measurement to minimum legal value */
260 
261  /* if we need to start the poll state machine, do it */
262  if (want_start) {
263  start();
264  }
265 
266  return OK;
267  }
268 
269  /* adjust to a legal polling interval in Hz */
270  default: {
271  /* do we need to start internal polling? */
272  bool want_start = (_measure_interval == 0);
273 
274  /* convert hz to tick interval via microseconds */
275  unsigned interval = (1000000 / arg);
276 
277  /* check against maximum rate */
278  if (interval < CONVERSION_INTERVAL) {
279  return -EINVAL;
280  }
281 
282  /* update interval for next measurement */
283  _measure_interval = interval;
284 
285  /* if we need to start the poll state machine, do it */
286  if (want_start) {
287  start();
288  }
289 
290  return OK;
291  }
292  }
293  }
294 
295  case SENSORIOCRESET:
296  return reset();
297 
298  case MAGIOCSSCALE:
299  /* set new scale factors */
300  memcpy(&_mag_scale, (struct mag_calibration_s *)arg, sizeof(_mag_scale));
301 
302  return 0;
303 
304  case MAGIOCGSCALE:
305  /* copy out scale factors */
306  memcpy((struct mag_calibration_s *)arg, &_mag_scale, sizeof(_mag_scale));
307  return 0;
308 
309  case MAGIOCGEXTERNAL:
310  return external();
311 
312  default:
313  /* give it to the superclass */
314  return CDev::ioctl(filp, cmd, arg);
315  }
316 }
317 
318 uint8_t
319 LSM303AGR::read_reg(unsigned reg)
320 {
321  uint8_t cmd[2];
322 
323  cmd[0] = reg | DIR_READ;
324  cmd[1] = 0;
325 
326  transfer(cmd, cmd, sizeof(cmd));
327 
328  return cmd[1];
329 }
330 
331 void
332 LSM303AGR::write_reg(unsigned reg, uint8_t value)
333 {
334  uint8_t cmd[2];
335 
336  cmd[0] = reg | DIR_WRITE;
337  cmd[1] = value;
338 
339  transfer(cmd, nullptr, sizeof(cmd));
340 }
341 
342 void
344 {
345  /* reset the report ring and state machine */
346  _collect_phase = false;
347 
348  /* schedule a cycle to start things */
349  ScheduleNow();
350 }
351 
352 void
354 {
355  if (_measure_interval > 0) {
356  /* ensure no new items are queued while we cancel this one */
357  _measure_interval = 0;
358  ScheduleClear();
359  }
360 }
361 
362 void
364 {
365  if (_measure_interval == 0) {
366  return;
367  }
368 
369  /* collection phase? */
370  if (_collect_phase) {
371 
372  /* perform collection */
373  if (OK != collect()) {
374  DEVICE_DEBUG("collection error");
375  /* restart the measurement state machine */
376  start();
377  return;
378  }
379 
380  /* next phase is measurement */
381  _collect_phase = false;
382 
383  /*
384  * Is there a collect->measure gap?
385  */
387 
388  /* schedule a fresh cycle call when we are ready to measure again */
389  ScheduleDelayed(_measure_interval - CONVERSION_INTERVAL);
390 
391  return;
392  }
393  }
394 
395  /* measurement phase */
396  measure();
397 
398  /* next phase is collection */
399  _collect_phase = true;
400 
401  if (_measure_interval > 0) {
402  /* schedule a fresh cycle call when the measurement is done */
403  ScheduleDelayed(CONVERSION_INTERVAL);
404  }
405 }
406 
407 void
409 {
410  /*
411  * Send the command to begin a measurement.
412  */
414 }
415 
416 int
418 {
419  const uint8_t status = read_reg(STATUS_REG_M);
420 
421  // only publish new data
422  if (status & STATUS_REG_M_Zyxda) {
423  /* start the performance counter */
425 
426  mag_report mag_report = {};
427  mag_report.timestamp = hrt_absolute_time();
428 
429  // switch to right hand coordinate system in place
430  mag_report.x_raw = read_reg(OUTX_L_REG_M) + (read_reg(OUTX_H_REG_M) << 8);
431  mag_report.y_raw = read_reg(OUTY_L_REG_M) + (read_reg(OUTY_H_REG_M) << 8);
432  mag_report.z_raw = -(read_reg(OUTZ_L_REG_M) + (read_reg(OUTZ_H_REG_M) << 8));
433 
434  float xraw_f = mag_report.x_raw;
435  float yraw_f = mag_report.y_raw;
436  float zraw_f = mag_report.z_raw;
437 
438  /* apply user specified rotation */
439  rotate_3f(_rotation, xraw_f, yraw_f, zraw_f);
440 
441  mag_report.x = ((xraw_f * _mag_range_scale) - _mag_scale.x_offset) * _mag_scale.x_scale;
442  mag_report.y = ((yraw_f * _mag_range_scale) - _mag_scale.y_offset) * _mag_scale.y_scale;
443  mag_report.z = ((zraw_f * _mag_range_scale) - _mag_scale.z_offset) * _mag_scale.z_scale;
444  mag_report.scaling = _mag_range_scale;
445  mag_report.error_count = perf_event_count(_bad_registers) + perf_event_count(_bad_values);
446 
447  /* remember the temperature. The datasheet isn't clear, but it
448  * seems to be a signed offset from 25 degrees C in units of 0.125C
449  */
450  //mag_report.temperature = 25 + (raw_mag_report.temperature * 0.125f);;
451  mag_report.device_id = _device_id.devid;
452  mag_report.is_external = external();
453 
454  if (!(_pub_blocked)) {
455 
456  if (_mag_topic != nullptr) {
457  /* publish it */
458  orb_publish(ORB_ID(sensor_mag), _mag_topic, &mag_report);
459 
460  } else {
461  _mag_topic = orb_advertise_multi(ORB_ID(sensor_mag), &mag_report, &_mag_orb_class_instance,
462  (mag_report.is_external) ? ORB_PRIO_HIGH : ORB_PRIO_MAX);
463 
464  if (_mag_topic == nullptr) {
465  DEVICE_DEBUG("ADVERT FAIL");
466  }
467  }
468  }
469 
470 
471  /* stop the perf counter */
473  }
474 
475  return OK;
476 }
477 
478 void
480 {
484 }
void write_reg(unsigned reg, uint8_t value)
Write a register in the LSM303AGR.
Definition: LSM303AGR.cpp:332
#define MAGIOCGSCALE
copy the mag scaling constants to the structure pointed to by (arg)
Definition: drv_mag.h:76
#define MAGIOCGEXTERNAL
determine if mag is external or onboard
Definition: drv_mag.h:88
virtual int ioctl(struct file *filp, int cmd, unsigned long arg)
Definition: LSM303AGR.cpp:243
static constexpr uint8_t CFG_REG_C_M_I2C_DIS
Definition: LSM303AGR.hpp:76
void measure()
Issue a measurement command.
Definition: LSM303AGR.cpp:408
static struct vehicle_status_s status
Definition: Commander.cpp:138
#define DIR_READ
Definition: LSM303AGR.cpp:47
measure the time elapsed performing an event
Definition: perf_counter.h:56
Definition of geo / math functions to perform geodesic calculations.
static constexpr uint8_t CFG_REG_A_M_ODR0
Definition: LSM303AGR.hpp:67
static constexpr uint8_t LSM303AGR_WHO_AM_I_M
Definition: LSM303AGR.cpp:54
void stop()
Stop automatic measurement.
Definition: LSM303AGR.cpp:353
virtual ~LSM303AGR()
Definition: LSM303AGR.cpp:84
uint8_t read_reg(unsigned reg)
Read a register from the LSM303AGR.
Definition: LSM303AGR.cpp:319
#define SENSOR_POLLRATE_DEFAULT
poll at driver normal rate
Definition: drv_sensor.h:136
__EXPORT void rotate_3f(enum Rotation rot, float &x, float &y, float &z)
rotate a 3 element float vector in-place
Definition: rotation.cpp:63
mag scaling factors; Vout = (Vin * Vscale) + Voffset
Definition: drv_mag.h:56
virtual int init()
Definition: LSM303AGR.cpp:100
static constexpr uint8_t OUTY_H_REG_M
Definition: LSM303AGR.hpp:87
enum Rotation _rotation
Definition: LSM303AGR.hpp:130
void Run() override
Perform a poll cycle; collect from the previous measurement and start a new one.
Definition: LSM303AGR.cpp:363
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
int _mag_orb_class_instance
Definition: LSM303AGR.hpp:133
void start()
Start automatic measurement.
Definition: LSM303AGR.cpp:343
static constexpr uint8_t CFG_REG_A_M
Definition: LSM303AGR.hpp:51
static constexpr uint8_t WHO_AM_I_M
Definition: LSM303AGR.hpp:49
perf_counter_t _bad_values
Definition: LSM303AGR.hpp:128
float x_offset
Definition: drv_mag.h:57
#define SENSORIOCSPOLLRATE
Set the driver polling rate to (arg) Hz, or one of the SENSOR_POLLRATE constants. ...
Definition: drv_sensor.h:134
static constexpr uint8_t CFG_REG_B_M_OFF_CANC
Definition: LSM303AGR.hpp:72
#define CONVERSION_INTERVAL
Definition: LSM303AGR.cpp:52
#define ORB_ID(_name)
Generates a pointer to the uORB metadata structure for a given topic.
Definition: uORB.h:87
int _class_instance
Definition: LSM303AGR.hpp:122
#define DRV_MAG_DEVTYPE_LSM303AGR
Definition: drv_sensor.h:113
#define MAGIOCSSCALE
set the mag scaling constants to the structure pointed to by (arg)
Definition: drv_mag.h:73
void perf_free(perf_counter_t handle)
Free a counter.
#define MAG_BASE_DEVICE_PATH
Definition: drv_mag.h:47
bool _collect_phase
Definition: LSM303AGR.hpp:111
void init()
Activates/configures the hardware registers.
static constexpr uint8_t CFG_REG_A_M_ODR1
Definition: LSM303AGR.hpp:66
#define perf_alloc(a, b)
Definition: px4io.h:59
Rotation
Enum for board and external compass rotations.
Definition: rotation.h:51
static constexpr uint8_t CFG_REG_C_M
Definition: LSM303AGR.hpp:75
float y_offset
Definition: drv_mag.h:59
unsigned _measure_interval
Definition: LSM303AGR.hpp:113
orb_advert_t _mag_topic
Definition: LSM303AGR.hpp:132
void perf_end(perf_counter_t handle)
End a performance event.
int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
Definition: uORB.cpp:70
static constexpr uint8_t OUTY_L_REG_M
Definition: LSM303AGR.hpp:86
float z_offset
Definition: drv_mag.h:61
static constexpr uint8_t OUTX_L_REG_M
Definition: LSM303AGR.hpp:84
mag_calibration_s _mag_scale
Definition: LSM303AGR.hpp:117
static constexpr uint8_t OUTZ_L_REG_M
Definition: LSM303AGR.hpp:88
int reset()
Reset chip.
Definition: LSM303AGR.cpp:125
static constexpr uint8_t OUTZ_H_REG_M
Definition: LSM303AGR.hpp:89
bool self_test()
Definition: LSM303AGR.cpp:141
struct @83::@85::@87 file
perf_counter_t _mag_sample_perf
Definition: LSM303AGR.hpp:126
void perf_print_counter(perf_counter_t handle)
Print one performance counter to stdout.
#define SENSORIOCRESET
Reset the sensor to its default configuration.
Definition: drv_sensor.h:141
uint64_t perf_event_count(perf_counter_t handle)
Return current event_count.
LSM303AGR(int bus, const char *path, uint32_t device, enum Rotation rotation)
Definition: LSM303AGR.cpp:64
Definition: bst.cpp:62
void print_info()
Diagnostics - print some basic information about the driver.
Definition: LSM303AGR.cpp:479
static constexpr uint8_t STATUS_REG_M_Zyxda
Definition: LSM303AGR.hpp:81
#define OK
Definition: uavcan_main.cpp:71
int collect()
Collect the result of the most recent measurement.
Definition: LSM303AGR.cpp:417
virtual int probe()
Definition: LSM303AGR.cpp:230
static constexpr uint8_t STATUS_REG_M
Definition: LSM303AGR.hpp:80
Dual< Scalar, N > abs(const Dual< Scalar, N > &a)
Definition: Dual.hpp:196
static constexpr uint8_t CFG_REG_B_M
Definition: LSM303AGR.hpp:71
orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance, int priority)
Definition: uORB.cpp:53
static constexpr uint8_t CFG_REG_B_M_OFF_LPF
Definition: LSM303AGR.hpp:73
void perf_begin(perf_counter_t handle)
Begin a performance event.
perf_counter_t _bad_registers
Definition: LSM303AGR.hpp:127
#define DEVICE_DEBUG(FMT,...)
Definition: Device.hpp:52
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
static constexpr uint8_t OUTX_H_REG_M
Definition: LSM303AGR.hpp:85
static constexpr uint8_t CFG_REG_A_M_MD0
Definition: LSM303AGR.hpp:69
static constexpr float _mag_range_scale
Definition: LSM303AGR.hpp:119
#define DIR_WRITE
Definition: LSM303AGR.cpp:48
#define mag_report
Definition: drv_mag.h:53