Browse Source

lis3dh: New device driver for LIS3DH accelerometer sensors.

LIS3DH MEMS digital output motion sensor ultra low-power high
performance 3-axes "nano" accelerometer

Manufacturer website:

  http://www.st.com/web/catalog/sense_power/FM89/SC444/PF250725

Data sheet:

  http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00274221.pdf

tests/driver_lis3dh is a simple test application for testing on actual
hardware.

Signed-off-by: Joakim Gebart <joakim.gebart@eistec.se>
dev/timer
Joakim Gebart 8 years ago
parent
commit
ca56de8251
  1. 846
      drivers/include/lis3dh.h
  2. 3
      drivers/lis3dh/Makefile
  3. 265
      drivers/lis3dh/lis3dh.c
  4. 34
      tests/driver_lis3dh/Makefile
  5. 11
      tests/driver_lis3dh/README.md
  6. 157
      tests/driver_lis3dh/main.c

846
drivers/include/lis3dh.h

@ -0,0 +1,846 @@
/*
* Copyright (C) 2015 Eistec AB
*
* 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_lis3dh LIS3DH accelerometer
* @ingroup drivers
* @brief Device driver for the LIS3DH accelerometer
* @{
*
* @file
* @brief Device driver interface for the LIS3DH accelerometer
*
*
* @author Joakim Gebart <joakim.gebart@eistec.se
*/
#ifndef LIS3DH_H_
#define LIS3DH_H_
#include <stdint.h>
#include "periph/spi.h"
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* The WHO_AM_I register should contain this value in order to correctly
* identify the chip.
*/
#define LIS3DH_WHO_AM_I_RESPONSE (0b00110011)
/**
* @brief LIS3DH hardware register addresses
*/
typedef enum {
LIS3DH_REG_STATUS_AUX = 0x07,
LIS3DH_REG_OUT_AUX_ADC1_L = 0x08,
LIS3DH_REG_OUT_AUX_ADC1_H = 0x09,
LIS3DH_REG_OUT_AUX_ADC2_L = 0x0A,
LIS3DH_REG_OUT_AUX_ADC2_H = 0x0B,
LIS3DH_REG_OUT_AUX_ADC3_L = 0x0C,
LIS3DH_REG_OUT_AUX_ADC3_H = 0x0D,
LIS3DH_REG_INT_COUNTER_REG = 0x0E,
LIS3DH_REG_WHO_AM_I = 0x0F,
LIS3DH_REG_TEMP_CFG_REG = 0x1F,
LIS3DH_REG_CTRL_REG1 = 0x20,
LIS3DH_REG_CTRL_REG2 = 0x21,
LIS3DH_REG_CTRL_REG3 = 0x22,
LIS3DH_REG_CTRL_REG4 = 0x23,
LIS3DH_REG_CTRL_REG5 = 0x24,
LIS3DH_REG_CTRL_REG6 = 0x25,
LIS3DH_REG_REFERENCE = 0x26,
LIS3DH_REG_STATUS_REG = 0x27,
LIS3DH_REG_OUT_X_L = 0x28,
LIS3DH_REG_OUT_X_H = 0x29,
LIS3DH_REG_OUT_Y_L = 0x2A,
LIS3DH_REG_OUT_Y_H = 0x2B,
LIS3DH_REG_OUT_Z_L = 0x2C,
LIS3DH_REG_OUT_Z_H = 0x2D,
LIS3DH_REG_FIFO_CTRL_REG = 0x2E,
LIS3DH_REG_FIFO_SRC_REG = 0x2F,
LIS3DH_REG_INT1_CFG = 0x30,
LIS3DH_REG_INT1_SOURCE = 0x31,
LIS3DH_REG_INT1_THS = 0x32,
LIS3DH_REG_INT1_DURATION = 0x33,
LIS3DH_REG_CLICK_CFG = 0x38,
LIS3DH_REG_CLICK_SRC = 0x39,
LIS3DH_REG_CLICK_THS = 0x3A,
LIS3DH_REG_TIME_LIMIT = 0x3B,
LIS3DH_REG_TIME_LATENCY = 0x3C,
LIS3DH_REG_TIME_WINDOW = 0x3D,
} lis3dh_reg_t;
/*
* Bit offsets within the individual registers
* source: LIS3DH datasheet
*/
/**
* @name TEMP_CFG_REG bitfield macros
*/
/** @{ */
/**
* @brief ADC enable
*
* Default value: 0
*
* 0: ADC disabled; 1: ADC enabled
*/
#define LIS3DH_TEMP_CFG_REG_ADC_PD_MASK (1 << 7)
/**
* @brief Temperature sensor (T) enable.
*
* Default value: 0
*
* 0: T disabled; 1: T enabled
*/
#define LIS3DH_TEMP_CFG_REG_TEMP_EN_MASK (1 << 6)
/** @} */
/**
* @name CTRL_REG1 bitfield macros
*/
/** @{ */
/**
* @name Output data rate selection bitfield macros
*
* Default value: 0000
*
* 0000: Power down; Others: Refer to data sheet
*
* @see LIS3DH data sheet Table 25, Data rate configuration
*/
/** @{ */
#define LIS3DH_CTRL_REG1_ODR_SHIFT (4)
#define LIS3DH_CTRL_REG1_ODR3_MASK (1 << (LIS3DH_CTRL_REG1_ODR_SHIFT + 3))
#define LIS3DH_CTRL_REG1_ODR2_MASK (1 << (LIS3DH_CTRL_REG1_ODR_SHIFT + 2))
#define LIS3DH_CTRL_REG1_ODR1_MASK (1 << (LIS3DH_CTRL_REG1_ODR_SHIFT + 1))
#define LIS3DH_CTRL_REG1_ODR0_MASK (1 << LIS3DH_CTRL_REG1_ODR_SHIFT)
#define LIS3DH_CTRL_REG1_ODR_MASK (LIS3DH_CTRL_REG1_ODR3_MASK | \
LIS3DH_CTRL_REG1_ODR2_MASK | \
LIS3DH_CTRL_REG1_ODR1_MASK | \
LIS3DH_CTRL_REG1_ODR0_MASK)
/** @} */
/**
* @brief Low power mode enable.
*
* Default value: 0
*
* 0. normal mode
* 1. low power mode
*/
#define LIS3DH_CTRL_REG1_LPEN_MASK (1 << 3)
/** @brief Z enable bit offset */
#define LIS3DH_CTRL_REG1_ZEN_SHIFT (2)
/**
* @brief Z axis enable.
*
* Default value: 1
*
* 0. Z axis disabled
* 1. Z axis enabled
*/
#define LIS3DH_CTRL_REG1_ZEN_MASK (1 << LIS3DH_CTRL_REG1_ZEN_SHIFT)
/** @brief Y enable bit offset */
#define LIS3DH_CTRL_REG1_YEN_SHIFT (1)
/**
* @brief Y axis enable.
*
* Default value: 1
*
* 0. Y axis disabled
* 1. Y axis enabled
*/
#define LIS3DH_CTRL_REG1_YEN_MASK (1 << LIS3DH_CTRL_REG1_YEN_SHIFT)
/** @brief X enable bit offset */
#define LIS3DH_CTRL_REG1_XEN_SHIFT (0)
/**
* @brief X axis enable.
*
* Default value: 1
*
* 0. X axis disabled
* 1. X axis enabled
*/
#define LIS3DH_CTRL_REG1_XEN_MASK (1 << LIS3DH_CTRL_REG1_XEN_SHIFT)
/** @brief XYZ enable bitfield offset */
#define LIS3DH_CTRL_REG1_XYZEN_SHIFT (0)
/** @brief X, Y, Z enable bitfield mask */
#define LIS3DH_CTRL_REG1_XYZEN_MASK (LIS3DH_CTRL_REG1_XEN_MASK | \
LIS3DH_CTRL_REG1_YEN_MASK | LIS3DH_CTRL_REG1_ZEN_MASK)
/** @} */
/**
* @name Axis selection macros
*
* Use these when calling lis3dh_set_axes()
*/
/** @{ */
#define LIS3DH_AXES_X (LIS3DH_CTRL_REG1_XEN_MASK) /**< enable X axis */
#define LIS3DH_AXES_Y (LIS3DH_CTRL_REG1_YEN_MASK) /**< enable Y axis */
#define LIS3DH_AXES_Z (LIS3DH_CTRL_REG1_ZEN_MASK) /**< enable Z axis */
/**
* @brief Convenience macro for enabling all axes.
*/
#define LIS3DH_AXES_XYZ (LIS3DH_CTRL_REG1_XYZEN_MASK)
/** @} */
/**
* @name CTRL_REG2 bitfield macros
*/
/** @{ */
/**
* @name High pass filter mode selection
*
* Default value: 00
*
* @see Refer to Table 29, "High pass filter mode configuration"
*/
/** @{ */
#define LIS3DH_CTRL_REG2_HPM1_MASK (1 << 7)
#define LIS3DH_CTRL_REG2_HPM0_MASK (1 << 6)
/** @} */
/**
* @name High pass filter cut off frequency selection
*/
/** @{ */
#define LIS3DH_CTRL_REG2_HPCF2_MASK (1 << 5)
#define LIS3DH_CTRL_REG2_HPCF1_MASK (1 << 4)
/** @} */
/**
* @brief Filtered data selection
*
* Default value: 0
*
* 0. internal filter bypassed
* 1. data from internal filter sent to output register and FIFO
*/
#define LIS3DH_CTRL_REG2_FDS_MASK (1 << 3)
/**
* @brief High pass filter enabled for CLICK function.
*
* 0. filter bypassed
* 1. filter enabled@
*/
#define LIS3DH_CTRL_REG2_HPCLICK_MASK (1 << 2)
/**
* @name High pass filter enabled for AOI function on interrupt 2
*
* 0. filter bypassed
* 1. filter enabled
*/
/** @{ */
#define LIS3DH_CTRL_REG2_HPIS2_MASK (1 << 1)
#define LIS3DH_CTRL_REG2_HPIS1_MASK (1 << 0)
/** @} */
/** @} */
/**
* @name CTRL_REG3 bitfield macros
*/
/** @{ */
/**
* @brief CLICK interrupt on INT1
*
* Default value 0.
*
* 0. Disable
* 1. Enable
*/
#define LIS3DH_CTRL_REG3_I1_CLICK_MASK (1 << 7)
/**
* @brief AOI1 interrupt on INT1
*
* Default value 0.
*
* 0. Disable
* 1. Enable
*/
#define LIS3DH_CTRL_REG3_I1_AOI1_MASK (1 << 6)
/**
* @brief AOI2 interrupt on INT1.
*
* Default value 0.
*
* 0. Disable
* 1. Enable
*/
#define LIS3DH_CTRL_REG3_I1_AOI2_MASK (1 << 5)
/**
* @brief DRDY1 interrupt on INT1
*
* Default value 0.
*
* 0. Disable
* 1. Enable
*/
#define LIS3DH_CTRL_REG3_I1_DRDY1_MASK (1 << 4)
/**
* @brief DRDY2 interrupt on INT1
*
* Default value 0.
*
* 0. Disable
* 1. Enable
*/
#define LIS3DH_CTRL_REG3_I1_DRDY2_MASK (1 << 3)
/**
* @brief FIFO Watermark interrupt on INT1
*
* Default value 0.
*
* 0. Disable
* 1. Enable
*/
#define LIS3DH_CTRL_REG3_I1_WTM_MASK (1 << 2)
/**
* @brief FIFO Overrun interrupt on INT1
*
* Default value 0.
*
* 0. Disable
* 1. Enable
*/
#define LIS3DH_CTRL_REG3_I1_OVERRUN_MASK (1 << 1)
/** @} */
/**
* @name CTRL_REG4 bitfield macros
*/
/** @{ */
/**
* @name Block data update.
*
* Default value: 0
*
* 0. continuous update
* 1. output registers not updated until MSB and LSB reading
*/
/** @{ */
/** @brief BDU bit mask */
#define LIS3DH_CTRL_REG4_BDU_MASK (1 << 7)
/** @brief BDU enable */
#define LIS3DH_CTRL_REG4_BDU_ENABLE (LIS3DH_CTRL_REG4_BDU_MASK)
/** @brief BDU disable */
#define LIS3DH_CTRL_REG4_BDU_DISABLE (0)
/** @} */
/**
* @name Big/little endian data selection
*
* Default value 0.
*
* 0. Data LSB @ lower address
* 1. Data MSB @ lower address
*/
/** @{ */
/** @brief BLE bit mask */
#define LIS3DH_CTRL_REG4_BLE_MASK (1 << 6)
/** @brief BLE little endian mode */
#define LIS3DH_CTRL_REG4_BLE_LITTLE_ENDIAN (0)
/** @brief BLE big endian mode */
#define LIS3DH_CTRL_REG4_BLE_BIG_ENDIAN (LIS3DH_CTRL_REG4_BLE_MASK)
/** @} */
/**
* @name Full scale selection.
*
* Default value: 00
*
* - 00: +/- 2G
* - 01: +/- 4G
* - 10: +/- 8G
* - 11: +/- 16G
*/
/** @{ */
#define LIS3DH_CTRL_REG4_FS1_MASK (1 << 5)
#define LIS3DH_CTRL_REG4_FS0_MASK (1 << 4)
#define LIS3DH_CTRL_REG4_FS_MASK (LIS3DH_CTRL_REG4_FS1_MASK | LIS3DH_CTRL_REG4_FS0_MASK)
/** @} */
/**
* @brief Scale parameters
*
* Use these names when calling lis3dh_set_scale()
*/
typedef enum {
LIS3DH_SCALE_2G = (0), /**< Scale: +/- 2G */
LIS3DH_SCALE_4G = (LIS3DH_CTRL_REG4_FS0_MASK), /**< Scale: +/- 4G */
LIS3DH_SCALE_8G = (LIS3DH_CTRL_REG4_FS1_MASK), /**< Scale: +/- 8G */
/** Scale: +/- 16G */
LIS3DH_SCALE_16G = (LIS3DH_CTRL_REG4_FS1_MASK | LIS3DH_CTRL_REG4_FS0_MASK)
} lis3dh_scale_t;
/**
* @brief High resolution output mode
*
* Default value: 0
*
* 0. High resolution disable
* 1. High resolution enable
*/
#define LIS3DH_CTRL_REG4_HR_MASK (1 << 3)
/**
* @name Self test enable
*
* Default value: 00
*
* - 00: Self test disabled
* - Other: See Table 34
*
* @see Table 34
*/
/** @{ */
#define LIS3DH_CTRL_REG4_ST1_MASK (1 << 2)
#define LIS3DH_CTRL_REG4_ST0_MASK (1 << 1)
/** @} */
/**
* @brief SPI serial interface mode selection
*
* Default value: 0
*
* 0. 4-wire interface
* 1. 3-wire interface
*/
#define LIS3DH_CTRL_REG4_SIM_MASK (1 << 0)
/**
* @brief Reboot memory content
*
* Default value: 0
*
* 0. normal mode
* 1. reboot memory content
*/
#define LIS3DH_CTRL_REG5_REBOOT_MASK (1 << 7)
/**
* @brief FIFO enable
*
* Default value: 0
*
* 0. FIFO disable
* 1. FIFO enable
*/
#define LIS3DH_CTRL_REG5_FIFO_EN_MASK (1 << 6)
/**
* @brief Latch interrupt request on INT1
*
* Latch interrupt request on INT1_SRC register, with INT1_SRC register
* cleared by reading INT1_SRC itself.
*
* Default value: 0
*
* 0. interrupt request not latched
* 1. interrupt request latched
*/
#define LIS3DH_CTRL_REG5_LIR_I1_MASK (1 << 3)
/**
* @brief 4D enable
*
* 4D detection is enabled on INT1 when 6D bit on INT1_CFG is set to 1.
*/
#define LIS3DH_CTRL_REG5_D4D_I1_MASK (1 << 2)
/** @} */
/**
* @name STATUS_REG bitfield macros
*/
/** @{ */
/**
* @brief X, Y or Z axis data overrun
*
* Default value: 0
*
* 0. no overrun has occurred
* 1. a new set of data has overwritten the previous ones
*/
#define LIS3DH_STATUS_REG_ZYXOR_MASK (1 << 7)
/**
* @brief Z axis data overrun
*
* Default value: 0
*
* 0. no overrun has occurred
* 1. a new data for the Z-axis has overwritten the previous one
*/
#define LIS3DH_STATUS_REG_ZOR_MASK (1 << 6)
/**
* @brief Y axis data overrun
*
* Default value: 0
*
* 0. no overrun has occurred
* 1. a new data for the Y-axis has overwritten the previous one
*/
#define LIS3DH_STATUS_REG_YOR_MASK (1 << 5)
/**
* @brief X axis data overrun
*
* Default value: 0
*
* 0. no overrun has occurred
* 1. a new data for the X-axis has overwritten the previous one
*/
#define LIS3DH_STATUS_REG_XOR_MASK (1 << 4)
/**
* @brief X, Y or Z axis new data available
*
* Default value: 0
*
* 0. a new set of data is not yet available
* 1. a new set of data is available
*/
#define LIS3DH_STATUS_REG_ZYXDA_MASK (1 << 3)
/**
* @brief Z axis new data available
*
* Default value: 0
*
* 0. a new data for the Z-axis is not yet available
* 1. a new data for the Z-axis is available
*/
#define LIS3DH_STATUS_REG_ZDA_MASK (1 << 2)
/**
* @brief Y axis new data available
*
* Default value: 0
*
* 0. a new data for the Y-axis is not yet available
* 1. a new data for the Y-axis is available
*/
#define LIS3DH_STATUS_REG_YDA_MASK (1 << 1)
/**
* @brief X axis new data available
*
* Default value: 0
*
* 0. a new data for the X-axis is not yet available
* 1. a new data for the X-axis is available
*/
#define LIS3DH_STATUS_REG_XDA_MASK (1 << 0)
/** @} */
/**
* @name FIFO_CTRL_REG bitfield macros
*/
/** @{ */
#define LIS3DH_FIFO_CTRL_REG_FM_SHIFT (6)
#define LIS3DH_FIFO_CTRL_REG_FM1_MASK (1 << 7)
#define LIS3DH_FIFO_CTRL_REG_FM0_MASK (1 << 6)
#define LIS3DH_FIFO_CTRL_REG_FM_MASK (LIS3DH_FIFO_CTRL_REG_FM1_MASK | \
LIS3DH_FIFO_CTRL_REG_FM0_MASK)
#define LIS3DH_FIFO_CTRL_REG_TR_MASK (1 << 5)
#define LIS3DH_FIFO_CTRL_REG_FTH4_MASK (1 << 4)
#define LIS3DH_FIFO_CTRL_REG_FTH3_MASK (1 << 3)
#define LIS3DH_FIFO_CTRL_REG_FTH2_MASK (1 << 2)
#define LIS3DH_FIFO_CTRL_REG_FTH1_MASK (1 << 1)
#define LIS3DH_FIFO_CTRL_REG_FTH0_MASK (1 << 0)
#define LIS3DH_FIFO_CTRL_REG_FTH_SHIFT (0)
#define LIS3DH_FIFO_CTRL_REG_FTH_MASK \
(LIS3DH_FIFO_CTRL_REG_FTH0_MASK | \
LIS3DH_FIFO_CTRL_REG_FTH1_MASK | \
LIS3DH_FIFO_CTRL_REG_FTH2_MASK | \
LIS3DH_FIFO_CTRL_REG_FTH3_MASK | \
LIS3DH_FIFO_CTRL_REG_FTH4_MASK)
/** @} */
/**
* @name FIFO_SRC_REG bitfield macros
*/
/** @{ */
#define LIS3DH_FIFO_SRC_REG_WTM_MASK (1 << 7)
#define LIS3DH_FIFO_SRC_REG_OVRN_FIFO_MASK (1 << 6)
#define LIS3DH_FIFO_SRC_REG_EMPTY_MASK (1 << 5)
#define LIS3DH_FIFO_SRC_REG_FSS4_MASK (1 << 4)
#define LIS3DH_FIFO_SRC_REG_FSS3_MASK (1 << 3)
#define LIS3DH_FIFO_SRC_REG_FSS2_MASK (1 << 2)
#define LIS3DH_FIFO_SRC_REG_FSS1_MASK (1 << 1)
#define LIS3DH_FIFO_SRC_REG_FSS0_MASK (1 << 0)
#define LIS3DH_FIFO_SRC_REG_FSS_SHIFT (0)
#define LIS3DH_FIFO_SRC_REG_FSS_MASK \
(LIS3DH_FIFO_SRC_REG_FSS0_MASK | \
LIS3DH_FIFO_SRC_REG_FSS1_MASK | \
LIS3DH_FIFO_SRC_REG_FSS2_MASK | \
LIS3DH_FIFO_SRC_REG_FSS3_MASK | \
LIS3DH_FIFO_SRC_REG_FSS4_MASK)
/** @} */
/**
* @name Register address bitfield macros
*/
/** @{ */
/**
* Write to register
*/
#define LIS3DH_SPI_WRITE_MASK (0 << 7)
/**
* The READ bit must be set when reading
*/
#define LIS3DH_SPI_READ_MASK (1 << 7)
/**
* Multi byte transfers must assert this bit when writing the address.
*/
#define LIS3DH_SPI_MULTI_MASK (1 << 6)
/**
* Opposite of LIS3DH_SPI_MULTI_MASK.
*/
#define LIS3DH_SPI_SINGLE_MASK (0 << 6)
/**
* Mask of the address bits in the address byte during transfers.
*/
#define LIS3DH_SPI_ADDRESS_MASK (0x3F)
/** @} */
/**
* Length of scalar measurement data in bytes.
*/
#define LIS3DH_ADC_DATA_SIZE (2)
/**
* @brief Allowed values for the Output Data Rate of the sensor.
*
* Use these when calling lis3dh_set_odr(odr).
*/
typedef enum {
LIS3DH_ODR_POWERDOWN = (0x00 << LIS3DH_CTRL_REG1_ODR_SHIFT),
LIS3DH_ODR_1Hz = (0x01 << LIS3DH_CTRL_REG1_ODR_SHIFT),
LIS3DH_ODR_10Hz = (0x02 << LIS3DH_CTRL_REG1_ODR_SHIFT),
LIS3DH_ODR_25Hz = (0x03 << LIS3DH_CTRL_REG1_ODR_SHIFT),
LIS3DH_ODR_50Hz = (0x04 << LIS3DH_CTRL_REG1_ODR_SHIFT),
LIS3DH_ODR_100Hz = (0x05 << LIS3DH_CTRL_REG1_ODR_SHIFT),
LIS3DH_ODR_200Hz = (0x06 << LIS3DH_CTRL_REG1_ODR_SHIFT),
LIS3DH_ODR_400Hz = (0x07 << LIS3DH_CTRL_REG1_ODR_SHIFT),
LIS3DH_ODR_LP1600Hz = (0x08 << LIS3DH_CTRL_REG1_ODR_SHIFT),
LIS3DH_ODR_NP1250Hz_LP5000HZ = (0x09 << LIS3DH_CTRL_REG1_ODR_SHIFT)
} lis3dh_odr_t;
/**
* @brief Allowed FIFO modes.
*
* Used when calling lis3dh_set_fifo_mode()
*/
typedef enum {
/** FIFO mode: Bypass */
LIS3DH_FIFO_MODE_BYPASS = (0x00 << LIS3DH_FIFO_CTRL_REG_FM_SHIFT),
/** FIFO mode: FIFO */
LIS3DH_FIFO_MODE_FIFO = (0x01 << LIS3DH_FIFO_CTRL_REG_FM_SHIFT),
/** FIFO mode: Stream */
LIS3DH_FIFO_MODE_STREAM = (0x02 << LIS3DH_FIFO_CTRL_REG_FM_SHIFT),
/** FIFO mode: Stream to FIFO */
LIS3DH_FIFO_MODE_STREAM_TO_FIFO = (0x03 << LIS3DH_FIFO_CTRL_REG_FM_SHIFT)
} lis3dh_fifo_mode_t;
/**
* @brief Device descriptor for LIS3DH sensors
*/
typedef struct {
spi_t spi; /**< SPI device the sensor is connected to */
gpio_t cs; /**< Chip select pin */
gpio_t int1; /**< INT1 pin */
gpio_t int2; /**< INT2 (DRDY) pin */
int16_t scale; /**< Current scale setting of the sensor */
} lis3dh_t;
/**
* @brief Result vector for accelerometer measurement
*/
typedef struct __attribute__((packed))
{
int16_t acc_x; /**< Acceleration in the X direction in milli-G */
int16_t acc_y; /**< Acceleration in the Y direction in milli-G */
int16_t acc_z; /**< Acceleration in the Z direction in milli-G */
}
lis3dh_data_t;
/**
* @brief Initialize a LIS3DH sensor instance
*
* @param[in] dev Device descriptor of sensor to initialize
* @param[in] spi SPI bus the accelerometer is connected to
* @param[in] cs_pin GPIO connected to the chip select pin of the accelerometer
* @param[in] int1_pin GPIO connected to the INT1 pin of the accelerometer
* @param[in] int2_pin GPIO connected to the INT2 pin of the accelerometer
* @param[in] scale Initial scale setting of the sensor
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_init(lis3dh_t *dev, spi_t spi, gpio_t cs_pin, gpio_t int1_pin, gpio_t int2_pin, lis3dh_scale_t scale);
/**
* @brief Read 3D acceleration data from the accelerometer
*
* @param[in] dev Device descriptor of sensor
* @param[out] acc_data Accelerometer data output buffer
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_read_xyz(const lis3dh_t *dev, lis3dh_data_t *acc_data);
/**
* @brief Read auxiliary ADC channel 1 data from the accelerometer
*
* @param[in] dev Device descriptor of sensor
* @param[out] out The value of ADC1 (OUT_1_{L,H}) will be written to this buffer
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_read_aux_adc1(const lis3dh_t *dev, int16_t *out);
/**
* @brief Read auxiliary ADC channel 2 data from the accelerometer
*
* @param[in] dev Device descriptor of sensor
* @param[out] out The value of ADC2 (OUT_2_{L,H}) will be written to this buffer
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_read_aux_adc2(const lis3dh_t *dev, int16_t *out);
/**
* @brief Read auxiliary ADC channel 3 data from the accelerometer
*
* @param[in] dev Device descriptor of sensor
* @param[out] out The value of ADC3 (OUT_3_{L,H}) will be written to this buffer
*
* @note The internal temperature sensor is connected to the third channel on
* the auxiliary ADC when the TEMP_EN bit of TEMP_CFG_REG is set.
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_read_aux_adc3(const lis3dh_t *dev, int16_t *out);
/**
* @brief Turn on/off power to the auxiliary ADC in LIS3DH.
*
* @param[in] dev Device descriptor of sensor
* @param[in] enable Power state of the auxiliary ADC
* @param[in] temperature If not zero, switch the ADC mux so that a
* temperature reading is available on OUT_3_L, OUT_3_H.
*
* @note This ADC is only used for the temperature reading and the external ADC
* pins. The accelerometer ADC is turned on by lis3dh_set_odr().
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_set_aux_adc(lis3dh_t *dev, const uint8_t enable, const uint8_t temperature);
/**
* @brief Enable/disable accelerometer axes.
*
* @param[in] dev Device descriptor of sensor
* @param[in] axes An OR-ed combination of LIS3DH_AXES_X,
* LIS3DH_AXES_Y, LIS3DH_AXES_Z.
*
* @note The macro LIS3DH_AXES_XYZ is a convenience shortcut to enable all axes.
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_set_axes(lis3dh_t *dev, const uint8_t axes);
/**
* @brief Set the FIFO mode.
*
* @param[in] dev Device descriptor of sensor
* @param[in] mode The chosen FIFO mode.
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_set_fifo_mode(lis3dh_t *dev, const lis3dh_fifo_mode_t mode);
/**
* @brief Enable/disable the FIFO.
*
* @param[in] dev Device descriptor of sensor
* @param[in] enable If zero, disable the FIFO, otherwise enables the FIFO.
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_set_fifo(lis3dh_t *dev, const uint8_t enable);
/**
* Set the output data rate of the sensor.
*
* @param[in] dev Device descriptor of sensor
* @param[in] odr Chosen output data rate.
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_set_odr(lis3dh_t *dev, const lis3dh_odr_t odr);
/**
* @brief Set the full scale range of the sensor.
*
* Valid values for scale is 2, 4, 8, 16 and represents the full range of the
* sensor.
*
* @param[in] dev Device descriptor of sensor
* @param scale The chosen sensitivity scale.
*
* @return 0 on success
* @return -1 on error
*/
int lis3dh_set_scale(lis3dh_t *dev, const lis3dh_scale_t scale);
#ifdef __cplusplus
}
#endif
#endif /* LIS3DH_H_ */
/** @} */

