2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
5 * @addtogroup PIOS_HMC5x83 HMC5x83 Functions
6 * @brief Deals with the hardware interface to the magnetometers
9 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
10 * @brief HMC5x83 Magnetic Sensor Functions from AHRS
11 * @see The GNU Public License (GPL) Version 3
13 ******************************************************************************
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include <pios_hmc5x83.h>
35 #ifdef PIOS_INCLUDE_HMC5X83
37 #define PIOS_HMC5X83_MAGIC 0x4d783833
38 /* Global Variables */
44 const struct pios_hmc5x83_cfg
*cfg
;
48 volatile bool data_ready
;
49 } pios_hmc5x83_dev_data_t
;
51 static int32_t PIOS_HMC5x83_Config(pios_hmc5x83_dev_data_t
*dev
);
53 // sensor driver interface
54 bool PIOS_HMC5x83_driver_Test(uintptr_t context
);
55 void PIOS_HMC5x83_driver_Reset(uintptr_t context
);
56 void PIOS_HMC5x83_driver_get_scale(float *scales
, uint8_t size
, uintptr_t context
);
57 void PIOS_HMC5x83_driver_fetch(void *, uint8_t size
, uintptr_t context
);
58 bool PIOS_HMC5x83_driver_poll(uintptr_t context
);
60 const PIOS_SENSORS_Driver PIOS_HMC5x83_Driver
= {
61 .test
= PIOS_HMC5x83_driver_Test
,
62 .poll
= PIOS_HMC5x83_driver_poll
,
63 .fetch
= PIOS_HMC5x83_driver_fetch
,
64 .reset
= PIOS_HMC5x83_driver_Reset
,
66 .get_scale
= PIOS_HMC5x83_driver_get_scale
,
70 * Allocate the device setting structure
71 * @return pios_hmc5x83_dev_data_t pointer to newly created structure
73 pios_hmc5x83_dev_data_t
*dev_alloc()
75 pios_hmc5x83_dev_data_t
*dev
= (pios_hmc5x83_dev_data_t
*)pios_malloc(sizeof(pios_hmc5x83_dev_data_t
));
77 PIOS_DEBUG_Assert(dev
);
78 memset(dev
, 0x00, sizeof(pios_hmc5x83_dev_data_t
));
79 dev
->magic
= PIOS_HMC5X83_MAGIC
;
84 * Validate a pios_hmc5x83_dev_t handler and return the related pios_hmc5x83_dev_data_t pointer
85 * @param dev device handler
86 * @return the device data structure
88 pios_hmc5x83_dev_data_t
*dev_validate(pios_hmc5x83_dev_t dev
)
90 pios_hmc5x83_dev_data_t
*dev_data
= (pios_hmc5x83_dev_data_t
*)dev
;
92 PIOS_DEBUG_Assert(dev_data
->magic
== PIOS_HMC5X83_MAGIC
);
97 * @brief Initialize the HMC5x83 magnetometer sensor.
100 pios_hmc5x83_dev_t
PIOS_HMC5x83_Init(const struct pios_hmc5x83_cfg
*cfg
, uint32_t port_id
, uint8_t slave_num
)
102 pios_hmc5x83_dev_data_t
*dev
= dev_alloc();
104 dev
->cfg
= cfg
; // store config before enabling interrupt
105 dev
->port_id
= port_id
;
106 dev
->slave_num
= slave_num
;
107 #ifdef PIOS_HMC5X83_HAS_GPIOS
108 PIOS_EXTI_Init(cfg
->exti_cfg
);
111 int32_t val
= PIOS_HMC5x83_Config(dev
);
112 PIOS_Assert(val
== 0);
114 dev
->data_ready
= false;
115 return (pios_hmc5x83_dev_t
)dev
;
118 void PIOS_HMC5x83_Register(pios_hmc5x83_dev_t handler
)
120 PIOS_SENSORS_Register(&PIOS_HMC5x83_Driver
, PIOS_SENSORS_TYPE_3AXIS_MAG
, handler
);
124 * @brief Initialize the HMC5x83 magnetometer sensor
126 * \param[in] pios_hmc5x83_dev_data_t device config to be used.
127 * \param[in] PIOS_HMC5x83_ConfigTypeDef struct to be used to configure sensor.
129 * CTRL_REGA: Control Register A
131 * Default value: 0x10
132 * 7:5 0 These bits must be cleared for correct operation.
133 * 4:2 DO2-DO0: Data Output Rate Bits
134 * DO2 | DO1 | DO0 | Minimum Data Output Rate (Hz)
135 * ------------------------------------------------------
140 * 1 | 0 | 0 | 15 (default)
143 * 1 | 1 | 1 | Not Used
144 * 1:0 MS1-MS0: Measurement Configuration Bits
146 * ------------------------------
148 * 0 | 1 | Positive Bias
149 * 1 | 0 | Negative Bias
152 * CTRL_REGB: Control RegisterB
154 * Default value: 0x20
155 * 7:5 GN2-GN0: Gain Configuration Bits.
156 * GN2 | GN1 | GN0 | Mag Input | Gain | Output Range
157 * | | | Range[Ga] | [LSB/mGa] |
158 * ------------------------------------------------------
159 * 0 | 0 | 0 | ±0.88Ga | 1370 | 0xF8000x07FF (-2048:2047)
160 * 0 | 0 | 1 | ±1.3Ga (def) | 1090 | 0xF8000x07FF (-2048:2047)
161 * 0 | 1 | 0 | ±1.9Ga | 820 | 0xF8000x07FF (-2048:2047)
162 * 0 | 1 | 1 | ±2.5Ga | 660 | 0xF8000x07FF (-2048:2047)
163 * 1 | 0 | 0 | ±4.0Ga | 440 | 0xF8000x07FF (-2048:2047)
164 * 1 | 0 | 1 | ±4.7Ga | 390 | 0xF8000x07FF (-2048:2047)
165 * 1 | 1 | 0 | ±5.6Ga | 330 | 0xF8000x07FF (-2048:2047)
166 * 1 | 1 | 1 | ±8.1Ga | 230 | 0xF8000x07FF (-2048:2047)
169 * 4:0 CRB4-CRB: 0 This bit must be cleared for correct operation.
171 * _MODE_REG: Mode Register
173 * Default value: 0x02
174 * 7:2 0 These bits must be cleared for correct operation.
175 * 1:0 MD1-MD0: Mode Select Bits
177 * ------------------------------
178 * 0 | 0 | Continuous-Conversion Mode.
179 * 0 | 1 | Single-Conversion Mode
180 * 1 | 0 | Negative Bias
183 static int32_t PIOS_HMC5x83_Config(pios_hmc5x83_dev_data_t
*dev
)
185 uint8_t CTRLA
= 0x00;
188 const struct pios_hmc5x83_cfg
*cfg
= dev
->cfg
;
192 CTRLA
|= (uint8_t)(cfg
->M_ODR
| cfg
->Meas_Conf
);
193 CTRLA
|= cfg
->TempCompensation
? PIOS_HMC5x83_CTRLA_TEMP
: 0;
194 dev
->CTRLB
|= (uint8_t)(cfg
->Gain
);
195 MODE
|= (uint8_t)(cfg
->Mode
);
198 if (cfg
->Driver
->Write((pios_hmc5x83_dev_t
)dev
, PIOS_HMC5x83_CONFIG_REG_A
, CTRLA
) != 0) {
203 if (cfg
->Driver
->Write((pios_hmc5x83_dev_t
)dev
, PIOS_HMC5x83_CONFIG_REG_B
, dev
->CTRLB
) != 0) {
208 if (cfg
->Driver
->Write((pios_hmc5x83_dev_t
)dev
, PIOS_HMC5x83_MODE_REG
, MODE
) != 0) {
216 * @brief Read current X, Z, Y values (in that order)
217 * \param[in] dev device handler
218 * \param[out] int16_t array of size 3 to store X, Z, and Y magnetometer readings
219 * \return 0 for success or -1 for failure
221 int32_t PIOS_HMC5x83_ReadMag(pios_hmc5x83_dev_t handler
, int16_t out
[3])
223 pios_hmc5x83_dev_data_t
*dev
= dev_validate(handler
);
225 dev
->data_ready
= false;
230 if (dev
->cfg
->Driver
->Read(handler
, PIOS_HMC5x83_DATAOUT_XMSB_REG
, buffer
, 6) != 0) {
234 switch (dev
->CTRLB
& 0xE0) {
236 sensitivity
= PIOS_HMC5x83_Sensitivity_0_88Ga
;
239 sensitivity
= PIOS_HMC5x83_Sensitivity_1_3Ga
;
242 sensitivity
= PIOS_HMC5x83_Sensitivity_1_9Ga
;
245 sensitivity
= PIOS_HMC5x83_Sensitivity_2_5Ga
;
248 sensitivity
= PIOS_HMC5x83_Sensitivity_4_0Ga
;
251 sensitivity
= PIOS_HMC5x83_Sensitivity_4_7Ga
;
254 sensitivity
= PIOS_HMC5x83_Sensitivity_5_6Ga
;
257 sensitivity
= PIOS_HMC5x83_Sensitivity_8_1Ga
;
262 for (int i
= 0; i
< 3; i
++) {
263 int16_t v
= ((int16_t)((uint16_t)buffer
[2 * i
] << 8)
264 + buffer
[2 * i
+ 1]) * 1000 / sensitivity
;
268 switch (dev
->cfg
->Orientation
) {
269 case PIOS_HMC5X83_ORIENTATION_EAST_NORTH_UP
:
274 case PIOS_HMC5X83_ORIENTATION_SOUTH_EAST_UP
:
279 case PIOS_HMC5X83_ORIENTATION_WEST_SOUTH_UP
:
284 case PIOS_HMC5X83_ORIENTATION_NORTH_WEST_UP
:
289 case PIOS_HMC5X83_ORIENTATION_EAST_SOUTH_DOWN
:
294 case PIOS_HMC5X83_ORIENTATION_SOUTH_WEST_DOWN
:
299 case PIOS_HMC5X83_ORIENTATION_WEST_NORTH_DOWN
:
304 case PIOS_HMC5X83_ORIENTATION_NORTH_EAST_DOWN
:
311 // This should not be necessary but for some reason it is coming out of continuous conversion mode
312 dev
->cfg
->Driver
->Write(handler
, PIOS_HMC5x83_MODE_REG
, PIOS_HMC5x83_MODE_CONTINUOUS
);
319 * @brief Read the identification bytes from the HMC5x83 sensor
320 * \param[out] uint8_t array of size 4 to store HMC5x83 ID.
321 * \return 0 if successful, -1 if not
323 uint8_t PIOS_HMC5x83_ReadID(pios_hmc5x83_dev_t handler
, uint8_t out
[4])
325 pios_hmc5x83_dev_data_t
*dev
= dev_validate(handler
);
326 uint8_t retval
= dev
->cfg
->Driver
->Read(handler
, PIOS_HMC5x83_DATAOUT_IDA_REG
, out
, 3);
333 * @brief Tells whether new magnetometer readings are available
334 * \return true if new data is available
335 * \return false if new data is not available
337 bool PIOS_HMC5x83_NewDataAvailable(pios_hmc5x83_dev_t handler
)
339 pios_hmc5x83_dev_data_t
*dev
= dev_validate(handler
);
341 return dev
->data_ready
;
345 * @brief Run self-test operation. Do not call this during operational use!!
346 * \return 0 if success, -1 if test failed
348 int32_t PIOS_HMC5x83_Test(pios_hmc5x83_dev_t handler
)
351 uint8_t registers
[3] = { 0, 0, 0 };
357 pios_hmc5x83_dev_data_t
*dev
= dev_validate(handler
);
359 /* Verify that ID matches (HMC5x83 ID is null-terminated ASCII string "H43") */
362 PIOS_HMC5x83_ReadID(handler
, (uint8_t *)id
);
363 if ((id
[0] != 'H') || (id
[1] != '4') || (id
[2] != '3')) { // Expect H43
367 /* Backup existing configuration */
368 if (dev
->cfg
->Driver
->Read(handler
, PIOS_HMC5x83_CONFIG_REG_A
, registers
, 3) != 0) {
372 /* Stop the device and read out last value */
373 PIOS_DELAY_WaitmS(10);
374 if (dev
->cfg
->Driver
->Write(handler
, PIOS_HMC5x83_MODE_REG
, PIOS_HMC5x83_MODE_IDLE
) != 0) {
377 if (dev
->cfg
->Driver
->Read(handler
, PIOS_HMC5x83_DATAOUT_STATUS_REG
, &status
, 1) != 0) {
380 if (PIOS_HMC5x83_ReadMag(handler
, values
) != 0) {
385 * Put HMC5x83 into self test mode
386 * This is done by placing measurement config into positive (0x01) or negative (0x10) bias
387 * and then placing the mode register into single-measurement mode. This causes the HMC5x83
388 * to create an artificial magnetic field of ~1.1 Gauss.
390 * If gain were PIOS_HMC5x83_GAIN_2_5, for example, X and Y will read around +766 LSB
391 * (1.16 Ga * 660 LSB/Ga) and Z would read around +713 LSB (1.08 Ga * 660 LSB/Ga)
393 * Changing measurement config back to PIOS_HMC5x83_MEASCONF_NORMAL will leave self-test mode.
395 PIOS_DELAY_WaitmS(10);
396 if (dev
->cfg
->Driver
->Write(handler
, PIOS_HMC5x83_CONFIG_REG_A
, PIOS_HMC5x83_MEASCONF_BIAS_POS
| PIOS_HMC5x83_ODR_15
) != 0) {
399 PIOS_DELAY_WaitmS(10);
400 if (dev
->cfg
->Driver
->Write(handler
, PIOS_HMC5x83_CONFIG_REG_B
, PIOS_HMC5x83_GAIN_8_1
) != 0) {
403 PIOS_DELAY_WaitmS(10);
404 if (dev
->cfg
->Driver
->Write(handler
, PIOS_HMC5x83_MODE_REG
, PIOS_HMC5x83_MODE_SINGLE
) != 0) {
408 /* Must wait for value to be updated */
409 PIOS_DELAY_WaitmS(200);
411 if (PIOS_HMC5x83_ReadMag(handler
, values
) != 0) {
415 dev
->cfg
->Driver
->Read(handler
, PIOS_HMC5x83_CONFIG_REG_A
, &ctrl_a_read
, 1);
416 dev
->cfg
->Driver
->Read(handler
, PIOS_HMC5x83_CONFIG_REG_B
, &ctrl_b_read
, 1);
417 dev
->cfg
->Driver
->Read(handler
, PIOS_HMC5x83_MODE_REG
, &mode_read
, 1);
418 dev
->cfg
->Driver
->Read(handler
, PIOS_HMC5x83_DATAOUT_STATUS_REG
, &status
, 1);
421 /* Restore backup configuration */
422 PIOS_DELAY_WaitmS(10);
423 if (dev
->cfg
->Driver
->Write(handler
, PIOS_HMC5x83_CONFIG_REG_A
, registers
[0]) != 0) {
426 PIOS_DELAY_WaitmS(10);
427 if (dev
->cfg
->Driver
->Write(handler
, PIOS_HMC5x83_CONFIG_REG_B
, registers
[1]) != 0) {
430 PIOS_DELAY_WaitmS(10);
431 if (dev
->cfg
->Driver
->Write(handler
, PIOS_HMC5x83_MODE_REG
, registers
[2]) != 0) {
441 bool PIOS_HMC5x83_IRQHandler(pios_hmc5x83_dev_t handler
)
443 pios_hmc5x83_dev_data_t
*dev
= dev_validate(handler
);
445 dev
->data_ready
= true;
449 #ifdef PIOS_INCLUDE_SPI
450 int32_t PIOS_HMC5x83_SPI_Read(pios_hmc5x83_dev_t handler
, uint8_t address
, uint8_t *buffer
, uint8_t len
);
451 int32_t PIOS_HMC5x83_SPI_Write(pios_hmc5x83_dev_t handler
, uint8_t address
, uint8_t buffer
);
453 const struct pios_hmc5x83_io_driver PIOS_HMC5x83_SPI_DRIVER
= {
454 .Read
= PIOS_HMC5x83_SPI_Read
,
455 .Write
= PIOS_HMC5x83_SPI_Write
,
458 static int32_t pios_hmc5x83_spi_claim_bus(pios_hmc5x83_dev_data_t
*dev
)
460 if (PIOS_SPI_ClaimBus(dev
->port_id
) < 0) {
463 PIOS_SPI_SetClockSpeed(dev
->port_id
, SPI_BaudRatePrescaler_16
);
464 PIOS_SPI_RC_PinSet(dev
->port_id
, dev
->slave_num
, 0);
468 static void pios_hmc5x83_spi_release_bus(pios_hmc5x83_dev_data_t
*dev
)
470 PIOS_SPI_RC_PinSet(dev
->port_id
, dev
->slave_num
, 1);
471 PIOS_SPI_ReleaseBus(dev
->port_id
);
474 * @brief Reads one or more bytes into a buffer
475 * \param[in] address HMC5x83 register address (depends on size)
476 * \param[out] buffer destination buffer
477 * \param[in] len number of bytes which should be read
478 * \return 0 if operation was successful
479 * \return -1 if error during I2C transfer
480 * \return -2 if unable to claim i2c device
482 int32_t PIOS_HMC5x83_SPI_Read(pios_hmc5x83_dev_t handler
, uint8_t address
, uint8_t *buffer
, uint8_t len
)
484 pios_hmc5x83_dev_data_t
*dev
= dev_validate(handler
);
486 if (pios_hmc5x83_spi_claim_bus(dev
) < 0) {
490 memset(buffer
, 0xA5, len
);
491 PIOS_SPI_TransferByte(dev
->port_id
, address
| PIOS_HMC5x83_SPI_AUTOINCR_FLAG
| PIOS_HMC5x83_SPI_READ_FLAG
);
493 // buffer[0] = address | PIOS_HMC5x83_SPI_AUTOINCR_FLAG | PIOS_HMC5x83_SPI_READ_FLAG;
494 /* Copy the transfer data to the buffer */
495 if (PIOS_SPI_TransferBlock(dev
->port_id
, NULL
, buffer
, len
, NULL
) < 0) {
496 pios_hmc5x83_spi_release_bus(dev
);
499 pios_hmc5x83_spi_release_bus(dev
);
504 * @brief Writes one or more bytes to the HMC5x83
505 * \param[in] address Register address
506 * \param[in] buffer source buffer
507 * \return 0 if operation was successful
508 * \return -1 if error during I2C transfer
509 * \return -2 if unable to claim spi device
511 int32_t PIOS_HMC5x83_SPI_Write(pios_hmc5x83_dev_t handler
, uint8_t address
, uint8_t buffer
)
513 pios_hmc5x83_dev_data_t
*dev
= dev_validate(handler
);
515 if (pios_hmc5x83_spi_claim_bus(dev
) < 0) {
519 address
| PIOS_HMC5x83_SPI_AUTOINCR_FLAG
,
523 if (PIOS_SPI_TransferBlock(dev
->port_id
, data
, NULL
, sizeof(data
), NULL
) < 0) {
524 pios_hmc5x83_spi_release_bus(dev
);
528 pios_hmc5x83_spi_release_bus(dev
);
531 #endif /* PIOS_INCLUDE_SPI */
532 #ifdef PIOS_INCLUDE_I2C
534 int32_t PIOS_HMC5x83_I2C_Read(pios_hmc5x83_dev_t handler
, uint8_t address
, uint8_t *buffer
, uint8_t len
);
535 int32_t PIOS_HMC5x83_I2C_Write(pios_hmc5x83_dev_t handler
, uint8_t address
, uint8_t buffer
);
537 const struct pios_hmc5x83_io_driver PIOS_HMC5x83_I2C_DRIVER
= {
538 .Read
= PIOS_HMC5x83_I2C_Read
,
539 .Write
= PIOS_HMC5x83_I2C_Write
,
543 * @brief Reads one or more bytes into a buffer
544 * \param[in] address HMC5x83 register address (depends on size)
545 * \param[out] buffer destination buffer
546 * \param[in] len number of bytes which should be read
547 * \return 0 if operation was successful
548 * \return -1 if error during I2C transfer
549 * \return -2 if unable to claim i2c device
551 int32_t PIOS_HMC5x83_I2C_Read(pios_hmc5x83_dev_t handler
, uint8_t address
, uint8_t *buffer
, uint8_t len
)
553 pios_hmc5x83_dev_data_t
*dev
= dev_validate(handler
);
554 uint8_t addr_buffer
[] = {
558 const struct pios_i2c_txn txn_list
[] = {
561 .addr
= PIOS_HMC5x83_I2C_ADDR
,
562 .rw
= PIOS_I2C_TXN_WRITE
,
563 .len
= sizeof(addr_buffer
),
569 .addr
= PIOS_HMC5x83_I2C_ADDR
,
570 .rw
= PIOS_I2C_TXN_READ
,
576 return PIOS_I2C_Transfer(dev
->port_id
, txn_list
, NELEMENTS(txn_list
));
580 * @brief Writes one or more bytes to the HMC5x83
581 * \param[in] address Register address
582 * \param[in] buffer source buffer
583 * \return 0 if operation was successful
584 * \return -1 if error during I2C transfer
585 * \return -2 if unable to claim i2c device
587 int32_t PIOS_HMC5x83_I2C_Write(pios_hmc5x83_dev_t handler
, uint8_t address
, uint8_t buffer
)
589 pios_hmc5x83_dev_data_t
*dev
= dev_validate(handler
);
595 const struct pios_i2c_txn txn_list
[] = {
598 .addr
= PIOS_HMC5x83_I2C_ADDR
,
599 .rw
= PIOS_I2C_TXN_WRITE
,
607 return PIOS_I2C_Transfer(dev
->port_id
, txn_list
, NELEMENTS(txn_list
));
609 #endif /* PIOS_INCLUDE_I2C */
611 /* PIOS sensor driver implementation */
612 bool PIOS_HMC5x83_driver_Test(uintptr_t context
)
614 return !PIOS_HMC5x83_Test((pios_hmc5x83_dev_t
)context
);
617 void PIOS_HMC5x83_driver_Reset(__attribute__((unused
)) uintptr_t context
) {}
619 void PIOS_HMC5x83_driver_get_scale(float *scales
, uint8_t size
, __attribute__((unused
)) uintptr_t context
)
621 PIOS_Assert(size
> 0);
625 void PIOS_HMC5x83_driver_fetch(void *data
, uint8_t size
, uintptr_t context
)
627 PIOS_Assert(size
> 0);
629 PIOS_HMC5x83_ReadMag((pios_hmc5x83_dev_t
)context
, mag
);
630 PIOS_SENSORS_3Axis_SensorsWithTemp
*tmp
= data
;
632 tmp
->sample
[0].x
= mag
[0];
633 tmp
->sample
[0].y
= mag
[1];
634 tmp
->sample
[0].z
= mag
[2];
635 tmp
->temperature
= 0;
638 bool PIOS_HMC5x83_driver_poll(uintptr_t context
)
640 return PIOS_HMC5x83_NewDataAvailable((pios_hmc5x83_dev_t
)context
);
643 #endif /* PIOS_INCLUDE_HMC5x83 */