PX4 Firmware
PX4 Autopilot Software http://px4.io
ak09916.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 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  * Driver for the standalone AK09916 magnetometer.
36  */
37 
38 #include <px4_platform_common/px4_config.h>
39 #include <px4_platform_common/log.h>
40 #include <px4_platform_common/time.h>
41 #include <lib/perf/perf_counter.h>
42 #include <drivers/drv_hrt.h>
44 #include <px4_platform_common/getopt.h>
45 
46 #include "ak09916.hpp"
47 
48 
49 extern "C" { __EXPORT int ak09916_main(int argc, char *argv[]); }
50 
51 
52 namespace ak09916
53 {
54 
57 
58 void start(bool, enum Rotation);
59 void info(bool);
60 void usage();
61 
62 void start(bool external_bus, enum Rotation rotation)
63 {
64  AK09916 **g_dev_ptr = (external_bus ? &g_dev_ext : &g_dev_int);
65  const char *path = (external_bus ? AK09916_DEVICE_PATH_MAG_EXT : AK09916_DEVICE_PATH_MAG);
66 
67  if (*g_dev_ptr != nullptr) {
68  PX4_ERR("already started");
69  exit(0);
70  }
71 
72  if (external_bus) {
73 #if defined(PX4_I2C_BUS_EXPANSION)
74  *g_dev_ptr = new AK09916(PX4_I2C_BUS_EXPANSION, path, rotation);
75 #else
76  PX4_ERR("External I2C not available");
77  exit(0);
78 #endif
79 
80  } else {
81  PX4_ERR("Internal I2C not available");
82  exit(0);
83  }
84 
85  if (*g_dev_ptr == nullptr || (OK != (*g_dev_ptr)->init())) {
86  PX4_ERR("driver start failed");
87  delete (*g_dev_ptr);
88  *g_dev_ptr = nullptr;
89  exit(1);
90  }
91 
92  exit(0);
93 }
94 
95 void
96 stop(bool external_bus)
97 {
98  AK09916 **g_dev_ptr = external_bus ? &g_dev_ext : &g_dev_int;
99 
100  if (*g_dev_ptr == nullptr) {
101  PX4_ERR("driver not running");
102  exit(1);
103  }
104 
105  (*g_dev_ptr)->stop();
106 
107  exit(0);
108 }
109 
110 void
111 info(bool external_bus)
112 {
113  AK09916 **g_dev_ptr = external_bus ? &g_dev_ext : &g_dev_int;
114 
115  if (*g_dev_ptr == nullptr) {
116  PX4_ERR("driver not running");
117  exit(1);
118  }
119 
120  printf("state @ %p\n", *g_dev_ptr);
121  (*g_dev_ptr)->print_info();
122 
123  exit(0);
124 }
125 
126 void
128 {
129  PX4_INFO("missing command: try 'start', 'info', stop'");
130  PX4_INFO("options:");
131  PX4_INFO(" -X (external bus)");
132  PX4_INFO(" -R (rotation)");
133 }
134 
135 } // namespace ak09916
136 
137 AK09916::AK09916(int bus, const char *path, enum Rotation rotation) :
138  I2C("AK09916", path, bus, AK09916_I2C_ADDR, 400000),
139  ScheduledWorkItem(MODULE_NAME, px4::device_bus_to_wq(get_device_id())),
140  _px4_mag(get_device_id(), ORB_PRIO_MAX, rotation),
141  _mag_reads(perf_alloc(PC_COUNT, "ak09916_mag_reads")),
142  _mag_errors(perf_alloc(PC_COUNT, "ak09916_mag_errors")),
143  _mag_overruns(perf_alloc(PC_COUNT, "ak09916_mag_overruns")),
144  _mag_overflows(perf_alloc(PC_COUNT, "ak09916_mag_overflows"))
145 {
148 }
149 
151 {
156 }
157 
158 int
160 {
161  int ret = I2C::init();
162 
163  if (ret != OK) {
164  PX4_WARN("AK09916 mag init failed");
165  return ret;
166  }
167 
168  ret = reset();
169 
170  if (ret != PX4_OK) {
171  return ret;
172  }
173 
174  start();
175 
176  return PX4_OK;
177 }
178 
179 void
181 {
182  if (!is_ready()) {
183  return;
184  }
185 
186  measure();
187 }
188 
189 bool
191 {
192  uint8_t st1;
193  const int ret = transfer(&AK09916REG_ST1, sizeof(AK09916REG_ST1), &st1, sizeof(st1));
194 
195  if (ret != OK) {
196  return false;
197  }
198 
199  // Monitor if data overrun flag is ever set.
200  if (st1 & AK09916_ST1_DOR) {
202  }
203 
204  return (st1 & AK09916_ST1_DRDY);
205 }
206 
207 void
209 {
210  ak09916_regs regs;
211 
212  const hrt_abstime now = hrt_absolute_time();
213 
214  const int ret = transfer(&AK09916REG_HXL, sizeof(AK09916REG_HXL),
215  reinterpret_cast<uint8_t *>(&regs), sizeof(regs));
216 
217  if (ret != OK) {
219  return;
220  }
221 
222  // Monitor if magnetic sensor overflow flag is set.
223  if (regs.st2 & AK09916_ST2_HOFL) {
225  }
226 
227  _px4_mag.set_external(external());
228  _px4_mag.update(now, regs.x, regs.y, regs.z);
229 }
230 
231 void
233 {
238 }
239 
240 uint8_t
241 AK09916::read_reg(uint8_t reg)
242 {
243  const uint8_t cmd = reg;
244  uint8_t ret{};
245 
246  transfer(&cmd, 1, &ret, 1);
247 
248  return ret;
249 }
250 
251 bool
253 {
254  const uint8_t deviceid = read_reg(AK09916REG_WIA);
255 
256  return (AK09916_DEVICE_ID_A == deviceid);
257 }
258 
259 void
260 AK09916::write_reg(uint8_t reg, uint8_t value)
261 {
262  const uint8_t cmd[2] = { reg, value};
263  transfer(cmd, 2, nullptr, 0);
264 }
265 
266 int
268 {
269  int rv = probe();
270 
271  if (rv == OK) {
272  // Now reset the mag.
274 
275  // Then re-initialize the bus/mag.
276  rv = setup();
277  }
278 
279  return rv;
280 }
281 
282 int
284 {
285  int retries = 10;
286 
287  do {
289 
290  if (check_id()) {
291  return OK;
292  }
293 
294  retries--;
295  } while (retries > 0);
296 
297  return PX4_ERROR;
298 }
299 
300 int
302 {
304 
305  return OK;
306 }
307 
308 void
310 {
312 
313  ScheduleNow();
314 }
315 
316 void
318 {
319  // Ensure no new items are queued while we cancel this one.
320  _cycle_interval = 0;
321 
322  ScheduleClear();
323 }
324 
325 void
327 {
328  if (_cycle_interval == 0) {
329  return;
330  }
331 
332  try_measure();
333 
334  if (_cycle_interval > 0) {
335  ScheduleDelayed(_cycle_interval);
336  }
337 }
338 
339 int
340 ak09916_main(int argc, char *argv[])
341 {
342  int myoptind = 1;
343  int ch;
344  const char *myoptarg = nullptr;
345  bool external_bus = false;
346  enum Rotation rotation = ROTATION_NONE;
347 
348  while ((ch = px4_getopt(argc, argv, "XR:", &myoptind, &myoptarg)) != EOF) {
349  switch (ch) {
350  case 'X':
351  external_bus = true;
352  break;
353 
354  case 'R':
355  rotation = (enum Rotation)atoi(myoptarg);
356  break;
357 
358  default:
359  ak09916::usage();
360  return 0;
361  }
362  }
363 
364  if (myoptind >= argc) {
365  ak09916::usage();
366  return -1;
367  }
368 
369  const char *verb = argv[myoptind];
370 
371  if (!strcmp(verb, "start")) {
372  ak09916::start(external_bus, rotation);
373  }
374 
375  if (!strcmp(verb, "stop")) {
376  ak09916::stop(external_bus);
377  }
378 
379  if (!strcmp(verb, "info")) {
380  ak09916::info(external_bus);
381  }
382 
383  ak09916::usage();
384  return -1;
385 }
__EXPORT int ak09916_main(int argc, char *argv[])
Driver for the standalone AK09916 magnetometer.
Definition: ak09916.cpp:340
int reset()
Definition: ak09916.cpp:267
void print_info()
Definition: ak09916.cpp:232
void try_measure()
Definition: ak09916.cpp:180
static constexpr unsigned AK09916_CONVERSION_INTERVAL_us
Definition: ak09916.hpp:70
#define AK09916_ST1_DOR
Definition: ICM20948_mag.h:89
void set_device_type(uint8_t devtype)
void set_error_count(uint64_t error_count)
static constexpr uint8_t AK09916_ST2_HOFL
Definition: ak09916.hpp:67
uint8_t read_reg(uint8_t reg)
Definition: ak09916.cpp:241
Definition: I2C.hpp:51
void usage()
Prints info about the driver argument usage.
Definition: ak09916.cpp:127
void info(bool)
Definition: ak09916.cpp:111
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
void measure()
Definition: ak09916.cpp:208
void write_reg(uint8_t reg, uint8_t value)
Definition: ak09916.cpp:260
Vector rotation library.
High-resolution timer with callouts and timekeeping.
void stop(bool external_bus)
Definition: ak09916.cpp:96
#define AK09916_DEVICE_ID_A
Definition: ICM20948_mag.h:65
void set_external(bool external)
bool is_ready()
Definition: ak09916.cpp:190
#define AK09916REG_WIA
Definition: ICM20948_mag.h:48
AK09916(int bus, const char *path, enum Rotation rotation)
Definition: ak09916.cpp:137
bool check_id()
Definition: ak09916.cpp:252
AK09916 * g_dev_int
Definition: ak09916.cpp:56
perf_counter_t _mag_reads
Definition: ak09916.hpp:115
perf_counter_t _mag_overruns
Definition: ak09916.hpp:117
void perf_count(perf_counter_t handle)
Count a performance event.
AK09916 * g_dev_ext
Definition: ak09916.cpp:55
void perf_free(perf_counter_t handle)
Free a counter.
void init()
Activates/configures the hardware registers.
#define AK09916_CNTL2_CONTINOUS_MODE_100HZ
Definition: ICM20948_mag.h:85
#define AK09916REG_HXL
Definition: ICM20948_mag.h:68
#define perf_alloc(a, b)
Definition: px4io.h:59
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
perf_counter_t _mag_errors
Definition: ak09916.hpp:116
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
#define AK09916REG_CNTL3
Definition: ICM20948_mag.h:77
#define AK09916_I2C_ADDR
Definition: ICM20948_mag.h:45
int setup()
Definition: ak09916.cpp:301
void set_scale(float scale)
#define AK09916_RESET
Definition: ICM20948_mag.h:60
void start(bool, enum Rotation)
Definition: ak09916.cpp:62
uint32_t _cycle_interval
Definition: ak09916.hpp:113
perf_counter_t _mag_overflows
Definition: ak09916.hpp:118
virtual int init() override
Definition: ak09916.cpp:159
~AK09916()
Definition: ak09916.cpp:150
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.
Definition: bst.cpp:62
void start()
Definition: ak09916.cpp:309
#define OK
Definition: uavcan_main.cpp:71
static constexpr auto AK09916_DEVICE_PATH_MAG_EXT
Definition: ak09916.hpp:46
#define AK09916REG_CNTL2
Definition: ICM20948_mag.h:76
void stop()
Definition: ak09916.cpp:317
void Run()
Definition: ak09916.cpp:326
int probe()
Definition: ak09916.cpp:283
#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).
PX4Magnetometer _px4_mag
Definition: ak09916.hpp:111
static constexpr float AK09916_MAG_RANGE_GA
Definition: ak09916.hpp:49
Performance measuring tools.
static constexpr auto AK09916_DEVICE_PATH_MAG
Definition: ak09916.hpp:45