diff --git a/drivers/include/lis3dh.h b/drivers/include/lis3dh.h new file mode 100644 index 0000000000..9313c908ee --- /dev/null +++ b/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 + +#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_ */ +/** @} */ diff --git a/drivers/lis3dh/Makefile b/drivers/lis3dh/Makefile new file mode 100644 index 0000000000..753ec6b6d7 --- /dev/null +++ b/drivers/lis3dh/Makefile @@ -0,0 +1,3 @@ +MODULE = lis3dh + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/lis3dh/lis3dh.c b/drivers/lis3dh/lis3dh.c new file mode 100644 index 0000000000..d0e37ef01e --- /dev/null +++ b/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 +#include +#include "periph/gpio.h" +#include "periph/spi.h" +#include "lis3dh.h" + +/** + * @ingroup drivers_lis3dh + * @{ + * + * @file + * @brief Implementation of LIS3DH SPI driver + * + * @author Joakim Gebart + */ + + +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; +} + +/** @} */ diff --git a/tests/driver_lis3dh/Makefile b/tests/driver_lis3dh/Makefile new file mode 100644 index 0000000000..21bf6fafdf --- /dev/null +++ b/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 diff --git a/tests/driver_lis3dh/README.md b/tests/driver_lis3dh/README.md new file mode 100644 index 0000000000..e0879762b8 --- /dev/null +++ b/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. diff --git a/tests/driver_lis3dh/main.c b/tests/driver_lis3dh/main.c new file mode 100644 index 0000000000..3bad5d0d87 --- /dev/null +++ b/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 + +#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; +}