3
drivers/lis3dh/Makefile

@ -0,0 +1,3 @@
MODULE = lis3dh
include $(RIOTBASE)/Makefile.base

265
drivers/lis3dh/lis3dh.c

@ -0,0 +1,265 @@
/*
* Copyright (C) 2015 Eistec AB
*
* 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.
*/
#include <stddef.h>
#include <stdint.h>
#include "periph/gpio.h"
#include "periph/spi.h"
#include "lis3dh.h"
/**
* @ingroup drivers_lis3dh
* @{
*
* @file
* @brief Implementation of LIS3DH SPI driver
*
* @author Joakim Gebart <joakim.gebart@eistec.se>
*/
static inline int lis3dh_write_bits(const lis3dh_t *dev, const lis3dh_reg_t reg, const uint8_t mask,
const uint8_t values);
static int lis3dh_write_reg(const lis3dh_t *dev, const lis3dh_reg_t reg, const uint8_t value);
static int lis3dh_read_regs(const lis3dh_t *dev, const lis3dh_reg_t reg, const uint8_t len,
uint8_t *buf);
int lis3dh_init(lis3dh_t *dev, spi_t spi, gpio_t cs_pin, gpio_t int1_pin, gpio_t int2_pin, lis3dh_scale_t scale)
{
uint8_t in;
dev->spi = spi;
dev->cs = cs_pin;
dev->int1 = int1_pin;
dev->int2 = int2_pin;
dev->scale = 0;
/* CS */
gpio_init_out(dev->cs, GPIO_NOPULL);
gpio_set(dev->cs);
if (lis3dh_read_regs(dev, LIS3DH_REG_WHO_AM_I, 1, &in) < 0) {
/* Communication error */
return -1;
}
if (in != LIS3DH_WHO_AM_I_RESPONSE) {
/* Chip is not responding correctly */
return -1;
}
/* Set block data update and little endian mode. */
lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG4,
(LIS3DH_CTRL_REG4_BDU_ENABLE |
LIS3DH_CTRL_REG4_BLE_LITTLE_ENDIAN));
lis3dh_set_scale(dev, scale);
return 0;
}
int lis3dh_read_xyz(const lis3dh_t *dev, lis3dh_data_t *acc_data)
{
int32_t tmp;
uint8_t i;
/* Set READ MULTIPLE mode */
static const uint8_t addr = (LIS3DH_REG_OUT_X_L | LIS3DH_SPI_READ_MASK | LIS3DH_SPI_MULTI_MASK);
/* Acquire exclusive access to the bus. */
spi_acquire(dev->spi);
/* Perform the transaction */
gpio_clear(dev->cs);
if (spi_transfer_regs(dev->spi, addr, NULL, (char *)acc_data,
sizeof(lis3dh_data_t)) != sizeof(lis3dh_data_t)) {
/* Transfer error */
return -1;
}
gpio_set(dev->cs);
/* Release the bus for other threads. */
spi_release(dev->spi);
/* Scale to milli-G */
for (i = 0; i < 3; ++i) {
tmp = (int32_t)(((int16_t *)acc_data)[i]);
tmp *= dev->scale;
tmp /= 32768;
(((int16_t *)acc_data)[i]) = (int16_t)tmp;
}
return 0;
}
int lis3dh_read_aux_adc1(const lis3dh_t *dev, int16_t *out)
{
return lis3dh_read_regs(dev, LIS3DH_REG_OUT_AUX_ADC1_L, LIS3DH_ADC_DATA_SIZE, (uint8_t *)out);
}
int lis3dh_read_aux_adc2(const lis3dh_t *dev, int16_t *out)
{
return lis3dh_read_regs(dev, LIS3DH_REG_OUT_AUX_ADC2_L, LIS3DH_ADC_DATA_SIZE, (uint8_t *)out);
}
int lis3dh_read_aux_adc3(const lis3dh_t *dev, int16_t *out)
{
return lis3dh_read_regs(dev, LIS3DH_REG_OUT_AUX_ADC3_L, LIS3DH_ADC_DATA_SIZE, (uint8_t *)out);
}
int lis3dh_set_aux_adc(lis3dh_t *dev, const uint8_t enable, const uint8_t temperature)
{
return lis3dh_write_bits(dev, LIS3DH_REG_TEMP_CFG_REG, LIS3DH_TEMP_CFG_REG_ADC_PD_MASK,
(enable ? LIS3DH_TEMP_CFG_REG_ADC_PD_MASK : 0) |
(temperature ? LIS3DH_TEMP_CFG_REG_TEMP_EN_MASK : 0));
}
int lis3dh_set_axes(lis3dh_t *dev, const uint8_t axes)
{
return lis3dh_write_bits(dev, LIS3DH_REG_CTRL_REG1, LIS3DH_CTRL_REG1_XYZEN_MASK, axes);
}
int lis3dh_set_fifo_mode(lis3dh_t *dev, const lis3dh_fifo_mode_t mode)
{
return lis3dh_write_bits(dev, LIS3DH_REG_FIFO_CTRL_REG, LIS3DH_FIFO_CTRL_REG_FM_MASK,
mode);
}
int lis3dh_set_fifo(lis3dh_t *dev, const uint8_t enable)
{
return lis3dh_write_bits(dev, LIS3DH_REG_CTRL_REG5, LIS3DH_CTRL_REG5_FIFO_EN_MASK,
(enable ? LIS3DH_CTRL_REG5_FIFO_EN_MASK : 0));
}
int lis3dh_set_odr(lis3dh_t *dev, const lis3dh_odr_t odr)
{
return lis3dh_write_bits(dev, LIS3DH_REG_CTRL_REG1, LIS3DH_CTRL_REG1_ODR_MASK,
odr);
}
int lis3dh_set_scale(lis3dh_t *dev, const lis3dh_scale_t scale)
{
/* Sensor full range is -32768 -- +32767 */
/* => Scale factor is scale/32768 */
switch (scale)
{
case LIS3DH_SCALE_2G:
dev->scale = 2000;
break;
case LIS3DH_SCALE_4G:
dev->scale = 4000;
break;
case LIS3DH_SCALE_8G:
dev->scale = 8000;
break;
case LIS3DH_SCALE_16G:
dev->scale = 16000;
break;
default:
return -1;
}
return lis3dh_write_bits(dev, LIS3DH_REG_CTRL_REG4, LIS3DH_CTRL_REG4_FS_MASK,
scale);
}
/**
* @brief Read sequential registers from the LIS3DH.
*
* @param[in] dev Device descriptor
* @param[in] reg The source register starting address
* @param[in] len Number of bytes to read
* @param[out] buf The values of the source registers will be written here
*
* @return 0 on success
* @return -1 on error
*/
static int lis3dh_read_regs(const lis3dh_t *dev, const lis3dh_reg_t reg, const uint8_t len,
uint8_t *buf)
{
/* Set READ MULTIPLE mode */
uint8_t addr = (reg & LIS3DH_SPI_ADDRESS_MASK) | LIS3DH_SPI_READ_MASK | LIS3DH_SPI_MULTI_MASK;
/* Acquire exclusive access to the bus. */
spi_acquire(dev->spi);
/* Perform the transaction */
gpio_clear(dev->cs);
if (spi_transfer_regs(dev->spi, addr, NULL, (char *)buf, len) < 0) {
/* Transfer error */
return -1;
}
gpio_set(dev->cs);
/* Release the bus for other threads. */
spi_release(dev->spi);
return 0;
}
/**
* @brief Write a value to an 8 bit register in the LIS3DH.
*
* @param[in] reg The target register.
* @param[in] value The value to write.
*
* @return 0 on success
* @return -1 on error
*/
static int lis3dh_write_reg(const lis3dh_t *dev, const lis3dh_reg_t reg, const uint8_t value)
{
/* Set WRITE SINGLE mode */
uint8_t addr = (reg & LIS3DH_SPI_ADDRESS_MASK) | LIS3DH_SPI_WRITE_MASK | LIS3DH_SPI_SINGLE_MASK;
/* Acquire exclusive access to the bus. */
spi_acquire(dev->spi);
/* Perform the transaction */
gpio_clear(dev->cs);
if (spi_transfer_reg(dev->spi, addr, value, NULL) < 0) {
/* Transfer error */
return -1;
}
gpio_set(dev->cs);
/* Release the bus for other threads. */
spi_release(dev->spi);
return 0;
}
/**
* @brief Write (both set and clear) bits of an 8-bit register on the LIS3DH.
*
* @param[in] addr Register address on the LIS3DH.
* @param[in] mask Bitmask for the bits to modify.
* @param[in] values The values to write to the masked bits.
*
* @return 0 on success
* @return -1 on error
*/
static inline int
lis3dh_write_bits(const lis3dh_t *dev, const lis3dh_reg_t reg, const uint8_t mask,
const uint8_t values)
{
uint8_t tmp;
if (lis3dh_read_regs(dev, reg, 1, &tmp) < 0) {
/* Communication error */
return -1;
}
tmp &= ~mask;
tmp |= (values & mask);
if (lis3dh_write_reg(dev, reg, tmp) < 0) {
/* Communication error */
return -1;
}
return 0;
}
/** @} */

