/* 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);