diff --git a/drivers/include/lis3mdl.h b/drivers/include/lis3mdl.h new file mode 100644 index 0000000000..2e763b69dc --- /dev/null +++ b/drivers/include/lis3mdl.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2015 HAW Hamburg + * + * 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 driver_LIS3MDL LIS3MDL 3-axis magnetometer + * @ingroup drivers + * @brief Device driver for the LIS3MDL 3-axis magnetometer + * @{ + * + * @file + * @brief Device driver interface for the LIS3MDL 3-axis magnetometer + * + * @author René Herthel + */ + +#ifndef LIS3MDL_H +#define LIS3MDL_H + +#include +#include "periph/i2c.h" +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief 3d data container of the LIS3MDL sensor + */ +typedef struct { + int16_t x_axis; /**< Magnometer data from x-axis */ + int16_t y_axis; /**< Magnometer data from y_axis */ + int16_t z_axis; /**< Magnometer data from z_axis */ +} lis3mdl_3d_data_t; + +/** + * @brief Device descriptor for LIS3MDL sensor + */ +typedef struct { + i2c_t i2c; /**< I2C device */ + uint8_t addr; /**< Magnometer I2C address */ +} lis3mdl_t; + +/** + * @brief Operating mode of x- and y-axis for LIS3MDL + */ +typedef enum { + LIS3MDL_XY_MODE_LOW = 0x00, /**< Low-power mode */ + LIS3MDL_XY_MODE_MEDIUM = 0x20, /**< Medium-performance mode */ + LIS3MDL_XY_MODE_HIGH = 0x40, /**< High-performance mode */ + LIS3MDL_XY_MODE_ULTRA = 0x60, /**< Ultra-High-performance mode */ +} lis3mdl_xy_mode_t; + +/** + * @brief Operating mode of z-axis for LIS3MDL + */ +typedef enum { + LIS3MDL_Z_MODE_LOW = 0x00, /**< Low-power mode */ + LIS3MDL_Z_MODE_MEDIUM = 0x04, /**< Medium-performance mode */ + LIS3MDL_Z_MODE_HIGH = 0x08, /**< High-performance mode */ + LIS3MDL_Z_MODE_ULTRA = 0x0C, /**< Ultra-High-performance mode */ +} lis3mdl_z_mode_t; + +/** + * @brief Output data rate [Hz] for LIS3MDL + */ +typedef enum { + LIS3MDL_ODR_0_625Hz = 0x00, /**< 0.625Hz */ + LIS3MDL_ODR_1_25Hz = 0x04, /**< 1.250Hz */ + LIS3MDL_ODR_2_5Hz = 0x08, /**< 5.000Hz */ + LIS3MDL_ODR_10Hz = 0x10, /**< 10.000Hz */ + LIS3DML_ODR_20HZ = 0x14, /**< 20.000Hz */ + LIS3DML_ODR_40HZ = 0x18, /**< 40.000Hz */ + LIS3MDL_ODR_80HZ = 0x1C, /**< 80.000Hz */ +} lis3mdl_odr_t; + +/** + * @brief Scale [gauss] for LIS3MDL + */ +typedef enum { + LIS3MDL_SCALE_4G = 0x00, /**< +- 4 gauss */ + LIS3MDL_SCALE_8G = 0x20, /**< +- 8 gauss */ + LIS3MDL_SCALE_12G = 0x40, /**< +- 12 gauss */ + LIS3MDL_SCALE_16G = 0x60, /**< +- 16 gauss */ +} lis3mdl_scale_t; + +/** + * @brief Operating modes + */ +typedef enum { + LIS3MDL_OP_CONT_CONV = 0x00, /**< Continous-conversion mode */ + LIS3MDL_OP_SNGL_CONV = 0x01, /**< Single-conversion mode */ + LIS3MDL_OP_PDOWN = 0x11, /**< Power-down mode */ +} lis3mdl_op_t; + +/** + * @brief Initialize a new LIS3DML device. + * + * @param[in] dev device descriptor of LIS3MDL + * @param[in] i2c I2C device connected to + * @param[in] address I2C address of the magnometer + * @param[in] xy_mode power mode of x- and y-axis + * @param[in] z_mode power mode of z-axis + * @param[in] odr output data rate of magnometer + * @param[in] scale scale configuration of magnometer + * @param[in] op_mode operation mode of the device + * + * @return 0 on success + * @return -1 on error + */ +int lis3mdl_init(lis3mdl_t *dev, i2c_t i2c, uint8_t address, + lis3mdl_xy_mode_t xy_mode, lis3mdl_z_mode_t z_mode, + lis3mdl_odr_t odr, lis3mdl_scale_t scale, + lis3mdl_op_t op_mode); + +/** + * @brief Reads the magnometer value of LIS3MDL. + * + * @param[in] dev device descriptor of LIS3MDL + * @param[in] data measured magnetometer data + */ +void lis3mdl_read_mag(lis3mdl_t *dev, lis3mdl_3d_data_t *data); + +/** + * @brief Reads the temperature value of LIS3MDL. + * + * @param[in] dev device descriptor of LIS3MDL + * @param[in] value measured temperature in degree celsius + */ +void lis3mdl_read_temp(lis3mdl_t *dev, int16_t *value); + +/** + * @brief Enable the LIS3MDL device. + * + * @param[in] dev device descriptor of LIS3MDL + */ +void lis3mdl_enable(lis3mdl_t *dev); + +/** + * @brief Disable the LIS3MDL device. + * + * @param[in] dev device descriptor of LIS3MDL + */ +void lis3mdl_disable(lis3mdl_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* LIS3MDL_H */ +/** @} */ diff --git a/drivers/lis3mdl/Makefile b/drivers/lis3mdl/Makefile new file mode 100644 index 0000000000..06f10e94d8 --- /dev/null +++ b/drivers/lis3mdl/Makefile @@ -0,0 +1,3 @@ +MODULE = lis3mdl + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/lis3mdl/include/lis3mdl-internal.h b/drivers/lis3mdl/include/lis3mdl-internal.h new file mode 100644 index 0000000000..b7deef768c --- /dev/null +++ b/drivers/lis3mdl/include/lis3mdl-internal.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015 HAW Hamburg + * + * 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 driver_lis3mdl + * + * @{ + * + * @file + * @brief Definitions for the LIS3MDL 3-axis magnetometer + * + * @author René Herthel + */ + +#ifndef LIS3MDL_INTERNAL_H +#define LIS3MDL_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LIS3DML chip identification + * @{ + */ +#define LIS3MDL_CHIP_ID (0x3D) +#define LIS3MDL_CHIP_ADDR (0x1C) +/** @} */ + +/** + * @name LIS3DML device identification register + * @{ + */ +#define LIS3DML_WHO_AM_I_REG (0x0F) +/** @} */ + +/** + * @name LIS3DML controll register + * @{ + */ +#define LIS3MDL_CTRL_REG1 (0x20) +#define LIS3MDL_CTRL_REG2 (0x21) +#define LIS3MDL_CTRL_REG3 (0x22) +#define LIS3MDL_CTRL_REG4 (0x23) +#define LIS3MDL_CTRL_REG5 (0x24) +/** @} */ + +/** + * @name LIS3DML masks for CTRL_REG1 + * @{ + */ +#define LIS3MDL_MASK_REG1_TEMP_EN (0x80) +/** @} */ + +/** + * @name LIS3DML masks for CTRL_REG2 + * @{ + */ +#define LIS3MDL_MASK_REG2_REBOOT (0x06) +#define LIS3MDL_MASK_REG2_SOFT_RST (0x04) +/** @} */ + +/** + * @name LIS3DML masks for CTRL_REG3 + * @{ + */ +#define LIS3MDL_MASK_REG3_LOW_POWER_EN (0x02) +#define LIS3MDL_MASK_REG3_CONT_CONV_MODE (0x00) +#define LIS3MDL_MASK_REG3_Z_LOW_POWER (0x00) +#define LIS3MDL_MASK_REG3_Z_MEDIUM_POWER (0x04) +/** @} */ + +/** + * @name LIS3DML masks for CTRL_REG5 + * @{ + */ +#define LIS3MDL_MASK_REG5_BDU (0x40) +#define LIS3MDL_MASK_REG5_BDU_OFF (0x00) +/** @} */ + +/** + * @name LIS3DML status register + * @{ + */ +#define LIS3MDL_STATUS_REG (0x27) +/** @} */ + +/** + * @name LIS3DML magnometer output register + * @{ + */ +#define LIS3MDL_OUT_X_L_REG (0x28) +#define LIS3MDL_OUT_X_H_REG (0x29) +#define LIS3MDL_OUT_Y_L_REG (0x2A) +#define LIS3MDL_OUT_Y_H_REG (0x2B) +#define LIS3MDL_OUT_Z_L_REG (0x2C) +#define LIS3MDL_OUT_Z_H_REG (0x2D) +/** @} */ + +/** + * @name LIS3DML temperature output register + * @{ + */ +#define LIS3MDL_TEMP_OUT_L_REG (0x2E) +#define LIS3MDL_TEMP_OUT_H_REG (0x2F) +/** @} */ + +/** + * @name LIS3DML interrupt register + * @{ + */ +#define LIS3MDL_INT_CFG_REG (0x30) +#define LIS3MDL_INT_SRC_REG (0x31) +#define LIS3MDL_INT_THS_L_REG (0x32) +#define LIS3MDL_INT_THS_H_REG (0x33) +/** @} */ + +/** + * @name LIS3MDL masks for interrupt cfg register + * @{ + */ +#define LIS3MDL_MASK_INT_CFG_XIEN (0x80) +#define LIS3MDL_MASK_INT_CFG_YIEN (0x40) +#define LIS3MDL_MASK_INT_CFG_ZIEN (0x20) +#define LIS3MDL_MASK_INT_CFG_IEA (0x04) +#define LIS3MDL_MASK_INT_CFG_LIR (0x02) +#define LIS3MDL_MASK_INT_CFG_IEN (0x01) +/** }@ */ + +/** + * @name LIS3MDL masks for interrupt src register + * @{ + */ +#define LIS3MDL_MASK_INT_SRC_PTH_X (0x80) +#define LIS3MDL_MASK_INT_SRC_PTH_Y (0x40) +#define LIS3MDL_MASK_INT_SRC_PTH_Z (0x20) +#define LIS3MDL_MASK_INT_SRC_NTH_X (0x10) +#define LIS3MDL_MASK_INT_SRC_NTH_Y (0x08) +#define LIS3MDL_MASK_INT_SRC_NTH_Z (0x04) +#define LIS3MDL_MASK_INT_SRC_MROI (0x02) +#define LIS3MDL_MASK_INT_SRC_INT (0x01) + +#ifdef __cplusplus +} +#endif + +#endif /* LIS3MDL_INTERNAL_H */ +/** @} */ diff --git a/drivers/lis3mdl/lis3mdl.c b/drivers/lis3mdl/lis3mdl.c new file mode 100644 index 0000000000..075d09b387 --- /dev/null +++ b/drivers/lis3mdl/lis3mdl.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 HAW Hamburg + * + * 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 driver_lis3mdl + * @{ + * + * @file + * @brief Device driver implementation for the LIS3MDL 3-axis magnetometer + * + * @author René Herthel + * + * @} + */ + +#include "lis3mdl.h" +#include "include/lis3mdl-internal.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define MASK_INT16_MSB (0x8000) +#define MASK_INT16_NMSB (0x7FFF) + +#define TEMP_DIVIDER (16) +#define TEMP_OFFSET (25) + +#define GAUSS_DIVIDER (1000) + +/** + * @brief Takes an unsigned value representing a two's complement number + * and returns the signed number it represents + * + * @param[in] value value which represents a two's complement number + * + * @return the converted signed number of 'value' + */ + static inline int16_t _twos_complement(int16_t value) +{ + if (value & MASK_INT16_MSB) { + value = ~(value & MASK_INT16_NMSB) + 1; + return ~(value & MASK_INT16_NMSB); + } + else { + return value; + } +} + +int lis3mdl_init(lis3mdl_t *dev, + i2c_t i2c, + uint8_t address, + lis3mdl_xy_mode_t xy_mode, + lis3mdl_z_mode_t z_mode, + lis3mdl_odr_t odr, + lis3mdl_scale_t scale, + lis3mdl_op_t op_mode) { + char tmp; + + dev->i2c = i2c; + dev->addr = address; + + i2c_acquire(dev->i2c); + + if (i2c_init_master(i2c, I2C_SPEED_NORMAL) < 0) { + DEBUG("LIS3MDL: Master initialization failed\n"); + return -1; + } + + i2c_read_reg(dev->i2c, dev->addr, LIS3DML_WHO_AM_I_REG, &tmp); + if (tmp != LIS3MDL_CHIP_ID) { + DEBUG("LIS3MDL: Identification failed\n"); + return -1; + } + + tmp = ( LIS3MDL_MASK_REG1_TEMP_EN /* enable temperature sensor */ + | xy_mode /* set x-, y-axis operative mode */ + | odr); /* set output data rate */ + i2c_write_reg(dev->i2c, dev->addr, LIS3MDL_CTRL_REG1, tmp); + + /* set Full-scale configuration */ + i2c_write_reg(dev->i2c, dev->addr, LIS3MDL_CTRL_REG2, scale); + + /* set continuous-conversion mode */ + i2c_write_reg(dev->i2c, dev->addr, LIS3MDL_CTRL_REG3, op_mode); + + /* set z-axis operative mode */ + i2c_write_reg(dev->i2c, dev->addr, LIS3MDL_CTRL_REG4, z_mode); + + i2c_release(dev->i2c); + + return 0; +} + +void lis3mdl_read_mag(lis3mdl_t *dev, lis3mdl_3d_data_t *data) +{ + char tmp[2] = {0, 0}; + + i2c_acquire(dev->i2c); + + i2c_read_regs(dev->i2c, dev->addr, LIS3MDL_OUT_X_L_REG, &tmp[0], 2); + data->x_axis = (tmp[1] << 8) | tmp[0]; + + i2c_read_regs(dev->i2c, dev->addr, LIS3MDL_OUT_Y_L_REG, &tmp[0], 2); + data->y_axis = (tmp[1] << 8) | tmp[0]; + + i2c_read_regs(dev->i2c, dev->addr, LIS3MDL_OUT_Z_L_REG, &tmp[0], 2); + data->z_axis = (tmp[1] << 8) | tmp[0]; + + data->x_axis = _twos_complement(data->x_axis); + data->y_axis = _twos_complement(data->y_axis); + data->z_axis = _twos_complement(data->z_axis); + + /* Divide the raw data by 1000 to geht [G] := Gauss */ + data->x_axis /= GAUSS_DIVIDER; + data->y_axis /= GAUSS_DIVIDER; + data->z_axis /= GAUSS_DIVIDER; + + i2c_release(dev->i2c); +} + +void lis3mdl_read_temp(lis3mdl_t *dev, int16_t *value) +{ + i2c_acquire(dev->i2c); + i2c_read_regs(dev->i2c, dev->addr, LIS3MDL_TEMP_OUT_L_REG, (char*)value, 2); + i2c_release(dev->i2c); + + *value = _twos_complement(*value); + + *value = (TEMP_OFFSET + (*value / TEMP_DIVIDER)); +} + +void lis3mdl_enable(lis3mdl_t *dev) +{ + i2c_acquire(dev->i2c); + /* Z-axis medium-power mode */ + i2c_write_reg(dev->i2c, dev->addr, + LIS3MDL_CTRL_REG3, LIS3MDL_MASK_REG3_Z_MEDIUM_POWER); + i2c_release(dev->i2c); +} + +void lis3mdl_disable(lis3mdl_t *dev) +{ + char tmp = ( LIS3MDL_MASK_REG3_LOW_POWER_EN /**< enable power-down mode */ + | LIS3MDL_MASK_REG3_Z_LOW_POWER); /**< Z-axis low-power mode */ + + i2c_acquire(dev->i2c); + i2c_write_reg(dev->i2c, dev->addr, LIS3MDL_CTRL_REG3, tmp); + i2c_release(dev->i2c); +} diff --git a/tests/driver_lis3mdl/Makefile b/tests/driver_lis3mdl/Makefile new file mode 100644 index 0000000000..e06d6d3781 --- /dev/null +++ b/tests/driver_lis3mdl/Makefile @@ -0,0 +1,25 @@ +APPLICATION = driver_lis3mdl +include ../Makefile.tests_common + +# only this board is known (yet) to provide the sensor LIS3MDL +BOARD_WHITELIST = limifrog-v1 + +FEATURES_REQUIRED = periph_i2c periph_gpio + +USEMODULE += lis3mdl +USEMODULE += xtimer + +ifneq (,$(TEST_LIS3MDL_I2C)) + CFLAGS += -DTEST_LIS3MDL_I2C=$(TEST_LIS3MDL_I2C) +else + # set random default + CFLAGS += -DTEST_LIS3MDL_I2C=I2C_1 +endif +ifneq (,$(TEST_LIS3MDL_MAG_ADDR)) + CFLAGS += -DTEST_LIS3MDL_MAG_ADDR=$(TEST_LIS3MDL_MAG_ADDR) +else + # set random default 7 bit address + CFLAGS += -DTEST_LIS3MDL_MAG_ADDR=28 +endif + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_lis3mdl/README.md b/tests/driver_lis3mdl/README.md new file mode 100644 index 0000000000..355a58b86a --- /dev/null +++ b/tests/driver_lis3mdl/README.md @@ -0,0 +1,11 @@ +# About +This is a manual test application for the LIS3MDL magnetometer driver. + +# Usage +This test application will initialize the accelerometer with the following parameters: + - Sampling Rate: 10Hz + - Scale: 4G + - Temperature sensor: Enabled + +After initialization, the sensor reads the magnetometer values every 100ms +and prints them to the STDOUT. diff --git a/tests/driver_lis3mdl/main.c b/tests/driver_lis3mdl/main.c new file mode 100644 index 0000000000..f74af6b05e --- /dev/null +++ b/tests/driver_lis3mdl/main.c @@ -0,0 +1,67 @@ + +/* + * Copyright (C) 2015 HAW Hamburg + * + * 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 tests + * @{ + * + * @file + * @brief Test application for the LIS3MDL 3-axis magnetometer + * + * @author René Herthel + * + * @} + */ +#ifndef TEST_LIS3MDL_I2C +#error "TEST_LIS3MDL_I2C not defined" +#endif +#ifndef TEST_LIS3MDL_MAG_ADDR +#error "TEST_LIS3MDL_MAG_ADDR not defined" +#endif + +#include + +#include "xtimer.h" +#include "lis3mdl.h" + +#define SLEEP (800 * 800U) + +int main(void) +{ + lis3mdl_t dev; + lis3mdl_3d_data_t mag_value; + int16_t temp_value = 0; + + puts("\nLIS3MDL test application"); + printf("Initializing LIS3MDL sensor at I2C_%i ... \n", TEST_LIS3MDL_I2C); + + if (lis3mdl_init(&dev, TEST_LIS3MDL_I2C, TEST_LIS3MDL_MAG_ADDR, + LIS3MDL_XY_MODE_MEDIUM, + LIS3MDL_Z_MODE_MEDIUM, LIS3MDL_ODR_10Hz, + LIS3MDL_SCALE_4G, LIS3MDL_OP_CONT_CONV) == 0) { + puts("[ OK ]\n"); + } + else { + puts("[ FAIL ]\n"); + return 1; + } + + while(1){ + lis3mdl_read_mag(&dev, &mag_value); + printf("Magnetometer [G]:\tX: %2d\tY: %2d\tZ: %2d\n", mag_value.x_axis, + mag_value.y_axis, + mag_value.z_axis); + lis3mdl_read_temp(&dev, &temp_value); + printf("Temperature:\t\t%i°C\n", temp_value); + + xtimer_usleep(SLEEP); + } + + return 0; +}