/* * 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 NohlgĂ„rd */ 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, uint8_t scale) { uint8_t in; dev->spi = spi; dev->cs = cs_pin; dev->scale = 0; /* CS */ gpio_init(dev->cs, GPIO_OUT); 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; } /* Clear all settings */ lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG1, LIS3DH_CTRL_REG1_XYZEN_MASK); /* Disable HP filter */ lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG2, 0); /* Disable INT1 interrupt sources */ lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG3, 0); /* Set block data update and little endian, set Normal mode (LP=0, HR=1) */ lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG4, (LIS3DH_CTRL_REG4_BDU_ENABLE | LIS3DH_CTRL_REG4_BLE_LITTLE_ENDIAN | LIS3DH_CTRL_REG4_HR_MASK)); /* Disable FIFO */ lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG5, 0); /* Reset INT2 settings */ lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG6, 0); /* Configure scale */ lis3dh_set_scale(dev, scale); return 0; } int lis3dh_read_xyz(const lis3dh_t *dev, lis3dh_data_t *acc_data) { 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 */ gpio_set(dev->cs); /* Release the bus for other threads. */ spi_release(dev->spi); 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) { int32_t 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(lis3dh_t *dev, const uint8_t mode, const uint8_t watermark) { int status; uint8_t reg; reg = (watermark << LIS3DH_FIFO_CTRL_REG_FTH_SHIFT) & LIS3DH_FIFO_CTRL_REG_FTH_MASK; reg |= mode; status = lis3dh_write_reg(dev, LIS3DH_REG_FIFO_CTRL_REG, reg); if (status < 0) { /* communication error */ return status; } if (mode != 0x00) { status = lis3dh_write_bits(dev, LIS3DH_REG_CTRL_REG5, LIS3DH_CTRL_REG5_FIFO_EN_MASK, LIS3DH_CTRL_REG5_FIFO_EN_MASK); } else { status = lis3dh_write_bits(dev, LIS3DH_REG_CTRL_REG5, LIS3DH_CTRL_REG5_FIFO_EN_MASK, 0); } return status; } int lis3dh_set_odr(lis3dh_t *dev, const uint8_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 uint8_t scale) { uint8_t scale_reg; /* Sensor full range is -32768 -- +32767 (measurements are left adjusted) */ /* => Scale factor is scale/32768 */ switch (scale) { case 2: dev->scale = 2000; scale_reg = LIS3DH_CTRL_REG4_SCALE_2G; break; case 4: dev->scale = 4000; scale_reg = LIS3DH_CTRL_REG4_SCALE_4G; break; case 8: dev->scale = 8000; scale_reg = LIS3DH_CTRL_REG4_SCALE_8G; break; case 16: dev->scale = 16000; scale_reg = LIS3DH_CTRL_REG4_SCALE_16G; break; default: return -1; } return lis3dh_write_bits(dev, LIS3DH_REG_CTRL_REG4, LIS3DH_CTRL_REG4_FS_MASK, scale_reg); } int lis3dh_set_int1(lis3dh_t *dev, const uint8_t mode) { return lis3dh_write_reg(dev, LIS3DH_REG_CTRL_REG3, mode); } int lis3dh_get_fifo_level(lis3dh_t *dev) { uint8_t reg; int level; if (lis3dh_read_regs(dev, LIS3DH_REG_FIFO_SRC_REG, 1, ®) != 0) { return -1; } level = (reg & LIS3DH_FIFO_SRC_REG_FSS_MASK) >> LIS3DH_FIFO_SRC_REG_FSS_SHIFT; return level; } /** * @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 */ gpio_set(dev->cs); /* Release the bus for other threads. */ spi_release(dev->spi); 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 */ gpio_set(dev->cs); /* Release the bus for other threads. */ spi_release(dev->spi); 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; } /** @} */