34
tests/driver_lis3dh/Makefile

@ -0,0 +1,34 @@
APPLICATION = driver_lis3dh
include ../Makefile.tests_common
FEATURES_REQUIRED = periph_spi periph_gpio
USEMODULE += lis3dh
USEMODULE += vtimer
ifneq (,$(TEST_LIS3DH_SPI))
CFLAGS += -DTEST_LIS3DH_SPI=$(TEST_LIS3DH_SPI)
else
# set arbitrary default
CFLAGS += -DTEST_LIS3DH_SPI=SPI_0
endif
ifneq (,$(TEST_LIS3DH_CS))
CFLAGS += -DTEST_LIS3DH_CS=$(TEST_LIS3DH_CS)
else
# set arbitrary default
CFLAGS += -DTEST_LIS3DH_CS=GPIO_0
endif
ifneq (,$(TEST_LIS3DH_INT1))
CFLAGS += -DTEST_LIS3DH_INT1=$(TEST_LIS3DH_INT1)
else
# set arbitrary default
CFLAGS += -DTEST_LIS3DH_INT1=GPIO_1
endif
ifneq (,$(TEST_LIS3DH_INT2))
CFLAGS += -DTEST_LIS3DH_INT2=$(TEST_LIS3DH_INT2)
else
# set arbitrary default
CFLAGS += -DTEST_LIS3DH_INT2=GPIO_2
endif
include $(RIOTBASE)/Makefile.include

