dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_lis3mdl.c
Go to the documentation of this file.
1 
11 /*
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, see <http://www.gnu.org/licenses/>
24  *
25  * Additional note on redistribution: The copyright and license notices above
26  * must be maintained in each individual source file that is a derivative work
27  * of this source file; otherwise redistribution is prohibited.
28  */
29 
30 #include "openpilot.h"
31 #include "pios.h"
32 
33 #if defined(PIOS_INCLUDE_LIS3MDL)
34 
35 #include "pios_semaphore.h"
36 #include "pios_thread.h"
37 #include "pios_queue.h"
38 #include "physical_constants.h"
39 #include "taskmonitor.h"
40 
41 #include "pios_lis3mdl_priv.h"
42 
43 #define PIOS_LIS_SPI_SPEED 9400000 /* 10MHz max per datasheet.
44  * probably results 9.33MHz */
45 
50 enum lis3mdl_dev_magic {
51  PIOS_LIS_DEV_MAGIC = 0x3353494c
52 };
53 
57 struct lis3mdl_dev {
58  enum lis3mdl_dev_magic magic;
59  const struct pios_lis3mdl_cfg *cfg;
60  pios_spi_t spi_id;
61  uint32_t spi_slave_mag;
62 };
63 
65 
68 static struct lis3mdl_dev *PIOS_LIS_Alloc(const struct pios_lis3mdl_cfg *cfg);
69 
74 static int32_t PIOS_LIS_Validate(struct lis3mdl_dev *dev);
75 
76 static bool PIOS_LIS_Callback(void *ctx, void *output,
77  int ms_to_wait, int *next_call);
78 
83 static int32_t PIOS_LIS_ClaimBus();
84 
89 static int32_t PIOS_LIS_ReleaseBus();
90 
91 static int32_t PIOS_LIS_ReadReg(lis3mdl_dev_t lis_dev,
92  uint8_t address, uint8_t *buffer);
93 static int32_t PIOS_LIS_WriteReg(lis3mdl_dev_t lis_dev,
94  uint8_t address, uint8_t buffer);
95 
96 static struct lis3mdl_dev *PIOS_LIS_Alloc(const struct pios_lis3mdl_cfg *cfg)
97 {
98  lis3mdl_dev_t dev;
99 
100  dev = (struct lis3mdl_dev *)PIOS_malloc(sizeof(*dev));
101  if (!dev)
102  return NULL;
103 
104  dev->magic = PIOS_LIS_DEV_MAGIC;
105 
106  return dev;
107 }
108 
109 static int32_t PIOS_LIS_Validate(struct lis3mdl_dev *dev)
110 {
111  if (dev == NULL)
112  return -1;
113  if (dev->magic != PIOS_LIS_DEV_MAGIC)
114  return -2;
115  if (dev->spi_id == 0)
116  return -3;
117  return 0;
118 }
119 
120 static int32_t AssertReg(lis3mdl_dev_t lis_dev,
121  uint8_t address, uint8_t expect) {
122  uint8_t c;
123 
124  int32_t ret = PIOS_LIS_ReadReg(lis_dev, address, &c);
125 
126  if (ret) {
127  return ret;
128  }
129 
130  if (c != expect) {
131  DEBUG_PRINTF(2,
132  "LIS: Assertion failed: *(%02x) == %02x (expect %02x)\n",
133  address, c, expect);
134  return -1;
135  }
136 
137  DEBUG_PRINTF(2, "LIS: Assertion passed: *(%02x) == %02x\n", address,
138  expect);
139 
140  return 0;
141 }
142 
143 int32_t PIOS_LIS3MDL_SPI_Init(lis3mdl_dev_t *dev, pios_spi_t spi_id,
144  uint32_t slave_mag, const struct pios_lis3mdl_cfg *cfg)
145 {
146  lis3mdl_dev_t lis_dev = PIOS_LIS_Alloc(cfg);
147  if (lis_dev == NULL)
148  return -1;
149  *dev = lis_dev;
150 
151  lis_dev->spi_id = spi_id;
152  lis_dev->spi_slave_mag = slave_mag;
153  lis_dev->cfg = cfg;
154 
155  int32_t ret;
156 
157  /* Unfortunately can't check mag right away, so proceed with
158  * init
159  */
160 
161  /* Verify expected chip id. */
162  ret = AssertReg(lis_dev, LIS_REG_MAG_WHO_AM_I,
164 
165  if (ret) {
166  return ret;
167  }
168 
169  /* Startup sequence, per ST AN4602 */
170 
171  /* First, write CTRL_REG2 to set scale */
172  ret = PIOS_LIS_WriteReg(lis_dev, LIS_REG_MAG_CTRL2,
174 
175  if (ret) {
176  return ret;
177  }
178 
179  /* Next, set UHP on X/Y and ODR=80. Enable temp sensor */
180  ret = PIOS_LIS_WriteReg(lis_dev, LIS_REG_MAG_CTRL1,
184 
185  if (ret) {
186  return ret;
187  }
188 
189  /* Next, enable UHP on Z axis */
190  ret = PIOS_LIS_WriteReg(lis_dev, LIS_REG_MAG_CTRL4,
192 
193  if (ret) {
194  return ret;
195  }
196 
197  /* Enable "Block data update" mode which prevents skew between
198  * LSB / MSB bytes of sensor value */
199  ret = PIOS_LIS_WriteReg(lis_dev, LIS_REG_MAG_CTRL5,
200  LIS_CTRL5_BDU);
201 
202  if (ret) {
203  return ret;
204  }
205 
206  /* Finally, enable continuous measurement mode with these parameters */
207  ret = PIOS_LIS_WriteReg(lis_dev, LIS_REG_MAG_CTRL3,
209 
210  if (ret) {
211  return ret;
212  }
213 
215  PIOS_LIS_Callback, lis_dev);
216 
217  return 0;
218 }
219 
220 static int32_t PIOS_LIS_ClaimBus(lis3mdl_dev_t lis_dev)
221 {
222  PIOS_Assert(!PIOS_LIS_Validate(lis_dev));
223 
224  if (PIOS_SPI_ClaimBus(lis_dev->spi_id) != 0)
225  return -2;
226 
227  PIOS_SPI_RC_PinSet(lis_dev->spi_id, lis_dev->spi_slave_mag, false);
228 
229  PIOS_SPI_SetClockSpeed(lis_dev->spi_id, PIOS_LIS_SPI_SPEED);
230 
231  return 0;
232 }
233 
234 static int32_t PIOS_LIS_ReleaseBus(lis3mdl_dev_t lis_dev)
235 {
236  PIOS_Assert(!PIOS_LIS_Validate(lis_dev));
237 
238  PIOS_SPI_RC_PinSet(lis_dev->spi_id, lis_dev->spi_slave_mag, true);
239 
240  PIOS_SPI_ReleaseBus(lis_dev->spi_id);
241 
242  return 0;
243 }
244 
245 static int32_t PIOS_LIS_ReadReg(lis3mdl_dev_t lis_dev,
246  uint8_t address, uint8_t *buffer)
247 {
248  PIOS_Assert(!(address & LIS_ADDRESS_READ));
250 
251  if (PIOS_LIS_ClaimBus(lis_dev) != 0)
252  return -1;
253 
254  PIOS_SPI_TransferByte(lis_dev->spi_id, LIS_ADDRESS_READ |
255  address); // request byte
256  *buffer = PIOS_SPI_TransferByte(lis_dev->spi_id, 0); // receive response
257 
258  PIOS_LIS_ReleaseBus(lis_dev);
259 
260  return 0;
261 }
262 
263 static int32_t PIOS_LIS_WriteReg(lis3mdl_dev_t lis_dev,
264  uint8_t address, uint8_t buffer)
265 {
266  PIOS_Assert(!PIOS_LIS_Validate(lis_dev));
267 
268  PIOS_Assert(!(address & LIS_ADDRESS_READ));
270 
271  if (PIOS_LIS_ClaimBus(lis_dev) != 0)
272  return -1;
273 
274  PIOS_SPI_TransferByte(lis_dev->spi_id, 0x7f & address);
275  PIOS_SPI_TransferByte(lis_dev->spi_id, buffer);
276 
277  PIOS_LIS_ReleaseBus(lis_dev);
278 
279  return 0;
280 }
281 
282 static bool PIOS_LIS_Callback(void *ctx, void *output,
283  int ms_to_wait, int *next_call)
284 {
285  lis3mdl_dev_t lis_dev = (lis3mdl_dev_t) ctx;
286 
287  PIOS_Assert(!PIOS_LIS_Validate(lis_dev));
288 
289  /* Wait 2ms if we don't have the data */
290  *next_call = 2;
291 
292  uint8_t status;
293  if (PIOS_LIS_ReadReg(lis_dev, LIS_REG_MAG_STATUS, &status))
294  return false;
295 
296  if (!(status & LIS_STATUS_ZYXDA))
297  return false;
298 
299  if (PIOS_LIS_ClaimBus(lis_dev) != 0)
300  return false;
301 
302  // TODO: Consider temperature in future
303  int16_t sensor_buf[
305 
306  PIOS_SPI_TransferByte(lis_dev->spi_id,
309 
310  if (PIOS_SPI_TransferBlock(lis_dev->spi_id, NULL,
311  (uint8_t *) sensor_buf,
312  sizeof(sensor_buf)) < 0) {
313  PIOS_LIS_ReleaseBus(lis_dev);
314  return false;
315  }
316 
317  PIOS_LIS_ReleaseBus(lis_dev);
318 
319 #define GET_SB_FROM_REGNO(x) \
320  (sensor_buf[ ((x) - LIS_REG_MAG_OUTX_L) / 2])
321 
322  float mag_x = GET_SB_FROM_REGNO(LIS_REG_MAG_OUTX_L);
323  float mag_y = GET_SB_FROM_REGNO(LIS_REG_MAG_OUTY_L);
324  float mag_z = GET_SB_FROM_REGNO(LIS_REG_MAG_OUTZ_L);
325 
326  struct pios_sensor_mag_data *mag_data = output;
327 
328  /* Adjust from 2's comp integer value to appropriate
329  * range */
331 
332  /*
333  * Vehicle axes = x front, y right, z down
334  * LIS3MDL axes = x left, y rear, z up
335  * See flight/Doc/imu_orientation.md
336  */
337  switch (lis_dev->cfg->orientation) {
338  case PIOS_LIS_TOP_0DEG:
339  mag_data->y = -mag_x * mag_scale;
340  mag_data->x = -mag_y * mag_scale;
341  mag_data->z = -mag_z * mag_scale;
342  break;
343  case PIOS_LIS_TOP_90DEG:
344  mag_data->y = -mag_y * mag_scale;
345  mag_data->x = mag_x * mag_scale;
346  mag_data->z = -mag_z * mag_scale;
347  break;
348  case PIOS_LIS_TOP_180DEG:
349  mag_data->y = mag_x * mag_scale;
350  mag_data->x = mag_y * mag_scale;
351  mag_data->z = -mag_z * mag_scale;
352  break;
353  case PIOS_LIS_TOP_270DEG:
354  mag_data->y = mag_y * mag_scale;
355  mag_data->x = -mag_x * mag_scale;
356  mag_data->z = -mag_z * mag_scale;
357  break;
359  mag_data->y = mag_x * mag_scale;
360  mag_data->x = -mag_y * mag_scale;
361  mag_data->z = mag_z * mag_scale;
362  break;
364  mag_data->y = -mag_y * mag_scale;
365  mag_data->x = -mag_x * mag_scale;
366  mag_data->z = mag_z * mag_scale;
367  break;
369  mag_data->y = -mag_x * mag_scale;
370  mag_data->x = mag_y * mag_scale;
371  mag_data->z = mag_z * mag_scale;
372  break;
374  mag_data->y = mag_y * mag_scale;
375  mag_data->x = mag_x * mag_scale;
376  mag_data->z = mag_z * mag_scale;
377  break;
378  }
379 
380  /* Output rate of 80 implies 12.5ms between samples.
381  * We sleep 11ms before first try, and then 2ms between subsequent
382  * subsequent tries. This means we normally get the data on our
383  * second try.
384  */
385  *next_call = 11;
386 
387  return true;
388 }
389 
390 #endif // PIOS_INCLUDE_LIS3MDL
391 
#define LIS_WHO_AM_I_VAL
Main PiOS header to include all the compiled in PiOS options.
#define LIS_CTRL3_MODE_CONTINUOUS
#define LIS_ADDRESS_READ
int32_t PIOS_SPI_RC_PinSet(pios_spi_t spi_dev, uint32_t slave_id, bool pin_value)
int32_t PIOS_SPI_ClaimBus(pios_spi_t spi_dev)
struct lis3mdl_dev * lis3mdl_dev_t
Definition: pios_lis3mdl.h:50
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
#define LIS_ADDRESS_AUTOINCREMENT
#define LIS_CTRL5_BDU
#define LIS_CTRL1_TEMPEN
#define LIS_CTRL1_ODR_80
#define DEBUG_PRINTF(level,...)
Definition: pios_board.h:39
Task monitoring library.
#define LIS_CTRL1_OPMODE_UHP
static struct flyingpicmd_cfg_fa cfg
Definition: main.c:49
int16_t status
Definition: main.c:61
#define LIS_STATUS_ZYXDA
uint8_t PIOS_SPI_TransferByte(pios_spi_t spi_dev, uint8_t b)
int32_t PIOS_SPI_SetClockSpeed(pios_spi_t spi_dev, uint32_t speed)
int32_t PIOS_SPI_TransferBlock(pios_spi_t spi_dev, const uint8_t *send_buffer, uint8_t *receive_buffer, uint16_t len)
static float mag_scale[3]
Definition: sensors.c:113
const struct pios_spi_cfg * cfg
Definition: pios_spi_priv.h:39
int32_t PIOS_SENSORS_RegisterCallback(enum pios_sensor_type type, PIOS_SENSOR_Callback_t callback, void *ctx)
Register a callback-based sensor with the PIOS_SENSORS interface.
Definition: pios_sensors.c:68
#define LIS_RANGE_12GAU_COUNTS_PER_MGAU
int32_t PIOS_LIS3MDL_SPI_Init(lis3mdl_dev_t *dev, pios_spi_t spi_id, uint32_t slave_mag, const struct pios_lis3mdl_cfg *cfg)
Initialize the LIS3MDL mag.
uint32_t magic
Pios sensor structure for generic mag data.
Definition: pios_sensors.h:54
#define LIS_CTRL4_OPMODEZ_UHP
Includes PiOS and core architecture components.
int32_t PIOS_SPI_ReleaseBus(pios_spi_t spi_dev)
#define PIOS_Assert(test)
Definition: pios_debug.h:52
#define LIS_CTRL2_FS_12GAU