drivers: add support for QMC5883L mag sensors
This commit is contained in:
parent
0e736b8879
commit
ca7578073a
@ -451,6 +451,17 @@ ifneq (,$(filter pulse_counter,$(USEMODULE)))
|
|||||||
FEATURES_REQUIRED += periph_gpio_irq
|
FEATURES_REQUIRED += periph_gpio_irq
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter qmc5883l_int,$(USEMODULE)))
|
||||||
|
USEMODULE += qmc5883l
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter qmc5883l,$(USEMODULE)))
|
||||||
|
FEATURES_REQUIRED += periph_i2c
|
||||||
|
ifneq (,$(filter qmc5883l_int,$(USEMODULE)))
|
||||||
|
FEATURES_REQUIRED += periph_gpio_irq
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter rgbled,$(USEMODULE)))
|
ifneq (,$(filter rgbled,$(USEMODULE)))
|
||||||
USEMODULE += color
|
USEMODULE += color
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -238,6 +238,10 @@ ifneq (,$(filter pulse_counter,$(USEMODULE)))
|
|||||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/pulse_counter/include
|
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/pulse_counter/include
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter qmc5883l,$(USEMODULE)))
|
||||||
|
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/qmc5883l/include
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter rn2xx3,$(USEMODULE)))
|
ifneq (,$(filter rn2xx3,$(USEMODULE)))
|
||||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/rn2xx3/include
|
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/rn2xx3/include
|
||||||
endif
|
endif
|
||||||
|
|||||||
307
drivers/include/qmc5883l.h
Normal file
307
drivers/include/qmc5883l.h
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup drivers_qmc5883l QMC5883L 3-Axis Digital Magnetic Sensor
|
||||||
|
* @ingroup drivers_sensors
|
||||||
|
* @ingroup drivers_saul
|
||||||
|
* @brief Driver for QST QMC5883L digital magnetic sensor
|
||||||
|
*
|
||||||
|
* # About
|
||||||
|
* This module provides a device driver for the QML5883L magnetic sensor
|
||||||
|
* (digital compass) by QST. This device is a successor and similar to the
|
||||||
|
* Honeywell HMC5883L, it does use a different register map and uses a different
|
||||||
|
* style of configuration.
|
||||||
|
*
|
||||||
|
* # Usage
|
||||||
|
* Use the qmc5883l_init() function to initialize your sensor. On exit of the
|
||||||
|
* initialization function, the sensor is put into continuous sampling mode
|
||||||
|
* (power on) mode.
|
||||||
|
*
|
||||||
|
* For reading the sampled data, you have two options: use polling or use
|
||||||
|
* interrupt based notifications.
|
||||||
|
*
|
||||||
|
* ## Polling
|
||||||
|
* You call periodically call qmc5883l_read[_raw]() directly and simply check
|
||||||
|
* the return value to be QMC5883L_OK or QMC5883L_OVERFLOW for valid data.
|
||||||
|
* Alternatively call qmc5883l_data_ready() to explicitly ask the sensor if new
|
||||||
|
* data is available.
|
||||||
|
*
|
||||||
|
* ## Interrupt based
|
||||||
|
* For the interrupt mode to be available, you have to build the driver with the
|
||||||
|
* associated functions using `USEMODULE += qmc5883l_int`.
|
||||||
|
*
|
||||||
|
* To configure and enable interrupt notifications for data ready events use the
|
||||||
|
* qmc5883l_init_int() function. This will setup the configured interrupt pin
|
||||||
|
* (params->pin_drdy) and enable the DRDY pin output for the QMC5883L sensor.
|
||||||
|
*
|
||||||
|
* @warning The DRDY interrupt callback is executed in interrupt context, so
|
||||||
|
* **do not** call any driver API function directly inside the
|
||||||
|
* callback! Instead use some IPC to notify a thread.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief API definition for the QMC5883L device driver
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QMC5883L_H
|
||||||
|
#define QMC5883L_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "periph/gpio.h"
|
||||||
|
#include "periph/i2c.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Static QMC5883L I2C address (can not be changed)
|
||||||
|
*/
|
||||||
|
#define QMC5883L_ADDR (0x0d)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Error codes used by the QMC5883L driver
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
QMC5883L_OK, /**< success */
|
||||||
|
QMC5883L_NODATA, /**< no data are available */
|
||||||
|
QMC5883L_OVERFLOW, /**< at least one axis overflowed its range */
|
||||||
|
QMC5883L_BUSERR, /**< i2c bus error */
|
||||||
|
QMC5883L_NOCFG, /**< configuration error */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Output data rate
|
||||||
|
*
|
||||||
|
* "Output data rate is controlled by ODR registers. Four data update
|
||||||
|
* frequencies can be selected: 10Hz, 50Hz, 100Hz and 200Hz. For most of
|
||||||
|
* compassing applications, we recommend 10 Hz for low power consumption. For
|
||||||
|
* gaming, the high update rate such as 100Hz or 200Hz can be used."
|
||||||
|
* (datasheet V1.0, p.17)
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
QMC5883L_ODR_10HZ = (0u << 2), /**< 10Hz data output rate */
|
||||||
|
QMC5883L_ODR_50HZ = (1u << 2), /**< 50Hz data output rate */
|
||||||
|
QMC5883L_ODR_100HZ = (2u << 2), /**< 100Hz data output rate */
|
||||||
|
QMC5883L_ODR_200HZ = (3u << 2), /**< 200Hz data output rate */
|
||||||
|
} qmc5883l_odr_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Output value range
|
||||||
|
*
|
||||||
|
* "Field ranges of the magnetic sensor can be selected through the register
|
||||||
|
* RNG. The full scale field range is determined by the application
|
||||||
|
* environments. For magnetic clear environment, low field range such as
|
||||||
|
* +/- 2gauss can be used. The field range goes hand in hand with the
|
||||||
|
* sensitivity of the magnetic sensor. The lowest field range has the highest
|
||||||
|
* sensitivity, therefore, higher resolution." (datasheet V1.0, p.17)
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
QMC5883L_RNG_2G = (0u << 4), /**< 2 Gauss data output range */
|
||||||
|
QMC5883L_RNG_8G = (1u << 4), /**< 8 Gauss data output range */
|
||||||
|
} qmc5883l_rng_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Over sample rate (bandwidth of internal digital filter)
|
||||||
|
*
|
||||||
|
* "Over sample Rate (OSR) registers are used to control bandwidth of an
|
||||||
|
* internal digital filter. Larger OSR value leads to smaller filter bandwidth,
|
||||||
|
* less in-band noise and higher power consumption. It could be used to reach a
|
||||||
|
* good balance between noise and power. Four over sample ratio can be
|
||||||
|
* selected, 64, 128, 256 or 512." (datasheet V1.0, p.17)
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
QMC5883L_OSR_512 = (0u << 6), /**< 512 samples per reading */
|
||||||
|
QMC5883L_OSR_256 = (1u << 6), /**< 256 samples per reading */
|
||||||
|
QMC5883L_OSR_128 = (2u << 6), /**< 128 samples per reading */
|
||||||
|
QMC5883L_OSR_64 = (3u << 6), /**< 64 samples per reading */
|
||||||
|
} qmc5883l_osr_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief QMC5883L initialization parameters
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
i2c_t i2c; /**< I2C bus the sensor is connected to */
|
||||||
|
gpio_t pin_drdy; /**< DRDY ISR pin, set to GPIO_UNDEF if unused */
|
||||||
|
qmc5883l_odr_t odr; /**< output data rate */
|
||||||
|
qmc5883l_rng_t rng; /**< output data range */
|
||||||
|
qmc5883l_osr_t osr; /**< oversampling rate */
|
||||||
|
} qmc5883l_params_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief QMC5883L device descriptor
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
i2c_t i2c; /**< I2C bus the sensor is connected to */
|
||||||
|
gpio_t pin_drdy; /**< DRDY interrupt pin */
|
||||||
|
uint8_t cfg; /**< actual applied device configuration */
|
||||||
|
} qmc5883l_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the given QMC5883L magnetic sensor
|
||||||
|
*
|
||||||
|
* @pre dev != NULL
|
||||||
|
* @pre params != NULL
|
||||||
|
*
|
||||||
|
* @param[out] dev QMC5883L device descriptor
|
||||||
|
* @param[in] params configuration parameters
|
||||||
|
*
|
||||||
|
* @return QMC5883L_OK on success
|
||||||
|
* @return QMC5883L_NOCFG on configuration error
|
||||||
|
* @return QMC5883L_BUSERR on any I2C bus error
|
||||||
|
*/
|
||||||
|
int qmc5883l_init(qmc5883l_t *dev, const qmc5883l_params_t *params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if new data is available
|
||||||
|
*
|
||||||
|
* @pre dev != NULL
|
||||||
|
*
|
||||||
|
* @param[in] dev QMC5883L device descriptor
|
||||||
|
*
|
||||||
|
* @return QMC5883L_OK if new data is available
|
||||||
|
* @return QMC5883L_NODATA if no new data is available
|
||||||
|
* @return QMC5883L_BUSERR on any I2C bus error
|
||||||
|
*/
|
||||||
|
int qmc5883l_data_ready(const qmc5883l_t *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read sampled data from the device [converted to milli-Gauss]
|
||||||
|
*
|
||||||
|
* @pre dev != NULL
|
||||||
|
* @pre data_out != NULL
|
||||||
|
*
|
||||||
|
* @param[in] dev QMC5883L device descriptor
|
||||||
|
* @param[out] data_out buffer for holding the resulting vector, **must** be
|
||||||
|
able to hold 3 data items (x, y, z)
|
||||||
|
*
|
||||||
|
* @return QMC5883L_OK on data being written to @p data_out
|
||||||
|
* @return QMC5883L_OVERFLOW on data successfully read, but at least one data
|
||||||
|
item overflowed its data range
|
||||||
|
* @return QMC5883L_NODATA if no new data is available, nothing is written to
|
||||||
|
@p data_out
|
||||||
|
* @return QMC5883L_BUSERR on any I2C bus error
|
||||||
|
*/
|
||||||
|
int qmc5883l_read(const qmc5883l_t *dev, int16_t *data_out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read RAW data from the device
|
||||||
|
*
|
||||||
|
* This function returns the configured data range of 2 or 8 Gauss mapped to
|
||||||
|
* 16-bit signed integers [--32768:32767].
|
||||||
|
*
|
||||||
|
* @pre dev != NULL
|
||||||
|
* @pre data_out != NULL
|
||||||
|
*
|
||||||
|
* @param[in] dev QMC5883L device descriptor
|
||||||
|
* @param[out] data_out buffer for holding the resulting vector, **must** be
|
||||||
|
able to hold 3 data items (x, y, z)
|
||||||
|
*
|
||||||
|
* @return QMC5883L_OK on data being written to @p data_out
|
||||||
|
* @return QMC5883L_OVERFLOW on data successfully read, but at least one data
|
||||||
|
item overflowed its data range
|
||||||
|
* @return QMC5883L_NODATA if no new data is available, nothing is written to
|
||||||
|
@p data_out
|
||||||
|
* @return QMC5883L_BUSERR on any I2C bus error
|
||||||
|
*/
|
||||||
|
int qmc5883l_read_raw(const qmc5883l_t *dev, int16_t *data_out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Power on the sensor (put it into continuous sampling mode)
|
||||||
|
*
|
||||||
|
* @pre dev != NULL
|
||||||
|
*
|
||||||
|
* @param[in] dev QMC5883L device descriptor
|
||||||
|
*
|
||||||
|
* @return QMC5883L_OK on success
|
||||||
|
* @return QMC5883L_BUSERR on any I2C bus error
|
||||||
|
*/
|
||||||
|
int qmc5883l_poweron(const qmc5883l_t *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Power off the sensor (put it into standby mode)
|
||||||
|
*
|
||||||
|
* @pre dev != NULL
|
||||||
|
*
|
||||||
|
* @param[in] dev QMC5883L device descriptor
|
||||||
|
*
|
||||||
|
* @return QMC5883L_OK on success
|
||||||
|
* @return QMC5883L_BUSERR on any I2C bus error
|
||||||
|
*/
|
||||||
|
int qmc5883l_poweroff(const qmc5883l_t *dev);
|
||||||
|
|
||||||
|
#if defined(MODULE_QMC5883L_INT) || defined(DOXYGEN)
|
||||||
|
/**
|
||||||
|
* @brief Initialize data ready (DRDY) interrupt notifications
|
||||||
|
*
|
||||||
|
* After this function is called the DRDY interrupt is enabled, so there is no
|
||||||
|
* need to call qmc5883l_irq_enable() afterwards.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configure and enable the data ready (DRDY) interrupt
|
||||||
|
*
|
||||||
|
* This function sets up the configured GPIO pin to trigger the given callback
|
||||||
|
* for rising edges and it enables the interrupt signal generation for the
|
||||||
|
* given QMC5883L sensor.
|
||||||
|
*
|
||||||
|
* @warning The given callback function is executed in interrupt context. Make
|
||||||
|
sure not to call any driver API function in that context!
|
||||||
|
*
|
||||||
|
* @pre dev != NULL
|
||||||
|
* @pre cb != NULL
|
||||||
|
*
|
||||||
|
* @param[in] dev QMC583L device descriptor
|
||||||
|
* @param[in] cb callback function triggered on DRDY events
|
||||||
|
* @param[in] arg optional user argument passed to @p cb
|
||||||
|
*
|
||||||
|
* @return QMC5883L_OK on success
|
||||||
|
* @return QMC5883L_NOCFG on GPIO configuration errors
|
||||||
|
* @return QMC5883L_BUSERR on any I2C bus error
|
||||||
|
*/
|
||||||
|
int qmc5883l_init_int(const qmc5883l_t *dev, gpio_cb_t cb, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable the data ready (DRDY) interrupt
|
||||||
|
*
|
||||||
|
* @note Call this function only after you have configured the DRDY interrupt
|
||||||
|
*
|
||||||
|
* @pre dev != NULL
|
||||||
|
*
|
||||||
|
* @param[in] dev QMC5883L device descriptor
|
||||||
|
*
|
||||||
|
* @return QMC5883L_OK on success
|
||||||
|
* @return QMC5883L_BUSERR on any I2C bus error
|
||||||
|
*/
|
||||||
|
int qmc5883l_irq_enable(const qmc5883l_t *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disable the data ready (DRDY) interrupt
|
||||||
|
*
|
||||||
|
* @pre dev != NULL
|
||||||
|
*
|
||||||
|
* @param[in] dev QMC5883L device descriptor
|
||||||
|
*
|
||||||
|
* @return QMC5883L_OK on success
|
||||||
|
* @return QMC5883L_BUSERR on any I2C bus error
|
||||||
|
*/
|
||||||
|
int qmc5883l_irq_disable(const qmc5883l_t *dev);
|
||||||
|
#endif /* defined(MODULE_QMC5883L_INT) || defined(DOXYGEN) */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* QMC5883L_H */
|
||||||
|
/** @} */
|
||||||
1
drivers/qmc5883l/Makefile
Normal file
1
drivers/qmc5883l/Makefile
Normal file
@ -0,0 +1 @@
|
|||||||
|
include $(RIOTBASE)/Makefile.base
|
||||||
84
drivers/qmc5883l/include/qmc5883l_params.h
Normal file
84
drivers/qmc5883l/include/qmc5883l_params.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup drivers_qmc5883l
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
* @file
|
||||||
|
* @brief Default configuration for QMC5883L devices
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QMC5883L_PARAMS_H
|
||||||
|
#define QMC5883L_PARAMS_H
|
||||||
|
|
||||||
|
#include "saul_reg.h"
|
||||||
|
#include "board.h"
|
||||||
|
#include "qmc5883l.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Set default configuration parameters for QMC5883L devices
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#ifndef QMC5883L_PARAM_I2C
|
||||||
|
#define QMC5883L_PARAM_I2C I2C_DEV(0)
|
||||||
|
#endif
|
||||||
|
#ifndef QMC5883L_PARAM_PIN_DRDY
|
||||||
|
#define QMC5883L_PARAM_PIN_DRDY (GPIO_UNDEF)
|
||||||
|
#endif
|
||||||
|
#ifndef QMC5883L_PARAM_ODR
|
||||||
|
#define QMC5883L_PARAM_ODR (QMC5883L_ODR_10HZ)
|
||||||
|
#endif
|
||||||
|
#ifndef QMC5883L_PARAM_RNG
|
||||||
|
#define QMC5883L_PARAM_RNG (QMC5883L_RNG_2G)
|
||||||
|
#endif
|
||||||
|
#ifndef QMC5883L_PARAM_OSR
|
||||||
|
#define QMC5883L_PARAM_OSR (QMC5883L_OSR_64)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef QMC5883L_PARAMS
|
||||||
|
#define QMC5883L_PARAMS { .i2c = QMC5883L_PARAM_I2C, \
|
||||||
|
.pin_drdy = QMC5883L_PARAM_PIN_DRDY, \
|
||||||
|
.odr = QMC5883L_PARAM_ODR, \
|
||||||
|
.rng = QMC5883L_PARAM_RNG, \
|
||||||
|
.osr = QMC5883L_PARAM_OSR }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef QMC5883L_SAUL_INFO
|
||||||
|
#define QMC5883L_SAUL_INFO { .name = "qmc5883l" }
|
||||||
|
#endif
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief QMC5883L configuration
|
||||||
|
*/
|
||||||
|
static const qmc5883l_params_t qmc5883l_params[] =
|
||||||
|
{
|
||||||
|
QMC5883L_PARAMS
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Additional meta information to keep in the SAUL registry
|
||||||
|
*/
|
||||||
|
static const saul_reg_info_t qmc5883l_saul_info[] =
|
||||||
|
{
|
||||||
|
QMC5883L_SAUL_INFO
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* QMC5883L_PARAMS_H */
|
||||||
|
/** @} */
|
||||||
203
drivers/qmc5883l/qmc5883l.c
Normal file
203
drivers/qmc5883l/qmc5883l.c
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||||
|
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup drivers_qmc5883l
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Implementation of the QMC5883L device driver
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "assert.h"
|
||||||
|
#include "periph/gpio.h"
|
||||||
|
#include "qmc5883l.h"
|
||||||
|
#include "qmc5883l_internal.h"
|
||||||
|
|
||||||
|
/* shortcut to the i2c device address */
|
||||||
|
#define ADDR QMC5883L_ADDR
|
||||||
|
|
||||||
|
static int _reg_read(const qmc5883l_t *dev, uint8_t reg,
|
||||||
|
uint8_t *val, int acquire, int release)
|
||||||
|
{
|
||||||
|
if (acquire) {
|
||||||
|
if (i2c_acquire(dev->i2c) != 0) {
|
||||||
|
return QMC5883L_BUSERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int res = i2c_read_reg(dev->i2c, ADDR, reg, val, 0);
|
||||||
|
if ((release) || (res != 0)) {
|
||||||
|
i2c_release(dev->i2c);
|
||||||
|
}
|
||||||
|
return (res == 0) ? QMC5883L_OK : QMC5883L_BUSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _reg_write(const qmc5883l_t *dev, uint8_t reg,
|
||||||
|
uint8_t val, int acquire, int release)
|
||||||
|
{
|
||||||
|
if (acquire) {
|
||||||
|
if (i2c_acquire(dev->i2c) != 0) {
|
||||||
|
return QMC5883L_BUSERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int res = i2c_write_reg(dev->i2c, ADDR, reg, val, 0);
|
||||||
|
if ((release) || (res != 0)) {
|
||||||
|
i2c_release(dev->i2c);
|
||||||
|
}
|
||||||
|
return (res == 0) ? QMC5883L_OK : QMC5883L_BUSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qmc5883l_init(qmc5883l_t *dev, const qmc5883l_params_t *params)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
assert(params);
|
||||||
|
int res;
|
||||||
|
|
||||||
|
dev->i2c = params->i2c;
|
||||||
|
dev->pin_drdy = params->pin_drdy;
|
||||||
|
dev->cfg = (params->odr | params->rng | params->osr | QMC5883L_CONT);
|
||||||
|
|
||||||
|
/* lets start with a soft reset */
|
||||||
|
res = _reg_write(dev, QMC5883L_CTRL2, QMC5883L_SOFT_RST, 1, 0);
|
||||||
|
if (res != QMC5883L_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify the reset by reading the status register, should be all zeros */
|
||||||
|
uint8_t tmp;
|
||||||
|
if (i2c_read_reg(dev->i2c, ADDR, QMC5883L_STATUS, &tmp, 0) != 0) {
|
||||||
|
i2c_release(dev->i2c);
|
||||||
|
return QMC5883L_BUSERR;
|
||||||
|
}
|
||||||
|
if (tmp != 0) {
|
||||||
|
i2c_release(dev->i2c);
|
||||||
|
return QMC5883L_NOCFG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write the actual device configuration */
|
||||||
|
res = _reg_write(dev, QMC5883L_SETRESET, 0x01, 0, 0);
|
||||||
|
if (res != QMC5883L_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
res = _reg_write(dev, QMC5883L_CTRL2, QMC5883L_INT_ENB, 0, 0);
|
||||||
|
if (res != QMC5883L_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return _reg_write(dev, QMC5883L_CTRL1, (dev->cfg | QMC5883L_CONT), 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int qmc5883l_data_ready(const qmc5883l_t *dev)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
uint8_t status;
|
||||||
|
|
||||||
|
int res = _reg_read(dev, QMC5883L_STATUS, &status, 1, 1);
|
||||||
|
if (res != QMC5883L_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return (status & QMC5883L_DRDY) ? QMC5883L_OK : QMC5883L_NODATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qmc5883l_read(const qmc5883l_t *dev, int16_t *data_out)
|
||||||
|
{
|
||||||
|
assert(data_out);
|
||||||
|
int16_t tmp[3];
|
||||||
|
|
||||||
|
int res = qmc5883l_read_raw(dev, tmp);
|
||||||
|
if ((res == QMC5883L_OK) || (res == QMC5883L_OVERFLOW)) {
|
||||||
|
uint16_t scale = (dev->cfg & QMC5883L_RNG_8G) ? 3 : 12;
|
||||||
|
for (unsigned i = 0; i < 3; i++) {
|
||||||
|
data_out[i] = tmp[i] / scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qmc5883l_read_raw(const qmc5883l_t *dev, int16_t *data_out)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
assert(data_out);
|
||||||
|
|
||||||
|
int res;
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t tmp[6];
|
||||||
|
|
||||||
|
res = _reg_read(dev, QMC5883L_STATUS, &status, 1, 0);
|
||||||
|
if (res != QMC5883L_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (!(status & QMC5883L_DRDY)) {
|
||||||
|
res = QMC5883L_NODATA;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (status & QMC5883L_OVL) {
|
||||||
|
res = QMC5883L_OVERFLOW;
|
||||||
|
}
|
||||||
|
if (i2c_read_regs(dev->i2c, ADDR, QMC5883L_DOXL, tmp, 6, 0) != 0) {
|
||||||
|
res = QMC5883L_BUSERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert data to host byte order */
|
||||||
|
data_out[0] = (int16_t)tmp[1] << 8 | tmp[0];
|
||||||
|
data_out[1] = (int16_t)tmp[3] << 8 | tmp[2];
|
||||||
|
data_out[2] = (int16_t)tmp[5] << 8 | tmp[4];
|
||||||
|
|
||||||
|
done:
|
||||||
|
i2c_release(dev->i2c);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qmc5883l_poweron(const qmc5883l_t *dev)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
return _reg_write(dev, QMC5883L_CTRL1, dev->cfg, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int qmc5883l_poweroff(const qmc5883l_t *dev)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
return _reg_write(dev, QMC5883L_CTRL1, 0, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MODULE_QMC5883L_INT
|
||||||
|
int qmc5883l_init_int(const qmc5883l_t *dev, gpio_cb_t cb, void *arg)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
assert(cb);
|
||||||
|
|
||||||
|
if (dev->pin_drdy == GPIO_UNDEF) {
|
||||||
|
return QMC5883L_NOCFG;
|
||||||
|
}
|
||||||
|
if (gpio_init_int(dev->pin_drdy, GPIO_IN, GPIO_RISING, cb, arg) != 0) {
|
||||||
|
return QMC5883L_NOCFG;
|
||||||
|
}
|
||||||
|
return _reg_write(dev, QMC5883L_CTRL2, 0, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int qmc5883l_irq_enable(const qmc5883l_t *dev)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
gpio_irq_enable(dev->pin_drdy);
|
||||||
|
return _reg_write(dev, QMC5883L_CTRL2, 0, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int qmc5883l_irq_disable(const qmc5883l_t *dev)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
gpio_irq_disable(dev->pin_drdy);
|
||||||
|
return _reg_write(dev, QMC5883L_CTRL2, QMC5883L_INT_ENB, 1, 1);
|
||||||
|
}
|
||||||
|
#endif /* MODULE_QMC5883L_INT */
|
||||||
76
drivers/qmc5883l/qmc5883l_internal.h
Normal file
76
drivers/qmc5883l/qmc5883l_internal.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup drivers_qmc5883l
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Register definitions for the QMC5883L device driver
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QMC5883L_INTERNAL_H
|
||||||
|
#define QMC5883L_INTERNAL_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Register map
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define QMC5883L_DOXL (0x00)
|
||||||
|
#define QMC5883L_DOXH (0x01)
|
||||||
|
#define QMC5883L_DOYL (0x02)
|
||||||
|
#define QMC5883L_DOYH (0x03)
|
||||||
|
#define QMC5883L_DOZL (0x04)
|
||||||
|
#define QMC5883L_DOZH (0x05)
|
||||||
|
#define QMC5883L_STATUS (0x06)
|
||||||
|
#define QMC5883L_TOUTL (0x07)
|
||||||
|
#define QMC5883L_TOUTH (0x08)
|
||||||
|
#define QMC5883L_CTRL1 (0x09)
|
||||||
|
#define QMC5883L_CTRL2 (0x0a)
|
||||||
|
#define QMC5883L_SETRESET (0x0b)
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Device modes
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define QMC5883L_STANDBY (0x00)
|
||||||
|
#define QMC5883L_CONT (0x01)
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Device status flags
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define QMC5883L_DRDY (0x01)
|
||||||
|
#define QMC5883L_OVL (0x02)
|
||||||
|
#define QMC5883L_DOR (0x04)
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Configuration bitfields
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define QMC5883L_INT_ENB (0x01)
|
||||||
|
#define QMC5883L_ROL_PNT (0x40)
|
||||||
|
#define QMC5883L_SOFT_RST (0x80)
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* QMC5883L_INTERNAL_H */
|
||||||
|
/** @} */
|
||||||
42
drivers/qmc5883l/qmc5883l_saul.c
Normal file
42
drivers/qmc5883l/qmc5883l_saul.c
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||||
|
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup drivers_qmc5883l
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief SAUL mapping for the QMC5883L sensor driver
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "saul.h"
|
||||||
|
#include "qmc5883l.h"
|
||||||
|
|
||||||
|
static int read(const void *dev, phydat_t *res)
|
||||||
|
{
|
||||||
|
int ret = qmc5883l_read((const qmc5883l_t *)dev, res->val);
|
||||||
|
if ((ret == QMC5883L_OK) || (ret == QMC5883L_OVERFLOW)) {
|
||||||
|
res->unit = UNIT_GS;
|
||||||
|
res->scale = -3;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
return -ECANCELED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const saul_driver_t qmc5883l_saul_driver = {
|
||||||
|
.read = read,
|
||||||
|
.write = saul_notsup,
|
||||||
|
.type = SAUL_SENSE_MAG,
|
||||||
|
};
|
||||||
@ -68,6 +68,7 @@ PSEUDOMODULES += posix_headers
|
|||||||
PSEUDOMODULES += printf_float
|
PSEUDOMODULES += printf_float
|
||||||
PSEUDOMODULES += prng
|
PSEUDOMODULES += prng
|
||||||
PSEUDOMODULES += prng_%
|
PSEUDOMODULES += prng_%
|
||||||
|
PSEUDOMODULES += qmc5883l_int
|
||||||
PSEUDOMODULES += riotboot_%
|
PSEUDOMODULES += riotboot_%
|
||||||
PSEUDOMODULES += saul_adc
|
PSEUDOMODULES += saul_adc
|
||||||
PSEUDOMODULES += saul_default
|
PSEUDOMODULES += saul_default
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user