11
tests/driver_lis3dh/README.md

@ -0,0 +1,11 @@
# About
This is a manual test application for the LIS3DH accelerometer driver.
# Usage
This test application will initialize the accelerometer with the following parameters:
- Sampling Rate: 100Hz
- Scale: 4G
- Temperature sensor: Enabled
After initialization, the sensor reads the acceleration values every 100ms
and prints them to the STDOUT.

157
tests/driver_lis3dh/main.c

@ -0,0 +1,157 @@
/*
* Copyright (C) 2015 Eistec AB
*
* 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 LIS3DH accelerometer driver
*
* @author Joakim Gebart <joakim.gebart@eistec.se
*
* @}
*/
#include <stdio.h>
#include "board.h"
#include "vtimer.h"
#include "periph/spi.h"
#include "lis3dh.h"
/* Check for definition of hardware pins, default to board.h values if not set. */
#ifndef TEST_LIS3DH_SPI
#ifdef LIS3DH_SPI
#define TEST_LIS3DH_SPI LIS3DH_SPI
#else
#error "TEST_LIS3DH_SPI not defined"
#endif
#endif
#ifndef TEST_LIS3DH_CS
#ifdef LIS3DH_CS
#define TEST_LIS3DH_CS LIS3DH_CS
#else
#error "TEST_LIS3DH_CS not defined"
#endif
#endif
#ifndef TEST_LIS3DH_INT1
#ifdef LIS3DH_INT1
#define TEST_LIS3DH_INT1 LIS3DH_INT1
#else
#error "TEST_LIS3DH_INT1 not defined"
#endif
#endif
#ifndef TEST_LIS3DH_INT2
#ifdef LIS3DH_INT2
#define TEST_LIS3DH_INT2 LIS3DH_INT2
#else
#error "TEST_LIS3DH_INT2 not defined"
#endif
#endif
#define SCALE LIS3DH_SCALE_4G
#define ODR LIS3DH_ODR_100Hz
#define SLEEP (100 * 1000U)
#define SPI_CONF (SPI_)
int main(void)
{
lis3dh_t dev;
lis3dh_data_t acc_data;
int16_t temperature;
puts("LIS3DH accelerometer driver test application\n");
printf("Initializing SPI_%i... ", TEST_LIS3DH_SPI);
if (spi_init_master(TEST_LIS3DH_SPI, SPI_CONF_SECOND_FALLING, SPI_SPEED_10MHZ) == 0) {
puts("[OK]");
}
else {
puts("[Failed]\n");
return 1;
}
puts("Initializing LIS3DH sensor... ");
if (lis3dh_init(&dev, TEST_LIS3DH_SPI, TEST_LIS3DH_CS,
TEST_LIS3DH_INT1, TEST_LIS3DH_INT2, SCALE) == 0) {
puts("[OK]");
}
else {
puts("[Failed]\n");
return 1;
}
puts("Set ODR... ");
if (lis3dh_set_odr(&dev, ODR) == 0) {
puts("[OK]");
}
else {
puts("[Failed]\n");
return 1;
}
puts("Set scale... ");
if (lis3dh_set_scale(&dev, SCALE) == 0) {
puts("[OK]");
}
else {
puts("[Failed]\n");
return 1;
}
puts("Set axes XYZ... ");
if (lis3dh_set_axes(&dev, LIS3DH_AXES_XYZ) == 0) {
puts("[OK]");
}
else {
puts("[Failed]\n");
return 1;
}
puts("Disable FIFO mode... ");
if (lis3dh_set_fifo(&dev, 0) == 0) {
puts("[OK]");
}
else {
puts("[Failed]\n");
return 1;
}
puts("Enable temperature reading... ");
if (lis3dh_set_aux_adc(&dev, 1, 1) == 0) {
puts("[OK]");
}
else {
puts("[Failed]\n");
return 1;
}
puts("LIS3DH init done.\n");
while (1) {
lis3dh_read_xyz(&dev, &acc_data);
if (lis3dh_read_xyz(&dev, &acc_data) != 0) {
puts("Reading acceleration data... ");
puts("[Failed]\n");
return 1;
}
if (lis3dh_read_aux_adc3(&dev, &temperature) != 0) {
puts("Reading temperature data... ");
puts("[Failed]\n");
return 1;
}
printf("Sensor data - X: %6i Y: %6i Z: %6i Temp: %6i\n",
acc_data.acc_x, acc_data.acc_y, acc_data.acc_z, temperature);
vtimer_usleep(SLEEP);
}
return 0;
}
Loading…
Cancel
Save