dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_hmc5883.c
Go to the documentation of this file.
1 
16 /*
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25  * for more details.
26  *
27  * You should have received a copy of the GNU General Public License along
28  * with this program; if not, see <http://www.gnu.org/licenses/>
29  */
30 
31 /* Project Includes */
32 #include "pios.h"
33 #include "pios_hmc5883_priv.h"
34 
35 #if defined(PIOS_INCLUDE_HMC5883)
36 
37 #include "pios_semaphore.h"
38 #include "pios_thread.h"
39 #include "pios_queue.h"
40 
41 /* Private constants */
42 #define HMC5883_TASK_PRIORITY PIOS_THREAD_PRIO_HIGHEST
43 #define HMC5883_TASK_STACK_BYTES 512
44 #define PIOS_HMC5883_MAX_DOWNSAMPLE 1
45 
46 /* Global Variables */
47 
48 /* Local Types */
49 enum pios_hmc5883_dev_magic {
50  PIOS_HMC5883_DEV_MAGIC = 0x3d8e17ab,
51 };
52 
53 struct hmc5883_dev {
54  pios_i2c_t i2c_id;
55  const struct pios_hmc5883_cfg *cfg;
56  struct pios_queue *queue;
57  struct pios_thread *task;
58  struct pios_semaphore *data_ready_sema;
59  enum pios_hmc5883_dev_magic magic;
60  enum pios_hmc5883_orientation orientation;
61 };
62 
63 /* Local Variables */
64 static int32_t PIOS_HMC5883_Config(const struct pios_hmc5883_cfg * cfg);
65 static int32_t PIOS_HMC5883_Read(uint8_t address, uint8_t * buffer, uint8_t len);
66 static int32_t PIOS_HMC5883_Write(uint8_t address, uint8_t buffer);
67 static void PIOS_HMC5883_Task(void *parameters);
68 
69 static struct hmc5883_dev *dev;
70 
74 static struct hmc5883_dev * PIOS_HMC5883_alloc(void)
75 {
76  struct hmc5883_dev *hmc5883_dev;
77 
78  hmc5883_dev = (struct hmc5883_dev *)PIOS_malloc(sizeof(*hmc5883_dev));
79  if (!hmc5883_dev) return (NULL);
80 
81  hmc5883_dev->magic = PIOS_HMC5883_DEV_MAGIC;
82 
83  hmc5883_dev->queue = PIOS_Queue_Create(PIOS_HMC5883_MAX_DOWNSAMPLE, sizeof(struct pios_sensor_mag_data));
84  if (hmc5883_dev->queue == NULL) {
85  PIOS_free(hmc5883_dev);
86  return NULL;
87  }
88 
89  return hmc5883_dev;
90 }
91 
96 static int32_t PIOS_HMC5883_Validate(struct hmc5883_dev *dev)
97 {
98  if (dev == NULL)
99  return -1;
100  if (dev->magic != PIOS_HMC5883_DEV_MAGIC)
101  return -2;
102  if (dev->i2c_id == 0)
103  return -3;
104  return 0;
105 }
106 
111 int32_t PIOS_HMC5883_Init(pios_i2c_t i2c_id, const struct pios_hmc5883_cfg *cfg)
112 {
113  dev = (struct hmc5883_dev *) PIOS_HMC5883_alloc();
114  if (dev == NULL)
115  return -1;
116 
117  dev->cfg = cfg;
118  dev->i2c_id = i2c_id;
119  dev->orientation = cfg->Default_Orientation;
120 
121  /* check if we are using an irq line */
122 #ifndef PIOS_HMC5883_NO_EXTI
123  if (cfg->exti_cfg != NULL) {
124  PIOS_EXTI_Init(cfg->exti_cfg);
125 
126  dev->data_ready_sema = PIOS_Semaphore_Create();
127  PIOS_Assert(dev->data_ready_sema != NULL);
128  }
129  else {
130  dev->data_ready_sema = NULL;
131  }
132 #endif
133 
134  if (PIOS_HMC5883_Config(cfg) != 0)
135  return -2;
136 
138 
139  dev->task = PIOS_Thread_Create(PIOS_HMC5883_Task, "pios_hmc5883", HMC5883_TASK_STACK_BYTES, NULL, HMC5883_TASK_PRIORITY);
140 
141  PIOS_Assert(dev->task != NULL);
142 
143  return 0;
144 }
145 
151 {
152  if (PIOS_HMC5883_Validate(dev) != 0)
153  return -1;
154 
155  dev->orientation = orientation;
156 
157  return 0;
158 }
159 
219 static int32_t PIOS_HMC5883_Config(const struct pios_hmc5883_cfg * cfg)
220 {
221  // CRTL_REGA
222  if (PIOS_HMC5883_Write(PIOS_HMC5883_CONFIG_REG_A, cfg->M_ODR | cfg->Meas_Conf) != 0)
223  return -1;
224 
225  // CRTL_REGB
226  if (PIOS_HMC5883_Write(PIOS_HMC5883_CONFIG_REG_B, cfg->Gain) != 0)
227  return -1;
228 
229  // Mode register
230  if (PIOS_HMC5883_Write(PIOS_HMC5883_MODE_REG, cfg->Mode) != 0)
231  return -1;
232 
233  return 0;
234 }
235 
240 static uint16_t PIOS_HMC5883_Config_GetSensitivity()
241 {
242  switch (dev->cfg->Gain) {
259  }
260 
261  return 0;
262 }
263 
269 static int32_t PIOS_HMC5883_ReadMag(struct pios_sensor_mag_data *mag_data)
270 {
271  if (PIOS_HMC5883_Validate(dev) != 0)
272  return -1;
273 
274  /* don't use PIOS_HMC5883_Read and PIOS_HMC5883_Write here because the task could be
275  * switched out of context in between which would give the sensor less time to capture
276  * the next sample.
277  */
278  uint8_t addr_read = PIOS_HMC5883_DATAOUT_XMSB_REG;
279  uint8_t buffer_read[6];
280 
281  // PIOS_HMC5883_MODE_CONTINUOUS: This should not be necessary but for some reason it is coming out of continuous conversion mode
282  // PIOS_HMC5883_MODE_SINGLE: This triggers the next measurement
283  uint8_t buffer_write[2] = {
285  dev->cfg->Mode
286  };
287 
288  const struct pios_i2c_txn txn_list[] = {
289  {
290  .info = __func__,
291  .addr = PIOS_HMC5883_I2C_ADDR,
292  .rw = PIOS_I2C_TXN_WRITE,
293  .len = sizeof(addr_read),
294  .buf = &addr_read,
295  },
296  {
297  .info = __func__,
298  .addr = PIOS_HMC5883_I2C_ADDR,
299  .rw = PIOS_I2C_TXN_READ,
300  .len = sizeof(buffer_read),
301  .buf = buffer_read,
302  },
303  {
304  .info = __func__,
305  .addr = PIOS_HMC5883_I2C_ADDR,
306  .rw = PIOS_I2C_TXN_WRITE,
307  .len = sizeof(buffer_write),
308  .buf = buffer_write,
309  },
310  };
311 
312  if (PIOS_I2C_Transfer(dev->i2c_id, txn_list, NELEMENTS(txn_list)) != 0)
313  return -1;
314 
315  int16_t mag_x, mag_y, mag_z;
316  uint16_t sensitivity = PIOS_HMC5883_Config_GetSensitivity();
317  mag_x = ((int16_t) ((uint16_t)buffer_read[0] << 8) + buffer_read[1]) * 1000 / sensitivity;
318  mag_z = ((int16_t) ((uint16_t)buffer_read[2] << 8) + buffer_read[3]) * 1000 / sensitivity;
319  mag_y = ((int16_t) ((uint16_t)buffer_read[4] << 8) + buffer_read[5]) * 1000 / sensitivity;
320 
321  // Define "0" when the fiducial is in the front left of the board
322  switch (dev->orientation) {
324  mag_data->x = -mag_x;
325  mag_data->y = mag_y;
326  mag_data->z = -mag_z;
327  break;
329  mag_data->x = -mag_y;
330  mag_data->y = -mag_x;
331  mag_data->z = -mag_z;
332  break;
334  mag_data->x = mag_x;
335  mag_data->y = -mag_y;
336  mag_data->z = -mag_z;
337  break;
339  mag_data->x = mag_y;
340  mag_data->y = mag_x;
341  mag_data->z = -mag_z;
342  break;
344  mag_data->x = -mag_x;
345  mag_data->y = -mag_y;
346  mag_data->z = mag_z;
347  break;
349  mag_data->x = mag_y;
350  mag_data->y = -mag_x;
351  mag_data->z = mag_z;
352  break;
354  mag_data->x = mag_x;
355  mag_data->y = mag_y;
356  mag_data->z = mag_z;
357  break;
359  mag_data->x = -mag_y;
360  mag_data->y = mag_x;
361  mag_data->z = mag_z;
362  break;
363  }
364 
365  return 0;
366 }
367 
368 
374 static uint8_t PIOS_HMC5883_ReadID(uint8_t out[4])
375 {
376  uint8_t retval = PIOS_HMC5883_Read(PIOS_HMC5883_DATAOUT_IDA_REG, out, 3);
377  out[3] = '\0';
378  return retval;
379 }
380 
381 
391 static int32_t PIOS_HMC5883_Read(uint8_t address, uint8_t * buffer, uint8_t len)
392 {
393  if(PIOS_HMC5883_Validate(dev) != 0)
394  return -1;
395 
396  uint8_t addr_buffer[] = {
397  address,
398  };
399 
400  const struct pios_i2c_txn txn_list[] = {
401  {
402  .info = __func__,
403  .addr = PIOS_HMC5883_I2C_ADDR,
404  .rw = PIOS_I2C_TXN_WRITE,
405  .len = sizeof(addr_buffer),
406  .buf = addr_buffer,
407  }
408  ,
409  {
410  .info = __func__,
411  .addr = PIOS_HMC5883_I2C_ADDR,
412  .rw = PIOS_I2C_TXN_READ,
413  .len = len,
414  .buf = buffer,
415  }
416  };
417 
418  return PIOS_I2C_Transfer(dev->i2c_id, txn_list, NELEMENTS(txn_list));
419 }
420 
429 static int32_t PIOS_HMC5883_Write(uint8_t address, uint8_t buffer)
430 {
431  if(PIOS_HMC5883_Validate(dev) != 0)
432  return -1;
433 
434  uint8_t data[] = {
435  address,
436  buffer,
437  };
438 
439  const struct pios_i2c_txn txn_list[] = {
440  {
441  .info = __func__,
442  .addr = PIOS_HMC5883_I2C_ADDR,
443  .rw = PIOS_I2C_TXN_WRITE,
444  .len = sizeof(data),
445  .buf = data,
446  }
447  ,
448  };
449 
450  return PIOS_I2C_Transfer(dev->i2c_id, txn_list, NELEMENTS(txn_list));
451 }
452 
457 int32_t PIOS_HMC5883_Test(void)
458 {
459  /* Verify that ID matches (HMC5883 ID is null-terminated ASCII string "H43") */
460  char id[4];
461  PIOS_HMC5883_ReadID((uint8_t *)id);
462  if((id[0] != 'H') || (id[1] != '4') || (id[2] != '3')) // Expect H43
463  return -1;
464 
465  return 0;
466 }
467 
471 #ifndef PIOS_HMC5883_NO_EXTI
472 bool PIOS_HMC5883_IRQHandler(void)
473 {
474  if (PIOS_HMC5883_Validate(dev) != 0)
475  return false;
476 
477  bool woken = false;
478  PIOS_Semaphore_Give_FromISR(dev->data_ready_sema, &woken);
479 
480  return woken;
481 }
482 #endif
483 
487 static void PIOS_HMC5883_Task(void *parameters)
488 {
489  while (PIOS_HMC5883_Validate(dev) != 0) {
490  PIOS_Thread_Sleep(100);
491  }
492 
493  uint32_t sample_delay;
494 
495  switch (dev->cfg->M_ODR) {
497  sample_delay = 1000 / 0.75f + 0.99999f;
498  break;
500  sample_delay = 1000 / 1.5f + 0.99999f;
501  break;
502  case PIOS_HMC5883_ODR_3:
503  sample_delay = 1000 / 3.0f + 0.99999f;
504  break;
506  sample_delay = 1000 / 7.5f + 0.99999f;
507  break;
508  case PIOS_HMC5883_ODR_15:
509  sample_delay = 1000 / 15.0f + 0.99999f;
510  break;
511  case PIOS_HMC5883_ODR_30:
512  sample_delay = 1000 / 30.0f + 0.99999f;
513  break;
514  case PIOS_HMC5883_ODR_75:
515  default:
516  sample_delay = 1000 / 75.0f + 0.99999f;
517  break;
518  }
519 
520  uint32_t now = PIOS_Thread_Systime();
521 
522  while (1) {
523  if ((dev->data_ready_sema != NULL) && (dev->cfg->Mode == PIOS_HMC5883_MODE_CONTINUOUS)) {
524  if (PIOS_Semaphore_Take(dev->data_ready_sema, PIOS_SEMAPHORE_TIMEOUT_MAX) != true) {
525  PIOS_Thread_Sleep(100);
526  continue;
527  }
528  } else {
529  PIOS_Thread_Sleep_Until(&now, sample_delay);
530  }
531 
532  struct pios_sensor_mag_data mag_data;
533  if (PIOS_HMC5883_ReadMag(&mag_data) == 0)
534  PIOS_Queue_Send(dev->queue, &mag_data, 0);
535  }
536 }
537 
538 #endif /* PIOS_INCLUDE_HMC5883 */
539 
uint32_t PIOS_Thread_Systime(void)
Definition: pios_thread.c:212
#define PIOS_HMC5883_GAIN_5_6
const struct pios_exti_cfg * exti_cfg
#define PIOS_HMC5883_MODE_REG
#define PIOS_HMC5883_ODR_1_5
struct pios_queue * PIOS_Queue_Create(size_t queue_length, size_t item_size)
Definition: pios_queue.c:47
#define PIOS_SEMAPHORE_TIMEOUT_MAX
int32_t PIOS_HMC5883_SetOrientation(enum pios_hmc5883_orientation orientation)
#define PIOS_HMC5883_ODR_75
Main PiOS header to include all the compiled in PiOS options.
pios_hmc5883_orientation
#define PIOS_HMC5883_GAIN_1_3
int32_t PIOS_HMC5883_Test(void)
#define NELEMENTS(x)
Definition: pios.h:192
int32_t PIOS_EXTI_Init(const struct pios_exti_cfg *cfg)
#define PIOS_HMC5883_MODE_CONTINUOUS
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
bool PIOS_Queue_Send(struct pios_queue *queuep, const void *itemp, uint32_t timeout_ms)
Definition: pios_queue.c:141
bool PIOS_Semaphore_Give_FromISR(struct pios_semaphore *sema, bool *woken)
bool PIOS_HMC5883_IRQHandler()
#define PIOS_HMC5883_DATAOUT_IDA_REG
#define PIOS_HMC5883_ODR_15
#define PIOS_HMC5883_ODR_7_5
uint8_t * buf
Definition: pios_i2c.h:45
#define PIOS_HMC5883_GAIN_4_0
#define PIOS_HMC5883_CONFIG_REG_B
uint8_t data[XFER_BYTES_PER_PACKET]
Definition: bl_messages.h:129
static struct flyingpicmd_cfg_fa cfg
Definition: main.c:49
struct pios_semaphore * PIOS_Semaphore_Create(void)
Creates a binary semaphore.
struct pios_i2c_adapter * pios_i2c_t
Definition: pios_i2c.h:48
struct pios_thread * PIOS_Thread_Create(void(*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
Definition: pios_thread.c:89
#define PIOS_HMC5883_GAIN_2_5
int32_t PIOS_SENSORS_Register(enum pios_sensor_type type, struct pios_queue *queue)
Register a queue-based sensor with the PIOS_SENSORS interface.
Definition: pios_sensors.c:82
static TaskInfoRunningElem task
#define PIOS_HMC5883_GAIN_1_9
int32_t PIOS_I2C_Transfer(pios_i2c_t i2c_id, const struct pios_i2c_txn txn_list[], uint32_t num_txns)
#define PIOS_HMC5883_GAIN_4_7
void PIOS_Thread_Sleep_Until(uint32_t *previous_ms, uint32_t increment_ms)
Definition: pios_thread.c:255
uint32_t magic
void PIOS_free(void *buf)
Definition: pios_heap.c:174
void PIOS_Thread_Sleep(uint32_t time_ms)
Definition: pios_thread.c:229
#define PIOS_HMC5883_ODR_30
Pios sensor structure for generic mag data.
Definition: pios_sensors.h:54
int32_t PIOS_HMC5883_Init(pios_i2c_t i2c_id, const struct pios_hmc5883_cfg *cfg)
#define PIOS_HMC5883_DATAOUT_XMSB_REG
bool PIOS_Semaphore_Take(struct pios_semaphore *sema, uint32_t timeout_ms)
Takes binary semaphore.
#define PIOS_HMC5883_CONFIG_REG_A
const char * info
Definition: pios_i2c.h:41
#define PIOS_HMC5883_I2C_ADDR
static struct pios_queue * queue
Definition: actuator.c:82
uint32_t len
Definition: pios_i2c.h:44
enum pios_hmc5883_orientation Default_Orientation
#define PIOS_HMC5883_GAIN_0_88
#define PIOS_HMC5883_ODR_3
#define PIOS_Assert(test)
Definition: pios_debug.h:52
#define PIOS_HMC5883_ODR_0_75
#define PIOS_HMC5883_GAIN_8_1