diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index ff72725cb8e..139a955c5ed 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory_ifdef(CONFIG_HP206C hp206c) add_subdirectory_ifdef(CONFIG_HTS221 hts221) add_subdirectory_ifdef(CONFIG_ISL29035 isl29035) add_subdirectory_ifdef(CONFIG_LIS2DH lis2dh) +add_subdirectory_ifdef(CONFIG_LIS2DS12 lis2ds12) add_subdirectory_ifdef(CONFIG_LIS2MDL lis2mdl) add_subdirectory_ifdef(CONFIG_LIS3DH lis3dh) add_subdirectory_ifdef(CONFIG_LIS3MDL lis3mdl) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index dd4e7e5fb97..8e66a462079 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -71,6 +71,8 @@ source "drivers/sensor/isl29035/Kconfig" source "drivers/sensor/lis2dh/Kconfig" +source "drivers/sensor/lis2ds12/Kconfig" + source "drivers/sensor/lis2mdl/Kconfig" source "drivers/sensor/lis3dh/Kconfig" diff --git a/drivers/sensor/lis2ds12/CMakeLists.txt b/drivers/sensor/lis2ds12/CMakeLists.txt new file mode 100644 index 00000000000..5ffe7dcd2ff --- /dev/null +++ b/drivers/sensor/lis2ds12/CMakeLists.txt @@ -0,0 +1,6 @@ +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_LIS2DS12 lis2ds12) +zephyr_library_sources_ifdef(CONFIG_LIS2DS12 lis2ds12_i2c.c) +zephyr_library_sources_ifdef(CONFIG_LIS2DS12 lis2ds12_spi.c) +zephyr_library_sources_ifdef(CONFIG_LIS2DS12_TRIGGER lis2ds12_trigger.c) diff --git a/drivers/sensor/lis2ds12/Kconfig b/drivers/sensor/lis2ds12/Kconfig new file mode 100644 index 00000000000..3b520b0e56c --- /dev/null +++ b/drivers/sensor/lis2ds12/Kconfig @@ -0,0 +1,90 @@ +# ST Microelectronics LIS2DS12 3-axis accelerometer driver +# +# Copyright (c) 2019 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig LIS2DS12 + bool "LIS2DS12 I2C/SPI accelerometer sensor driver" + depends on (I2C && HAS_DTS_I2C) || (SPI && HAS_DTS_SPI) + help + Enable driver for LIS2DS12 accelerometer sensor driver + +if LIS2DS12 + +choice LIS2DS12_TRIGGER_MODE + prompt "Trigger mode" + help + Specify the type of triggering to be used by the driver. + +config LIS2DS12_TRIGGER_NONE + bool "No trigger" + +config LIS2DS12_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select LIS2DS12_TRIGGER + +config LIS2DS12_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select LIS2DS12_TRIGGER + +endchoice + +config LIS2DS12_TRIGGER + bool + +config LIS2DS12_THREAD_PRIORITY + int "Thread priority" + depends on LIS2DS12_TRIGGER_OWN_THREAD + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config LIS2DS12_THREAD_STACK_SIZE + int "Thread stack size" + depends on LIS2DS12_TRIGGER_OWN_THREAD + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +config LIS2DS12_ENABLE_TEMP + bool "Enable temperature" + help + Enable/disable temperature + +menu "Attributes" + +config LIS2DS12_FS + int "Accelerometer full-scale range" + default 0 + help + Specify the default accelerometer full-scale range. + An X value for the config represents a range of +/- X G. Valid values + are: + 0: Full Scale selected at runtime + 2: +/- 2g + 4: +/- 4g + 8: +/- 8g + 16: +/- 16g + +config LIS2DS12_ODR + int "Accelerometer Output data rate frequency" + range 0 10 + default 0 + help + Specify the default accelerometer output data rate expressed in + samples per second (Hz). + 0: ODR selected at runtime + 1: 12.5Hz + 2: 25Hz + 3: 50Hz + 4: 100Hz + 5: 200Hz + 6: 400Hz + 7: 800Hz +endmenu + +endif # LIS2DS12 diff --git a/drivers/sensor/lis2ds12/lis2ds12.c b/drivers/sensor/lis2ds12/lis2ds12.c new file mode 100644 index 00000000000..b4519affdad --- /dev/null +++ b/drivers/sensor/lis2ds12/lis2ds12.c @@ -0,0 +1,317 @@ +/* ST Microelectronics LIS2DS12 3-axis accelerometer driver + * + * Copyright (c) 2019 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/lis2ds12.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lis2ds12.h" + +#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL +LOG_MODULE_REGISTER(LIS2DS12); + +static struct lis2ds12_data lis2ds12_data; + +static struct lis2ds12_config lis2ds12_config = { + .comm_master_dev_name = DT_ST_LIS2DS12_0_BUS_NAME, +#if defined(DT_ST_LIS2DS12_BUS_SPI) + .bus_init = lis2ds12_spi_init, +#elif defined(DT_ST_LIS2DS12_BUS_I2C) + .bus_init = lis2ds12_i2c_init, +#else +#error "BUS MACRO NOT DEFINED IN DTS" +#endif +}; + +#if defined(LIS2DS12_ODR_RUNTIME) +static const u16_t lis2ds12_hr_odr_map[] = {0, 12, 25, 50, 100, 200, 400, 800}; + +static int lis2ds12_freq_to_odr_val(u16_t freq) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(lis2ds12_hr_odr_map); i++) { + if (freq == lis2ds12_hr_odr_map[i]) { + return i; + } + } + + return -EINVAL; +} + +static int lis2ds12_accel_odr_set(struct device *dev, u16_t freq) +{ + struct lis2ds12_data *data = dev->driver_data; + int odr; + + odr = lis2ds12_freq_to_odr_val(freq); + if (odr < 0) { + return odr; + } + + if (data->hw_tf->update_reg(data, + LIS2DS12_REG_CTRL1, + LIS2DS12_MASK_CTRL1_ODR, + odr << LIS2DS12_SHIFT_CTRL1_ODR) < 0) { + LOG_DBG("failed to set accelerometer sampling rate"); + return -EIO; + } + + return 0; +} +#endif + +#ifdef LIS2DS12_FS_RUNTIME +static const u16_t lis2ds12_accel_fs_map[] = {2, 16, 4, 8}; +static const u16_t lis2ds12_accel_fs_sens[] = {1, 8, 2, 4}; + +static int lis2ds12_accel_range_to_fs_val(s32_t range) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(lis2ds12_accel_fs_map); i++) { + if (range == lis2ds12_accel_fs_map[i]) { + return i; + } + } + + return -EINVAL; +} + +static int lis2ds12_accel_range_set(struct device *dev, s32_t range) +{ + int fs; + struct lis2ds12_data *data = dev->driver_data; + + fs = lis2ds12_accel_range_to_fs_val(range); + if (fs < 0) { + return fs; + } + + if (data->hw_tf->update_reg(data, + LIS2DS12_REG_CTRL1, + LIS2DS12_MASK_CTRL1_FS, + fs << LIS2DS12_SHIFT_CTRL1_FS) < 0) { + LOG_DBG("failed to set accelerometer full-scale"); + return -EIO; + } + + data->gain = (float)(lis2ds12_accel_fs_sens[fs] * GAIN_XL); + return 0; +} +#endif + +static int lis2ds12_accel_config(struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (attr) { +#ifdef LIS2DS12_FS_RUNTIME + case SENSOR_ATTR_FULL_SCALE: + return lis2ds12_accel_range_set(dev, sensor_ms2_to_g(val)); +#endif +#ifdef LIS2DS12_ODR_RUNTIME + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return lis2ds12_accel_odr_set(dev, val->val1); +#endif + default: + LOG_DBG("Accel attribute not supported."); + return -ENOTSUP; + } + + return 0; +} + +static int lis2ds12_attr_set(struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + return lis2ds12_accel_config(dev, chan, attr, val); + default: + LOG_WRN("attr_set() not supported on this channel."); + return -ENOTSUP; + } + + return 0; +} + +static int lis2ds12_sample_fetch_accel(struct device *dev) +{ + struct lis2ds12_data *data = dev->driver_data; + u8_t buf[6]; + + if (data->hw_tf->read_data(data, LIS2DS12_REG_OUTX_L, + buf, sizeof(buf)) < 0) { + LOG_DBG("failed to read sample"); + return -EIO; + } + + data->sample_x = (s16_t)((u16_t)(buf[0]) | ((u16_t)(buf[1]) << 8)); + data->sample_y = (s16_t)((u16_t)(buf[2]) | ((u16_t)(buf[3]) << 8)); + data->sample_z = (s16_t)((u16_t)(buf[4]) | ((u16_t)(buf[5]) << 8)); + + return 0; +} + +static int lis2ds12_sample_fetch(struct device *dev, enum sensor_channel chan) +{ + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + lis2ds12_sample_fetch_accel(dev); + break; +#if defined(CONFIG_LIS2DS12_ENABLE_TEMP) + case SENSOR_CHAN_DIE_TEMP: + lis2ds12_sample_fetch_temp(dev); + break; +#endif + case SENSOR_CHAN_ALL: + lis2ds12_sample_fetch_accel(dev); +#if defined(CONFIG_LIS2DS12_ENABLE_TEMP) + lis2ds12_sample_fetch_temp(dev); +#endif + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static inline void lis2ds12_convert(struct sensor_value *val, int raw_val, + float gain) +{ + s64_t dval; + + /* Gain is in mg/LSB */ + /* Convert to m/s^2 */ + dval = ((s64_t)raw_val * gain * SENSOR_G) / 1000; + val->val1 = dval / 1000000LL; + val->val2 = dval % 1000000LL; +} + +static inline int lis2ds12_get_channel(enum sensor_channel chan, + struct sensor_value *val, + struct lis2ds12_data *data, + float gain) +{ + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + lis2ds12_convert(val, data->sample_x, gain); + break; + case SENSOR_CHAN_ACCEL_Y: + lis2ds12_convert(val, data->sample_y, gain); + break; + case SENSOR_CHAN_ACCEL_Z: + lis2ds12_convert(val, data->sample_z, gain); + break; + case SENSOR_CHAN_ACCEL_XYZ: + lis2ds12_convert(val, data->sample_x, gain); + lis2ds12_convert(val + 1, data->sample_y, gain); + lis2ds12_convert(val + 2, data->sample_z, gain); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int lis2ds12_channel_get(struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct lis2ds12_data *data = dev->driver_data; + + return lis2ds12_get_channel(chan, val, data, data->gain); +} + +static const struct sensor_driver_api lis2ds12_api_funcs = { + .attr_set = lis2ds12_attr_set, +#if defined(CONFIG_LIS2DS12_TRIGGER) + .trigger_set = lis2ds12_trigger_set, +#endif + .sample_fetch = lis2ds12_sample_fetch, + .channel_get = lis2ds12_channel_get, +}; + +static int lis2ds12_init(struct device *dev) +{ + const struct lis2ds12_config * const config = dev->config->config_info; + struct lis2ds12_data *data = dev->driver_data; + u8_t chip_id; + + data->comm_master = device_get_binding(config->comm_master_dev_name); + if (!data->comm_master) { + LOG_DBG("master not found: %s", + config->comm_master_dev_name); + return -EINVAL; + } + + config->bus_init(dev); + + /* s/w reset the sensor */ + if (data->hw_tf->write_reg(data, + LIS2DS12_REG_CTRL2, + LIS2DS12_SOFT_RESET) < 0) { + LOG_DBG("s/w reset fail"); + return -EIO; + } + + if (data->hw_tf->read_reg(data, LIS2DS12_REG_WHO_AM_I, &chip_id) < 0) { + LOG_DBG("failed reading chip id"); + return -EIO; + } + + if (chip_id != LIS2DS12_VAL_WHO_AM_I) { + LOG_DBG("invalid chip id 0x%x", chip_id); + return -EIO; + } + + LOG_DBG("chip id 0x%x", chip_id); + +#ifdef CONFIG_LIS2DS12_TRIGGER + if (lis2ds12_trigger_init(dev) < 0) { + LOG_ERR("Failed to initialize triggers."); + return -EIO; + } +#endif + + /* set sensor default odr */ + if (data->hw_tf->update_reg(data, + LIS2DS12_REG_CTRL1, + LIS2DS12_MASK_CTRL1_ODR, + LIS2DS12_DEFAULT_ODR) < 0) { + LOG_DBG("failed setting odr"); + return -EIO; + } + + /* set sensor default scale */ + if (data->hw_tf->update_reg(data, + LIS2DS12_REG_CTRL1, + LIS2DS12_MASK_CTRL1_FS, + LIS2DS12_DEFAULT_FS) < 0) { + LOG_DBG("failed setting scale"); + return -EIO; + } + data->gain = LIS2DS12_DEFAULT_GAIN; + + return 0; +} + +DEVICE_AND_API_INIT(lis2ds12, DT_ST_LIS2DS12_0_LABEL, lis2ds12_init, + &lis2ds12_data, &lis2ds12_config, POST_KERNEL, + CONFIG_SENSOR_INIT_PRIORITY, &lis2ds12_api_funcs); diff --git a/drivers/sensor/lis2ds12/lis2ds12.h b/drivers/sensor/lis2ds12/lis2ds12.h new file mode 100644 index 00000000000..90e6e2651cc --- /dev/null +++ b/drivers/sensor/lis2ds12/lis2ds12.h @@ -0,0 +1,142 @@ +/* ST Microelectronics LIS2DS12 3-axis accelerometer driver + * + * Copyright (c) 2019 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/lis2ds12.pdf + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_LIS2DS12_LIS2DS12_H_ +#define ZEPHYR_DRIVERS_SENSOR_LIS2DS12_LIS2DS12_H_ + +#include +#include +#include + +#define LIS2DS12_REG_WHO_AM_I 0x0F +#define LIS2DS12_VAL_WHO_AM_I 0x43 + +#define LIS2DS12_REG_CTRL1 0x20 +#define LIS2DS12_MASK_CTRL1_ODR (BIT(7) | BIT(6) | \ + BIT(5) | BIT(4)) +#define LIS2DS12_SHIFT_CTRL1_ODR 4 +#define LIS2DS12_MASK_CTRL1_FS (BIT(3) | BIT(2)) +#define LIS2DS12_SHIFT_CTRL1_FS 2 +#define LIS2DS12_MASK_CTRL1_HF_ODR BIT(1) +#define LIS2DS12_MASK_CTRL1_HF_BDU BIT(0) + +#define LIS2DS12_REG_CTRL2 0x21 +#define LIS2DS12_SOFT_RESET 0x40 + +#define LIS2DS12_REG_CTRL3 0x22 +#define LIS2DS12_SHIFT_LIR 0x02 +#define LIS2DS12_MASK_LIR 0x04 + +#define LIS2DS12_REG_CTRL4 0x23 +#define LIS2DS12_SHIFT_INT1_DRDY 0x00 +#define LIS2DS12_MASK_INT1_DRDY 0x01 + +#define LIS2DS12_REG_OUT_T 0x26 + +#define LIS2DS12_REG_STATUS 0x27 +#define LIS2DS12_INT_DRDY 0x01 + +#define LIS2DS12_REG_OUTX_L 0x28 +#define LIS2DS12_REG_OUTX_H 0x29 +#define LIS2DS12_REG_OUTY_L 0x2A +#define LIS2DS12_REG_OUTY_H 0x2B +#define LIS2DS12_REG_OUTZ_L 0x2C +#define LIS2DS12_REG_OUTZ_H 0x2D + + +#if (CONFIG_LIS2DS12_ODR == 0) +#define LIS2DS12_ODR_RUNTIME 1 +#define LIS2DS12_DEFAULT_ODR 0 +#else +#define LIS2DS12_DEFAULT_ODR (CONFIG_LIS2DS12_ODR << 4) +#endif + +/* Accel sensor sensitivity unit is 0.061 mg/LSB */ +#define GAIN_XL (61LL / 1000.0) + +#if CONFIG_LIS2DS12_FS == 0 +#define LIS2DS12_FS_RUNTIME 1 +#define LIS2DS12_DEFAULT_FS (0 << 2) +#define LIS2DS12_DEFAULT_GAIN GAIN_XL +#elif CONFIG_LIS2DS12_FS == 2 +#define LIS2DS12_DEFAULT_FS (0 << 2) +#define LIS2DS12_DEFAULT_GAIN GAIN_XL +#elif CONFIG_LIS2DS12_FS == 4 +#define LIS2DS12_DEFAULT_FS (2 << 2) +#define LIS2DS12_DEFAULT_GAIN (2.0 * GAIN_XL) +#elif CONFIG_LIS2DS12_FS == 8 +#define LIS2DS12_DEFAULT_FS (3 << 2) +#define LIS2DS12_DEFAULT_GAIN (4.0 * GAIN_XL) +#elif CONFIG_LIS2DS12_FS == 16 +#define LIS2DS12_DEFAULT_FS (1 << 2) +#define LIS2DS12_DEFAULT_GAIN (8.0 * GAIN_XL) +#else +#error "Bad LIS2DS12 FS value (should be 0, 2, 4, 8, 16)" +#endif + +struct lis2ds12_config { + char *comm_master_dev_name; + int (*bus_init)(struct device *dev); +}; + +struct lis2ds12_data; + +struct lis2ds12_transfer_function { + int (*read_data)(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value, u8_t len); + int (*write_data)(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value, u8_t len); + int (*read_reg)(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value); + int (*write_reg)(struct lis2ds12_data *data, u8_t reg_addr, + u8_t value); + int (*update_reg)(struct lis2ds12_data *data, u8_t reg_addr, + u8_t mask, u8_t value); +}; + +struct lis2ds12_data { + struct device *comm_master; + int sample_x; + int sample_y; + int sample_z; + float gain; + const struct lis2ds12_transfer_function *hw_tf; + +#ifdef CONFIG_LIS2DS12_TRIGGER + struct device *gpio; + struct gpio_callback gpio_cb; + + struct sensor_trigger data_ready_trigger; + sensor_trigger_handler_t data_ready_handler; + +#if defined(CONFIG_LIS2DS12_TRIGGER_OWN_THREAD) + K_THREAD_STACK_MEMBER(thread_stack, CONFIG_LIS2DS12_THREAD_STACK_SIZE); + struct k_thread thread; + struct k_sem trig_sem; +#elif defined(CONFIG_LIS2DS12_TRIGGER_GLOBAL_THREAD) + struct k_work work; + struct device *dev; +#endif + +#endif /* CONFIG_LIS2DS12_TRIGGER */ +}; + +int lis2ds12_spi_init(struct device *dev); +int lis2ds12_i2c_init(struct device *dev); + +#ifdef CONFIG_LIS2DS12_TRIGGER +int lis2ds12_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +int lis2ds12_trigger_init(struct device *dev); +#endif + +#endif /* ZEPHYR_DRIVERS_SENSOR_LIS2DS12_LIS2DS12_H_ */ diff --git a/drivers/sensor/lis2ds12/lis2ds12_i2c.c b/drivers/sensor/lis2ds12/lis2ds12_i2c.c new file mode 100644 index 00000000000..d1458e6ec7c --- /dev/null +++ b/drivers/sensor/lis2ds12/lis2ds12_i2c.c @@ -0,0 +1,75 @@ +/* ST Microelectronics LIS2DS12 3-axis accelerometer driver + * + * Copyright (c) 2019 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/lis2ds12.pdf + */ + +#include +#include +#include + +#include "lis2ds12.h" + +#ifdef DT_ST_LIS2DS12_BUS_I2C + +static u16_t lis2ds12_i2c_slave_addr = DT_ST_LIS2DS12_0_BASE_ADDRESS; + +#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL +LOG_MODULE_DECLARE(LIS2DS12); + +static int lis2ds12_i2c_read_data(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value, u8_t len) +{ + return i2c_burst_read(data->comm_master, lis2ds12_i2c_slave_addr, + reg_addr, value, len); +} + +static int lis2ds12_i2c_write_data(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value, u8_t len) +{ + return i2c_burst_write(data->comm_master, lis2ds12_i2c_slave_addr, + reg_addr, value, len); +} + +static int lis2ds12_i2c_read_reg(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value) +{ + return i2c_reg_read_byte(data->comm_master, lis2ds12_i2c_slave_addr, + reg_addr, value); +} + +static int lis2ds12_i2c_write_reg(struct lis2ds12_data *data, u8_t reg_addr, + u8_t value) +{ + return i2c_reg_write_byte(data->comm_master, lis2ds12_i2c_slave_addr, + reg_addr, value); +} + +static int lis2ds12_i2c_update_reg(struct lis2ds12_data *data, u8_t reg_addr, + u8_t mask, u8_t value) +{ + return i2c_reg_update_byte(data->comm_master, lis2ds12_i2c_slave_addr, + reg_addr, mask, value); +} + +static const struct lis2ds12_transfer_function lis2ds12_i2c_transfer_fn = { + .read_data = lis2ds12_i2c_read_data, + .write_data = lis2ds12_i2c_write_data, + .read_reg = lis2ds12_i2c_read_reg, + .write_reg = lis2ds12_i2c_write_reg, + .update_reg = lis2ds12_i2c_update_reg, +}; + +int lis2ds12_i2c_init(struct device *dev) +{ + struct lis2ds12_data *data = dev->driver_data; + + data->hw_tf = &lis2ds12_i2c_transfer_fn; + + return 0; +} +#endif /* DT_ST_LIS2DS12_BUS_I2C */ diff --git a/drivers/sensor/lis2ds12/lis2ds12_spi.c b/drivers/sensor/lis2ds12/lis2ds12_spi.c new file mode 100644 index 00000000000..51b069ef8b7 --- /dev/null +++ b/drivers/sensor/lis2ds12/lis2ds12_spi.c @@ -0,0 +1,182 @@ +/* ST Microelectronics LIS2DS12 3-axis accelerometer driver + * + * Copyright (c) 2019 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/lis2ds12.pdf + */ + + +#include +#include +#include "lis2ds12.h" +#include + +#ifdef DT_ST_LIS2DS12_BUS_SPI + +#define LIS2DS12_SPI_READ (1 << 7) + +#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL +LOG_MODULE_DECLARE(LIS2DS12); + +#if defined(CONFIG_LIS2DS12_SPI_GPIO_CS) +static struct spi_cs_control lis2ds12_cs_ctrl; +#endif + +static struct spi_config lis2ds12_spi_conf = { + .frequency = DT_ST_LIS2DS12_0_SPI_MAX_FREQUENCY, + .operation = (SPI_OP_MODE_MASTER | SPI_MODE_CPOL | + SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_LINES_SINGLE), + .slave = DT_ST_LIS2DS12_0_BASE_ADDRESS, + .cs = NULL, +}; + +static int lis2ds12_raw_read(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value, u8_t len) +{ + struct spi_config *spi_cfg = &lis2ds12_spi_conf; + u8_t buffer_tx[2] = { reg_addr | LIS2DS12_SPI_READ, 0 }; + const struct spi_buf tx_buf = { + .buf = buffer_tx, + .len = 2, + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + const struct spi_buf rx_buf[2] = { + { + .buf = NULL, + .len = 1, + }, + { + .buf = value, + .len = len, + } + }; + const struct spi_buf_set rx = { + .buffers = rx_buf, + .count = 2 + }; + + + if (len > 64) { + return -EIO; + } + + if (spi_transceive(data->comm_master, spi_cfg, &tx, &rx)) { + return -EIO; + } + + return 0; +} + +static int lis2ds12_raw_write(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value, u8_t len) +{ + struct spi_config *spi_cfg = &lis2ds12_spi_conf; + u8_t buffer_tx[1] = { reg_addr & ~LIS2DS12_SPI_READ }; + const struct spi_buf tx_buf[2] = { + { + .buf = buffer_tx, + .len = 1, + }, + { + .buf = value, + .len = len, + } + }; + const struct spi_buf_set tx = { + .buffers = tx_buf, + .count = 2 + }; + + + if (len > 64) { + return -EIO; + } + + if (spi_write(data->comm_master, spi_cfg, &tx)) { + return -EIO; + } + + return 0; +} + +static int lis2ds12_spi_read_data(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value, u8_t len) +{ + return lis2ds12_raw_read(data, reg_addr, value, len); +} + +static int lis2ds12_spi_write_data(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value, u8_t len) +{ + return lis2ds12_raw_write(data, reg_addr, value, len); +} + +static int lis2ds12_spi_read_reg(struct lis2ds12_data *data, u8_t reg_addr, + u8_t *value) +{ + return lis2ds12_raw_read(data, reg_addr, value, 1); +} + +static int lis2ds12_spi_write_reg(struct lis2ds12_data *data, u8_t reg_addr, + u8_t value) +{ + u8_t tmp_val = value; + + return lis2ds12_raw_write(data, reg_addr, &tmp_val, 1); +} + +static int lis2ds12_spi_update_reg(struct lis2ds12_data *data, u8_t reg_addr, + u8_t mask, u8_t value) +{ + u8_t tmp_val; + + lis2ds12_raw_read(data, reg_addr, &tmp_val, 1); + tmp_val = (tmp_val & ~mask) | (value & mask); + + return lis2ds12_raw_write(data, reg_addr, &tmp_val, 1); +} + +static const struct lis2ds12_transfer_function lis2ds12_spi_transfer_fn = { + .read_data = lis2ds12_spi_read_data, + .write_data = lis2ds12_spi_write_data, + .read_reg = lis2ds12_spi_read_reg, + .write_reg = lis2ds12_spi_write_reg, + .update_reg = lis2ds12_spi_update_reg, +}; + +int lis2ds12_spi_init(struct device *dev) +{ + struct lis2ds12_data *data = dev->driver_data; + + data->hw_tf = &lis2ds12_spi_transfer_fn; + +#if defined(CONFIG_LIS2DS12_SPI_GPIO_CS) + /* handle SPI CS thru GPIO if it is the case */ + if (IS_ENABLED(CONFIG_LIS2DS12_SPI_GPIO_CS)) { + lis2ds12_cs_ctrl.gpio_dev = device_get_binding( + CONFIG_LIS2DS12_SPI_GPIO_CS_DRV_NAME); + if (!lis2ds12_cs_ctrl.gpio_dev) { + LOG_ERR("Unable to get GPIO SPI CS device"); + return -ENODEV; + } + + lis2ds12_cs_ctrl.gpio_pin = CONFIG_LIS2DS12_SPI_GPIO_CS_PIN; + lis2ds12_cs_ctrl.delay = 0; + + lis2ds12_spi_conf.cs = &lis2ds12_cs_ctrl; + + LOG_DBG("SPI GPIO CS configured on %s:%u", + CONFIG_LIS2DS12_SPI_GPIO_CS_DRV_NAME, + CONFIG_LIS2DS12_SPI_GPIO_CS_PIN); + } +#endif + + return 0; +} +#endif /* DT_ST_LIS2DS12_BUS_SPI */ diff --git a/drivers/sensor/lis2ds12/lis2ds12_trigger.c b/drivers/sensor/lis2ds12/lis2ds12_trigger.c new file mode 100644 index 00000000000..a85f09ff262 --- /dev/null +++ b/drivers/sensor/lis2ds12/lis2ds12_trigger.c @@ -0,0 +1,192 @@ +/* ST Microelectronics LIS2DS12 3-axis accelerometer driver + * + * Copyright (c) 2019 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/lis2ds12.pdf + */ + +#include +#include +#include +#include +#include +#include + +#include "lis2ds12.h" + +#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL +#include +LOG_MODULE_DECLARE(LIS2DS12); + +static void lis2ds12_gpio_callback(struct device *dev, + struct gpio_callback *cb, u32_t pins) +{ + struct lis2ds12_data *data = + CONTAINER_OF(cb, struct lis2ds12_data, gpio_cb); + + ARG_UNUSED(pins); + + gpio_pin_disable_callback(dev, DT_ST_LIS2DS12_0_IRQ_GPIOS_PIN); + +#if defined(CONFIG_LIS2DS12_TRIGGER_OWN_THREAD) + k_sem_give(&data->trig_sem); +#elif defined(CONFIG_LIS2DS12_TRIGGER_GLOBAL_THREAD) + k_work_submit(&data->work); +#endif +} + +static void lis2ds12_handle_drdy_int(struct device *dev) +{ + struct lis2ds12_data *data = dev->driver_data; + + if (data->data_ready_handler != NULL) { + data->data_ready_handler(dev, &data->data_ready_trigger); + } +} + +static void lis2ds12_handle_int(void *arg) +{ + struct device *dev = arg; + struct lis2ds12_data *data = dev->driver_data; + u8_t status; + + if (data->hw_tf->read_reg(data, LIS2DS12_REG_STATUS, &status) < 0) { + LOG_ERR("status reading error"); + return; + } + + if (status & LIS2DS12_INT_DRDY) { + lis2ds12_handle_drdy_int(dev); + } + + gpio_pin_enable_callback(data->gpio, DT_ST_LIS2DS12_0_IRQ_GPIOS_PIN); +} + +#ifdef CONFIG_LIS2DS12_TRIGGER_OWN_THREAD +static void lis2ds12_thread(int dev_ptr, int unused) +{ + struct device *dev = INT_TO_POINTER(dev_ptr); + struct lis2ds12_data *data = dev->driver_data; + + ARG_UNUSED(unused); + + while (1) { + k_sem_take(&data->trig_sem, K_FOREVER); + lis2ds12_handle_int(dev); + } +} +#endif + +#ifdef CONFIG_LIS2DS12_TRIGGER_GLOBAL_THREAD +static void lis2ds12_work_cb(struct k_work *work) +{ + struct lis2ds12_data *data = + CONTAINER_OF(work, struct lis2ds12_data, work); + + lis2ds12_handle_int(data->dev); +} +#endif + +static int lis2ds12_init_interrupt(struct device *dev) +{ + struct lis2ds12_data *data = dev->driver_data; + + /* Enable latched mode */ + if (data->hw_tf->update_reg(data, + LIS2DS12_REG_CTRL3, + LIS2DS12_MASK_LIR, + (1 << LIS2DS12_SHIFT_LIR)) < 0) { + LOG_ERR("Could not enable LIR mode."); + return -EIO; + } + + /* enable data-ready interrupt */ + if (data->hw_tf->update_reg(data, + LIS2DS12_REG_CTRL4, + LIS2DS12_MASK_INT1_DRDY, + (1 << LIS2DS12_SHIFT_INT1_DRDY)) < 0) { + LOG_ERR("Could not enable data-ready interrupt."); + return -EIO; + } + + return 0; +} + +int lis2ds12_trigger_init(struct device *dev) +{ + struct lis2ds12_data *data = dev->driver_data; + + /* setup data ready gpio interrupt */ + data->gpio = device_get_binding(DT_ST_LIS2DS12_0_IRQ_GPIOS_CONTROLLER); + if (data->gpio == NULL) { + LOG_ERR("Cannot get pointer to %s device.", + DT_ST_LIS2DS12_0_IRQ_GPIOS_CONTROLLER); + return -EINVAL; + } + + gpio_pin_configure(data->gpio, DT_ST_LIS2DS12_0_IRQ_GPIOS_PIN, + GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE | + GPIO_INT_ACTIVE_HIGH | GPIO_INT_DEBOUNCE); + + gpio_init_callback(&data->gpio_cb, + lis2ds12_gpio_callback, + BIT(DT_ST_LIS2DS12_0_IRQ_GPIOS_PIN)); + + if (gpio_add_callback(data->gpio, &data->gpio_cb) < 0) { + LOG_ERR("Could not set gpio callback."); + return -EIO; + } + +#if defined(CONFIG_LIS2DS12_TRIGGER_OWN_THREAD) + k_sem_init(&data->trig_sem, 0, UINT_MAX); + + k_thread_create(&data->thread, data->thread_stack, + CONFIG_LIS2DS12_THREAD_STACK_SIZE, + (k_thread_entry_t)lis2ds12_thread, dev, + 0, NULL, K_PRIO_COOP(CONFIG_LIS2DS12_THREAD_PRIORITY), + 0, 0); +#elif defined(CONFIG_LIS2DS12_TRIGGER_GLOBAL_THREAD) + data->work.handler = lis2ds12_work_cb; + data->dev = dev; +#endif + + gpio_pin_enable_callback(data->gpio, DT_ST_LIS2DS12_0_IRQ_GPIOS_PIN); + + return 0; +} + +int lis2ds12_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct lis2ds12_data *data = dev->driver_data; + u8_t buf[6]; + + __ASSERT_NO_MSG(trig->type == SENSOR_TRIG_DATA_READY); + + gpio_pin_disable_callback(data->gpio, DT_ST_LIS2DS12_0_IRQ_GPIOS_PIN); + + data->data_ready_handler = handler; + if (handler == NULL) { + LOG_WRN("lis2ds12: no handler"); + return 0; + } + + /* re-trigger lost interrupt */ + if (data->hw_tf->read_data(data, LIS2DS12_REG_OUTX_L, + buf, sizeof(buf)) < 0) { + LOG_ERR("status reading error"); + return -EIO; + } + + data->data_ready_trigger = *trig; + + lis2ds12_init_interrupt(dev); + gpio_pin_enable_callback(data->gpio, DT_ST_LIS2DS12_0_IRQ_GPIOS_PIN); + + return 0; +} + diff --git a/dts/bindings/sensor/st,lis2ds12-i2c.yaml b/dts/bindings/sensor/st,lis2ds12-i2c.yaml new file mode 100644 index 00000000000..3e7e0c9f141 --- /dev/null +++ b/dts/bindings/sensor/st,lis2ds12-i2c.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2019 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: STMicroelectronics MEMS sensors LIS2DS12 +version: 0.1 + +description: > + This binding gives a base representation of LIS2DS12 3-axis accelerometer + +inherits: + !include i2c-device.yaml + +properties: + compatible: + constraint: "st,lis2ds12" + + irq-gpios: + type: compound + category: required + generation: define, use-prop-name +... diff --git a/dts/bindings/sensor/st,lis2ds12-spi.yaml b/dts/bindings/sensor/st,lis2ds12-spi.yaml new file mode 100644 index 00000000000..364930b640d --- /dev/null +++ b/dts/bindings/sensor/st,lis2ds12-spi.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2019 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: STMicroelectronics MEMS sensors LIS2DS12 SPI +version: 0.1 + +description: > + This binding gives a base representation of LIS2DS12 3-axis accelerometer + accessed through SPI bus + +inherits: + !include spi-device.yaml + +properties: + compatible: + constraint: "st,lis2ds12" + + irq-gpios: + type: compound + category: required + generation: define, use-prop-name +... diff --git a/tests/drivers/build_all/dts_fixup.h b/tests/drivers/build_all/dts_fixup.h index 08bae0aba79..0924d23e580 100644 --- a/tests/drivers/build_all/dts_fixup.h +++ b/tests/drivers/build_all/dts_fixup.h @@ -158,6 +158,15 @@ #define DT_SENSIRION_SHT3XD_0_BUS_NAME "" #endif +#ifndef DT_ST_LIS2DS12_0_LABEL +#define DT_ST_LIS2DS12_0_LABEL "" +#define DT_ST_LIS2DS12_0_BUS_NAME "" +#define DT_ST_LIS2DS12_0_BASE_ADDRESS 0x19 +#define DT_ST_LIS2DS12_0_IRQ_GPIOS_CONTROLLER "" +#define DT_ST_LIS2DS12_0_IRQ_GPIOS_PIN 0 +#define DT_ST_LIS2DS12_BUS_I2C 1 +#endif + #ifndef DT_LIS3DH_DEV_NAME #define DT_LIS3DH_DEV_NAME "" #define DT_LIS3DH_I2C_ADDRESS 0x19 @@ -219,6 +228,16 @@ #define DT_LIS2DH_INT2_GPIO_PIN 1 #endif +#ifndef DT_ST_LIS2DS12_0_LABEL +#define DT_ST_LIS2DS12_0_LABEL "" +#define DT_ST_LIS2DS12_0_BUS_NAME "" +#define DT_ST_LIS2DS12_0_SPI_MAX_FREQUENCY 100000 +#define DT_ST_LIS2DS12_0_BASE_ADDRESS 1 +#define DT_ST_LIS2DS12_0_IRQ_GPIOS_CONTROLLER "" +#define DT_ST_LIS2DS12_0_IRQ_GPIOS_PIN 0 +#define DT_ST_LIS2DS12_BUS_SPI 1 +#endif + #ifndef DT_MICROCHIP_ENC28J60_0_LABEL #define DT_MICROCHIP_ENC28J60_0_BASE_ADDRESS 0 #define DT_MICROCHIP_ENC28J60_0_BUS_NAME "" diff --git a/tests/drivers/build_all/sensors_i_z.conf b/tests/drivers/build_all/sensors_i_z.conf index f0cb73bed6b..49700efc4a1 100644 --- a/tests/drivers/build_all/sensors_i_z.conf +++ b/tests/drivers/build_all/sensors_i_z.conf @@ -7,6 +7,7 @@ CONFIG_LOG=y CONFIG_SENSOR_LOG_LEVEL_DBG=y CONFIG_ISL29035=y CONFIG_LIS2DH=y +CONFIG_LIS2DS12=y CONFIG_LIS2MDL=y CONFIG_LIS3DH=y CONFIG_LIS3MDL=y diff --git a/tests/drivers/build_all/sensors_trigger_i_z.conf b/tests/drivers/build_all/sensors_trigger_i_z.conf index dd8bf8e39bb..978a6bb2c49 100644 --- a/tests/drivers/build_all/sensors_trigger_i_z.conf +++ b/tests/drivers/build_all/sensors_trigger_i_z.conf @@ -8,6 +8,8 @@ CONFIG_ISL29035=y CONFIG_ISL29035_TRIGGER_OWN_THREAD=y CONFIG_LIS2DH=y CONFIG_LIS2DH_TRIGGER_OWN_THREAD=y +CONFIG_LIS2DS12=y +CONFIG_LIS2DS12_TRIGGER_OWN_THREAD=y CONFIG_LIS2MDL=y CONFIG_LIS2MDL_TRIGGER_OWN_THREAD=y CONFIG_LIS3DH=y