drivers/sensor: add support to ISM330DHCX IMU sensor

The ISM330DHCX is a ultra-low power IMU with a 3D digital accelerometer
and 3D digital gyroscope tailored for Industry 4.0 applications, which
can be interfaced through either I2C or SPI bus.

https://www.st.com/resource/en/datasheet/ism330dhcx.pdf

This driver is based on stmemsc i/f v1.02.

Signed-off-by: Armando Visconti <armando.visconti@st.com>
This commit is contained in:
Armando Visconti 2020-03-06 10:54:25 +01:00 committed by Maureen Helm
commit c0f7a1ac6f
15 changed files with 2577 additions and 0 deletions

View file

@ -28,6 +28,7 @@ add_subdirectory_ifdef(CONFIG_HTS221 hts221)
add_subdirectory_ifdef(CONFIG_IIS2DLPC iis2dlpc)
add_subdirectory_ifdef(CONFIG_IIS3DHHC iis3dhhc)
add_subdirectory_ifdef(CONFIG_ISL29035 isl29035)
add_subdirectory_ifdef(CONFIG_ISM330DHCX ism330dhcx)
add_subdirectory_ifdef(CONFIG_LIS2DH lis2dh)
add_subdirectory_ifdef(CONFIG_LIS2DS12 lis2ds12)
add_subdirectory_ifdef(CONFIG_LIS2DW12 lis2dw12)

View file

@ -85,6 +85,8 @@ source "drivers/sensor/iis3dhhc/Kconfig"
source "drivers/sensor/isl29035/Kconfig"
source "drivers/sensor/ism330dhcx/Kconfig"
source "drivers/sensor/lis2dh/Kconfig"
source "drivers/sensor/lis2ds12/Kconfig"

View file

@ -0,0 +1,13 @@
# ST Microelectronics ISM330DHCX 6-axis IMU sensor driver
#
# Copyright (c) 2020 STMicroelectronics
#
# SPDX-License-Identifier: Apache-2.0
#
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_ISM330DHCX ism330dhcx.c)
zephyr_library_sources_ifdef(CONFIG_ISM330DHCX ism330dhcx_i2c.c)
zephyr_library_sources_ifdef(CONFIG_ISM330DHCX ism330dhcx_spi.c)
zephyr_library_sources_ifdef(CONFIG_ISM330DHCX_SENSORHUB ism330dhcx_shub.c)
zephyr_library_sources_ifdef(CONFIG_ISM330DHCX_TRIGGER ism330dhcx_trigger.c)

View file

@ -0,0 +1,171 @@
# ST Microelectronics ISM330DHCX 6-axis IMU sensor driver
# Copyright (c) 2020 STMicroelectronics
# SPDX-License-Identifier: Apache-2.0
menuconfig ISM330DHCX
bool "ISM330DHCX I2C/SPI accelerometer and gyroscope Chip"
depends on (I2C && HAS_DTS_I2C) || SPI
select HAS_STMEMSC
select USE_STDC_ISM330DHCX
help
Enable driver for ISM330DHCX accelerometer and gyroscope
sensor.
if ISM330DHCX
choice ISM330DHCX_TRIGGER_MODE
prompt "Trigger mode"
help
Specify the type of triggering to be used by the driver.
config ISM330DHCX_TRIGGER_NONE
bool "No trigger"
config ISM330DHCX_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
select ISM330DHCX_TRIGGER
config ISM330DHCX_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
select ISM330DHCX_TRIGGER
endchoice
config ISM330DHCX_TRIGGER
bool
if ISM330DHCX_TRIGGER
config ISM330DHCX_THREAD_PRIORITY
int "Thread priority"
depends on ISM330DHCX_TRIGGER_OWN_THREAD
default 10
help
Priority of thread used by the driver to handle interrupts.
config ISM330DHCX_THREAD_STACK_SIZE
int "Thread stack size"
depends on ISM330DHCX_TRIGGER_OWN_THREAD
default 1024
help
Stack size of thread used by the driver to handle interrupts.
choice ISM330DHCX_INT_PIN
prompt "Sensor INT pin number"
default ISM330DHCX_INT_PIN_1
help
The number of ISM330DHCX int pin used to generate interrupt to cpu.
Supported values are int1 or int2
config ISM330DHCX_INT_PIN_1
bool "int1"
config ISM330DHCX_INT_PIN_2
bool "int2"
endchoice
endif # ISM330DHCX_TRIGGER
config ISM330DHCX_ENABLE_TEMP
bool "Enable temperature"
help
Enable/disable temperature
config ISM330DHCX_SENSORHUB
bool "Enable I2C sensorhub feature"
help
Enable/disable internal sensorhub. You can enable
a maximum of two external sensors (if more than two are enabled
the system would enumerate only the first two found)
if ISM330DHCX_SENSORHUB
config ISM330DHCX_EXT_LIS2MDL
bool "Enable LIS2MDL as external sensor"
config ISM330DHCX_EXT_IIS2MDC
bool "Enable IIS2MDC as external sensor"
config ISM330DHCX_EXT_LPS22HH
bool "Enable LPS22HH as external sensor"
config ISM330DHCX_EXT_HTS221
bool "Enable HTS221 as external sensor"
config ISM330DHCX_EXT_LPS22HB
bool "Enable LPS22HB as external sensor"
endif # ISM330DHCX_SENSORHUB
menu "Attributes"
config ISM330DHCX_GYRO_FS
int "Gyroscope full-scale range"
default 0
help
Specify the default gyroscope full-scale range.
An X value for the config represents a range of +/- X degree per
second. Valid values are:
0: Full Scale selected at runtime
125: +/- 125dps
250: +/- 250dps
500: +/- 500dps
1000: +/- 1000dps
2000: +/- 2000dps
config ISM330DHCX_GYRO_ODR
int "Gyroscope 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: 26Hz
3: 52Hz
4: 104Hz
5: 208Hz
6: 416Hz
7: 833Hz
8: 1660Hz
9: 3330Hz
10: 6660Hz
config ISM330DHCX_ACCEL_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 ISM330DHCX_ACCEL_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: 26Hz
3: 52Hz
4: 104Hz
5: 208Hz
6: 416Hz
7: 833Hz
8: 1660Hz
9: 3330Hz
10: 6660Hz
endmenu
endif # ISM330DHCX

View file

@ -0,0 +1,844 @@
/* ST Microelectronics ISM330DHCX 6-axis IMU sensor driver
*
* Copyright (c) 2020 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://www.st.com/resource/en/datasheet/ism330dhcx.pdf
*/
#include <drivers/sensor.h>
#include <kernel.h>
#include <device.h>
#include <init.h>
#include <string.h>
#include <sys/byteorder.h>
#include <sys/__assert.h>
#include <logging/log.h>
#include "ism330dhcx.h"
LOG_MODULE_REGISTER(ISM330DHCX, CONFIG_SENSOR_LOG_LEVEL);
static const u16_t ism330dhcx_odr_map[] = {0, 12, 26, 52, 104, 208, 416, 833,
1660, 3330, 6660};
#if defined(ISM330DHCX_ACCEL_ODR_RUNTIME) || defined(ISM330DHCX_GYRO_ODR_RUNTIME)
static int ism330dhcx_freq_to_odr_val(u16_t freq)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(ism330dhcx_odr_map); i++) {
if (freq == ism330dhcx_odr_map[i]) {
return i;
}
}
return -EINVAL;
}
#endif
static int ism330dhcx_odr_to_freq_val(u16_t odr)
{
/* for valid index, return value from map */
if (odr < ARRAY_SIZE(ism330dhcx_odr_map)) {
return ism330dhcx_odr_map[odr];
}
/* invalid index, return last entry */
return ism330dhcx_odr_map[ARRAY_SIZE(ism330dhcx_odr_map) - 1];
}
#ifdef ISM330DHCX_ACCEL_FS_RUNTIME
static const u16_t ism330dhcx_accel_fs_map[] = {2, 16, 4, 8};
static const u16_t ism330dhcx_accel_fs_sens[] = {1, 8, 2, 4};
static int ism330dhcx_accel_range_to_fs_val(s32_t range)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(ism330dhcx_accel_fs_map); i++) {
if (range == ism330dhcx_accel_fs_map[i]) {
return i;
}
}
return -EINVAL;
}
#endif
#ifdef ISM330DHCX_GYRO_FS_RUNTIME
static const u16_t ism330dhcx_gyro_fs_map[] = {250, 500, 1000, 2000, 125};
static const u16_t ism330dhcx_gyro_fs_sens[] = {2, 4, 8, 16, 1};
static int ism330dhcx_gyro_range_to_fs_val(s32_t range)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(ism330dhcx_gyro_fs_map); i++) {
if (range == ism330dhcx_gyro_fs_map[i]) {
return i;
}
}
return -EINVAL;
}
#endif
static inline int ism330dhcx_reboot(struct device *dev)
{
struct ism330dhcx_data *data = dev->driver_data;
if (ism330dhcx_boot_set(data->ctx, 1) < 0) {
return -EIO;
}
/* Wait sensor turn-on time as per datasheet */
k_busy_wait(35 * USEC_PER_MSEC);
return 0;
}
static int ism330dhcx_accel_set_fs_raw(struct device *dev, u8_t fs)
{
struct ism330dhcx_data *data = dev->driver_data;
if (ism330dhcx_xl_full_scale_set(data->ctx, fs) < 0) {
return -EIO;
}
data->accel_fs = fs;
return 0;
}
static int ism330dhcx_accel_set_odr_raw(struct device *dev, u8_t odr)
{
struct ism330dhcx_data *data = dev->driver_data;
if (ism330dhcx_xl_data_rate_set(data->ctx, odr) < 0) {
return -EIO;
}
data->accel_freq = ism330dhcx_odr_to_freq_val(odr);
return 0;
}
static int ism330dhcx_gyro_set_fs_raw(struct device *dev, u8_t fs)
{
struct ism330dhcx_data *data = dev->driver_data;
if (ism330dhcx_gy_full_scale_set(data->ctx, fs) < 0) {
return -EIO;
}
return 0;
}
static int ism330dhcx_gyro_set_odr_raw(struct device *dev, u8_t odr)
{
struct ism330dhcx_data *data = dev->driver_data;
if (ism330dhcx_gy_data_rate_set(data->ctx, odr) < 0) {
return -EIO;
}
return 0;
}
#ifdef ISM330DHCX_ACCEL_ODR_RUNTIME
static int ism330dhcx_accel_odr_set(struct device *dev, u16_t freq)
{
int odr;
odr = ism330dhcx_freq_to_odr_val(freq);
if (odr < 0) {
return odr;
}
if (ism330dhcx_accel_set_odr_raw(dev, odr) < 0) {
LOG_DBG("failed to set accelerometer sampling rate");
return -EIO;
}
return 0;
}
#endif
#ifdef ISM330DHCX_ACCEL_FS_RUNTIME
static int ism330dhcx_accel_range_set(struct device *dev, s32_t range)
{
int fs;
struct ism330dhcx_data *data = dev->driver_data;
fs = ism330dhcx_accel_range_to_fs_val(range);
if (fs < 0) {
return fs;
}
if (ism330dhcx_accel_set_fs_raw(dev, fs) < 0) {
LOG_DBG("failed to set accelerometer full-scale");
return -EIO;
}
data->acc_gain = (ism330dhcx_accel_fs_sens[fs] * GAIN_UNIT_XL);
return 0;
}
#endif
static int ism330dhcx_accel_config(struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
switch (attr) {
#ifdef ISM330DHCX_ACCEL_FS_RUNTIME
case SENSOR_ATTR_FULL_SCALE:
return ism330dhcx_accel_range_set(dev, sensor_ms2_to_g(val));
#endif
#ifdef ISM330DHCX_ACCEL_ODR_RUNTIME
case SENSOR_ATTR_SAMPLING_FREQUENCY:
return ism330dhcx_accel_odr_set(dev, val->val1);
#endif
default:
LOG_DBG("Accel attribute not supported.");
return -ENOTSUP;
}
return 0;
}
#ifdef ISM330DHCX_GYRO_ODR_RUNTIME
static int ism330dhcx_gyro_odr_set(struct device *dev, u16_t freq)
{
int odr;
odr = ism330dhcx_freq_to_odr_val(freq);
if (odr < 0) {
return odr;
}
if (ism330dhcx_gyro_set_odr_raw(dev, odr) < 0) {
LOG_DBG("failed to set gyroscope sampling rate");
return -EIO;
}
return 0;
}
#endif
#ifdef ISM330DHCX_GYRO_FS_RUNTIME
static int ism330dhcx_gyro_range_set(struct device *dev, s32_t range)
{
int fs;
struct ism330dhcx_data *data = dev->driver_data;
fs = ism330dhcx_gyro_range_to_fs_val(range);
if (fs < 0) {
return fs;
}
if (ism330dhcx_gyro_set_fs_raw(dev, fs) < 0) {
LOG_DBG("failed to set gyroscope full-scale");
return -EIO;
}
data->gyro_gain = (ism330dhcx_gyro_fs_sens[fs] * GAIN_UNIT_G);
return 0;
}
#endif
static int ism330dhcx_gyro_config(struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
switch (attr) {
#ifdef ISM330DHCX_GYRO_FS_RUNTIME
case SENSOR_ATTR_FULL_SCALE:
return ism330dhcx_gyro_range_set(dev, sensor_rad_to_degrees(val));
#endif
#ifdef ISM330DHCX_GYRO_ODR_RUNTIME
case SENSOR_ATTR_SAMPLING_FREQUENCY:
return ism330dhcx_gyro_odr_set(dev, val->val1);
#endif
default:
LOG_DBG("Gyro attribute not supported.");
return -ENOTSUP;
}
return 0;
}
static int ism330dhcx_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 ism330dhcx_accel_config(dev, chan, attr, val);
case SENSOR_CHAN_GYRO_XYZ:
return ism330dhcx_gyro_config(dev, chan, attr, val);
#if defined(CONFIG_ISM330DHCX_SENSORHUB)
case SENSOR_CHAN_MAGN_XYZ:
case SENSOR_CHAN_PRESS:
case SENSOR_CHAN_HUMIDITY:
return ism330dhcx_shub_config(dev, chan, attr, val);
#endif /* CONFIG_ISM330DHCX_SENSORHUB */
default:
LOG_WRN("attr_set() not supported on this channel.");
return -ENOTSUP;
}
return 0;
}
static int ism330dhcx_sample_fetch_accel(struct device *dev)
{
struct ism330dhcx_data *data = dev->driver_data;
union axis3bit16_t buf;
if (ism330dhcx_acceleration_raw_get(data->ctx, buf.u8bit) < 0) {
LOG_DBG("Failed to read sample");
return -EIO;
}
data->acc[0] = sys_le16_to_cpu(buf.i16bit[0]);
data->acc[1] = sys_le16_to_cpu(buf.i16bit[1]);
data->acc[2] = sys_le16_to_cpu(buf.i16bit[2]);
return 0;
}
static int ism330dhcx_sample_fetch_gyro(struct device *dev)
{
struct ism330dhcx_data *data = dev->driver_data;
union axis3bit16_t buf;
if (ism330dhcx_angular_rate_raw_get(data->ctx, buf.u8bit) < 0) {
LOG_DBG("Failed to read sample");
return -EIO;
}
data->gyro[0] = sys_le16_to_cpu(buf.i16bit[0]);
data->gyro[1] = sys_le16_to_cpu(buf.i16bit[1]);
data->gyro[2] = sys_le16_to_cpu(buf.i16bit[2]);
return 0;
}
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
static int ism330dhcx_sample_fetch_temp(struct device *dev)
{
struct ism330dhcx_data *data = dev->driver_data;
union axis1bit16_t buf;
if (ism330dhcx_temperature_raw_get(data->ctx, buf.u8bit) < 0) {
LOG_DBG("Failed to read sample");
return -EIO;
}
data->temp_sample = sys_le16_to_cpu(buf.i16bit);
return 0;
}
#endif
#if defined(CONFIG_ISM330DHCX_SENSORHUB)
static int ism330dhcx_sample_fetch_shub(struct device *dev)
{
if (ism330dhcx_shub_fetch_external_devs(dev) < 0) {
LOG_DBG("failed to read ext shub devices");
return -EIO;
}
return 0;
}
#endif /* CONFIG_ISM330DHCX_SENSORHUB */
static int ism330dhcx_sample_fetch(struct device *dev, enum sensor_channel chan)
{
switch (chan) {
case SENSOR_CHAN_ACCEL_XYZ:
ism330dhcx_sample_fetch_accel(dev);
#if defined(CONFIG_ISM330DHCX_SENSORHUB)
ism330dhcx_sample_fetch_shub(dev);
#endif
break;
case SENSOR_CHAN_GYRO_XYZ:
ism330dhcx_sample_fetch_gyro(dev);
break;
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
case SENSOR_CHAN_DIE_TEMP:
ism330dhcx_sample_fetch_temp(dev);
break;
#endif
case SENSOR_CHAN_ALL:
ism330dhcx_sample_fetch_accel(dev);
ism330dhcx_sample_fetch_gyro(dev);
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
ism330dhcx_sample_fetch_temp(dev);
#endif
#if defined(CONFIG_ISM330DHCX_SENSORHUB)
ism330dhcx_sample_fetch_shub(dev);
#endif
break;
default:
return -ENOTSUP;
}
return 0;
}
static inline void ism330dhcx_accel_convert(struct sensor_value *val, int raw_val,
u32_t sensitivity)
{
s64_t dval;
/* Sensitivity is exposed in ug/LSB */
/* Convert to m/s^2 */
dval = (s64_t)(raw_val) * sensitivity * SENSOR_G_DOUBLE;
val->val1 = (s32_t)(dval / 1000000);
val->val2 = (s32_t)(dval % 1000000);
}
static inline int ism330dhcx_accel_get_channel(enum sensor_channel chan,
struct sensor_value *val,
struct ism330dhcx_data *data,
u32_t sensitivity)
{
u8_t i;
switch (chan) {
case SENSOR_CHAN_ACCEL_X:
ism330dhcx_accel_convert(val, data->acc[0], sensitivity);
break;
case SENSOR_CHAN_ACCEL_Y:
ism330dhcx_accel_convert(val, data->acc[1], sensitivity);
break;
case SENSOR_CHAN_ACCEL_Z:
ism330dhcx_accel_convert(val, data->acc[2], sensitivity);
break;
case SENSOR_CHAN_ACCEL_XYZ:
for (i = 0; i < 3; i++) {
ism330dhcx_accel_convert(val++, data->acc[i], sensitivity);
}
break;
default:
return -ENOTSUP;
}
return 0;
}
static int ism330dhcx_accel_channel_get(enum sensor_channel chan,
struct sensor_value *val,
struct ism330dhcx_data *data)
{
return ism330dhcx_accel_get_channel(chan, val, data, data->acc_gain);
}
static inline void ism330dhcx_gyro_convert(struct sensor_value *val, int raw_val,
u32_t sensitivity)
{
s64_t dval;
/* Sensitivity is exposed in udps/LSB */
/* Convert to rad/s */
dval = (s64_t)(raw_val) * sensitivity * SENSOR_DEG2RAD_DOUBLE;
val->val1 = (s32_t)(dval / 1000000);
val->val2 = (s32_t)(dval % 1000000);
}
static inline int ism330dhcx_gyro_get_channel(enum sensor_channel chan,
struct sensor_value *val,
struct ism330dhcx_data *data,
u32_t sensitivity)
{
u8_t i;
switch (chan) {
case SENSOR_CHAN_GYRO_X:
ism330dhcx_gyro_convert(val, data->gyro[0], sensitivity);
break;
case SENSOR_CHAN_GYRO_Y:
ism330dhcx_gyro_convert(val, data->gyro[1], sensitivity);
break;
case SENSOR_CHAN_GYRO_Z:
ism330dhcx_gyro_convert(val, data->gyro[2], sensitivity);
break;
case SENSOR_CHAN_GYRO_XYZ:
for (i = 0; i < 3; i++) {
ism330dhcx_gyro_convert(val++, data->gyro[i], sensitivity);
}
break;
default:
return -ENOTSUP;
}
return 0;
}
static int ism330dhcx_gyro_channel_get(enum sensor_channel chan,
struct sensor_value *val,
struct ism330dhcx_data *data)
{
return ism330dhcx_gyro_get_channel(chan, val, data,
ISM330DHCX_DEFAULT_GYRO_SENSITIVITY);
}
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
static void ism330dhcx_gyro_channel_get_temp(struct sensor_value *val,
struct ism330dhcx_data *data)
{
/* val = temp_sample / 256 + 25 */
val->val1 = data->temp_sample / 256 + 25;
val->val2 = (data->temp_sample % 256) * (1000000 / 256);
}
#endif
#if defined(CONFIG_ISM330DHCX_SENSORHUB)
static inline void ism330dhcx_magn_convert(struct sensor_value *val, int raw_val,
u16_t sensitivity)
{
double dval;
/* Sensitivity is exposed in mgauss/LSB */
dval = (double)(raw_val * sensitivity);
val->val1 = (s32_t)dval / 1000000;
val->val2 = (s32_t)dval % 1000000;
}
static inline int ism330dhcx_magn_get_channel(enum sensor_channel chan,
struct sensor_value *val,
struct ism330dhcx_data *data)
{
s16_t sample[3];
int idx;
idx = ism330dhcx_shub_get_idx(SENSOR_CHAN_MAGN_XYZ);
if (idx < 0) {
LOG_DBG("external magn not supported");
return -ENOTSUP;
}
sample[0] = (s16_t)(data->ext_data[idx][0] |
(data->ext_data[idx][1] << 8));
sample[1] = (s16_t)(data->ext_data[idx][2] |
(data->ext_data[idx][3] << 8));
sample[2] = (s16_t)(data->ext_data[idx][4] |
(data->ext_data[idx][5] << 8));
switch (chan) {
case SENSOR_CHAN_MAGN_X:
ism330dhcx_magn_convert(val, sample[0], data->magn_gain);
break;
case SENSOR_CHAN_MAGN_Y:
ism330dhcx_magn_convert(val, sample[1], data->magn_gain);
break;
case SENSOR_CHAN_MAGN_Z:
ism330dhcx_magn_convert(val, sample[2], data->magn_gain);
break;
case SENSOR_CHAN_MAGN_XYZ:
ism330dhcx_magn_convert(val, sample[0], data->magn_gain);
ism330dhcx_magn_convert(val + 1, sample[1], data->magn_gain);
ism330dhcx_magn_convert(val + 2, sample[2], data->magn_gain);
break;
default:
return -ENOTSUP;
}
return 0;
}
static inline void ism330dhcx_hum_convert(struct sensor_value *val,
struct ism330dhcx_data *data)
{
float rh;
s16_t raw_val;
struct hts221_data *ht = &data->hts221;
int idx;
idx = ism330dhcx_shub_get_idx(SENSOR_CHAN_HUMIDITY);
if (idx < 0) {
LOG_DBG("external press/temp not supported");
return;
}
raw_val = ((s16_t)(data->ext_data[idx][0] |
(data->ext_data[idx][1] << 8)));
/* find relative humidty by linear interpolation */
rh = (ht->y1 - ht->y0) * raw_val + ht->x1 * ht->y0 - ht->x0 * ht->y1;
rh /= (ht->x1 - ht->x0);
/* convert humidity to integer and fractional part */
val->val1 = rh;
val->val2 = rh * 1000000;
}
static inline void ism330dhcx_press_convert(struct sensor_value *val,
struct ism330dhcx_data *data)
{
s32_t raw_val;
int idx;
idx = ism330dhcx_shub_get_idx(SENSOR_CHAN_PRESS);
if (idx < 0) {
LOG_DBG("external press/temp not supported");
return;
}
raw_val = (s32_t)(data->ext_data[idx][0] |
(data->ext_data[idx][1] << 8) |
(data->ext_data[idx][2] << 16));
/* Pressure sensitivity is 4096 LSB/hPa */
/* Convert raw_val to val in kPa */
val->val1 = (raw_val >> 12) / 10;
val->val2 = (raw_val >> 12) % 10 * 100000 +
(((s32_t)((raw_val) & 0x0FFF) * 100000L) >> 12);
}
static inline void ism330dhcx_temp_convert(struct sensor_value *val,
struct ism330dhcx_data *data)
{
s16_t raw_val;
int idx;
idx = ism330dhcx_shub_get_idx(SENSOR_CHAN_PRESS);
if (idx < 0) {
LOG_DBG("external press/temp not supported");
return;
}
raw_val = (s16_t)(data->ext_data[idx][3] |
(data->ext_data[idx][4] << 8));
/* Temperature sensitivity is 100 LSB/deg C */
val->val1 = raw_val / 100;
val->val2 = (s32_t)raw_val % 100 * (10000);
}
#endif
static int ism330dhcx_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct ism330dhcx_data *data = dev->driver_data;
switch (chan) {
case SENSOR_CHAN_ACCEL_X:
case SENSOR_CHAN_ACCEL_Y:
case SENSOR_CHAN_ACCEL_Z:
case SENSOR_CHAN_ACCEL_XYZ:
ism330dhcx_accel_channel_get(chan, val, data);
break;
case SENSOR_CHAN_GYRO_X:
case SENSOR_CHAN_GYRO_Y:
case SENSOR_CHAN_GYRO_Z:
case SENSOR_CHAN_GYRO_XYZ:
ism330dhcx_gyro_channel_get(chan, val, data);
break;
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
case SENSOR_CHAN_DIE_TEMP:
ism330dhcx_gyro_channel_get_temp(val, data);
break;
#endif
#if defined(CONFIG_ISM330DHCX_SENSORHUB)
case SENSOR_CHAN_MAGN_X:
case SENSOR_CHAN_MAGN_Y:
case SENSOR_CHAN_MAGN_Z:
case SENSOR_CHAN_MAGN_XYZ:
ism330dhcx_magn_get_channel(chan, val, data);
break;
case SENSOR_CHAN_HUMIDITY:
ism330dhcx_hum_convert(val, data);
break;
case SENSOR_CHAN_PRESS:
ism330dhcx_press_convert(val, data);
break;
case SENSOR_CHAN_AMBIENT_TEMP:
ism330dhcx_temp_convert(val, data);
break;
#endif
default:
return -ENOTSUP;
}
return 0;
}
static const struct sensor_driver_api ism330dhcx_api_funcs = {
.attr_set = ism330dhcx_attr_set,
#if CONFIG_ISM330DHCX_TRIGGER
.trigger_set = ism330dhcx_trigger_set,
#endif
.sample_fetch = ism330dhcx_sample_fetch,
.channel_get = ism330dhcx_channel_get,
};
static int ism330dhcx_init_chip(struct device *dev)
{
struct ism330dhcx_data *ism330dhcx = dev->driver_data;
u8_t chip_id;
if (ism330dhcx_device_id_get(ism330dhcx->ctx, &chip_id) < 0) {
LOG_DBG("Failed reading chip id");
return -EIO;
}
LOG_INF("chip id 0x%x", chip_id);
if (chip_id != ISM330DHCX_ID) {
LOG_DBG("Invalid chip id 0x%x", chip_id);
return -EIO;
}
/* reset device */
if (ism330dhcx_reset_set(ism330dhcx->ctx, 1) < 0) {
return -EIO;
}
k_busy_wait(100);
if (ism330dhcx_accel_set_fs_raw(dev,
ISM330DHCX_DEFAULT_ACCEL_FULLSCALE) < 0) {
LOG_DBG("failed to set accelerometer full-scale");
return -EIO;
}
ism330dhcx->acc_gain = ISM330DHCX_DEFAULT_ACCEL_SENSITIVITY;
ism330dhcx->accel_freq = ism330dhcx_odr_to_freq_val(CONFIG_ISM330DHCX_ACCEL_ODR);
if (ism330dhcx_accel_set_odr_raw(dev, CONFIG_ISM330DHCX_ACCEL_ODR) < 0) {
LOG_DBG("failed to set accelerometer sampling rate");
return -EIO;
}
if (ism330dhcx_gyro_set_fs_raw(dev, ISM330DHCX_DEFAULT_GYRO_FULLSCALE) < 0) {
LOG_DBG("failed to set gyroscope full-scale");
return -EIO;
}
ism330dhcx->gyro_gain = ISM330DHCX_DEFAULT_GYRO_SENSITIVITY;
ism330dhcx->gyro_freq = ism330dhcx_odr_to_freq_val(CONFIG_ISM330DHCX_GYRO_ODR);
if (ism330dhcx_gyro_set_odr_raw(dev, CONFIG_ISM330DHCX_GYRO_ODR) < 0) {
LOG_DBG("failed to set gyroscope sampling rate");
return -EIO;
}
/* Set FIFO bypass mode */
if (ism330dhcx_fifo_mode_set(ism330dhcx->ctx, ISM330DHCX_BYPASS_MODE) < 0) {
LOG_DBG("failed to set FIFO mode");
return -EIO;
}
if (ism330dhcx_block_data_update_set(ism330dhcx->ctx, 1) < 0) {
LOG_DBG("failed to set BDU mode");
return -EIO;
}
return 0;
}
static struct ism330dhcx_data ism330dhcx_data;
static const struct ism330dhcx_config ism330dhcx_config = {
.bus_name = DT_INST_0_ST_ISM330DHCX_BUS_NAME,
#if defined(DT_ST_ISM330DHCX_BUS_SPI)
.bus_init = ism330dhcx_spi_init,
.spi_conf.frequency = DT_INST_0_ST_ISM330DHCX_SPI_MAX_FREQUENCY,
.spi_conf.operation = (SPI_OP_MODE_MASTER | SPI_MODE_CPOL |
SPI_MODE_CPHA | SPI_WORD_SET(8) |
SPI_LINES_SINGLE),
.spi_conf.slave = DT_INST_0_ST_ISM330DHCX_BASE_ADDRESS,
#if defined(DT_INST_0_ST_ISM330DHCX_CS_GPIOS_CONTROLLER)
.gpio_cs_port = DT_INST_0_ST_ISM330DHCX_CS_GPIOS_CONTROLLER,
.cs_gpio = DT_INST_0_ST_ISM330DHCX_CS_GPIOS_PIN,
.spi_conf.cs = &ism330dhcx_data.cs_ctrl,
#else
.spi_conf.cs = NULL,
#endif
#elif defined(DT_ST_ISM330DHCX_BUS_I2C)
.bus_init = ism330dhcx_i2c_init,
.i2c_slv_addr = DT_INST_0_ST_ISM330DHCX_BASE_ADDRESS,
#else
#error "BUS MACRO NOT DEFINED IN DTS"
#endif
#ifdef CONFIG_ISM330DHCX_TRIGGER
#if defined(DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_CONTROLLER_1)
/* Two gpio pins declared in DTS */
#if defined(CONFIG_ISM330DHCX_INT_PIN_1)
.int_gpio_port = DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_CONTROLLER_0,
.int_gpio_pin = DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_PIN_0,
.int_gpio_flags = DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_FLAGS_0,
.int_pin = 1,
#elif defined(CONFIG_ISM330DHCX_INT_PIN_2)
.int_gpio_port = DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_CONTROLLER_1,
.int_gpio_pin = DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_PIN_1,
.int_gpio_flags = DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_FLAGS_1,
.int_pin = 2,
#endif /* CONFIG_ISM330DHCX_INT_PIN_* */
#else
/* One gpio pin declared in DTS */
.int_gpio_port = DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_CONTROLLER,
.int_gpio_pin = DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_PIN,
.int_gpio_flags = DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_FLAGS,
#if defined(CONFIG_ISM330DHCX_INT_PIN_1)
.int_pin = 1,
#elif defined(CONFIG_ISM330DHCX_INT_PIN_2)
.int_pin = 2,
#endif /* CONFIG_ISM330DHCX_INT_PIN_* */
#endif /* DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_CONTROLLER_1 */
#endif /* CONFIG_ISM330DHCX_TRIGGER */
};
static int ism330dhcx_init(struct device *dev)
{
const struct ism330dhcx_config * const config = dev->config->config_info;
struct ism330dhcx_data *data = dev->driver_data;
data->bus = device_get_binding(config->bus_name);
if (!data->bus) {
LOG_DBG("master not found: %s",
config->bus_name);
return -EINVAL;
}
config->bus_init(dev);
#ifdef CONFIG_ISM330DHCX_TRIGGER
if (ism330dhcx_init_interrupt(dev) < 0) {
LOG_ERR("Failed to initialize interrupt.");
return -EIO;
}
#endif
if (ism330dhcx_init_chip(dev) < 0) {
LOG_DBG("failed to initialize chip");
return -EIO;
}
#ifdef CONFIG_ISM330DHCX_SENSORHUB
if (ism330dhcx_shub_init(dev) < 0) {
LOG_DBG("failed to initialize external chip");
return -EIO;
}
#endif
return 0;
}
static struct ism330dhcx_data ism330dhcx_data;
DEVICE_AND_API_INIT(ism330dhcx, DT_INST_0_ST_ISM330DHCX_LABEL, ism330dhcx_init,
&ism330dhcx_data, &ism330dhcx_config, POST_KERNEL,
CONFIG_SENSOR_INIT_PRIORITY, &ism330dhcx_api_funcs);

View file

@ -0,0 +1,214 @@
/* ST Microelectronics ISM330DHCX 6-axis IMU sensor driver
*
* Copyright (c) 2020 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://www.st.com/resource/en/datasheet/ism330dhcx.pdf
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_ISM330DHCX_ISM330DHCX_H_
#define ZEPHYR_DRIVERS_SENSOR_ISM330DHCX_ISM330DHCX_H_
#include <drivers/sensor.h>
#include <zephyr/types.h>
#include <drivers/gpio.h>
#include <drivers/spi.h>
#include <sys/util.h>
#include "ism330dhcx_reg.h"
union axis3bit16_t {
s16_t i16bit[3];
u8_t u8bit[6];
};
union axis1bit16_t {
s16_t i16bit;
u8_t u8bit[2];
};
#define ISM330DHCX_EN_BIT 0x01
#define ISM330DHCX_DIS_BIT 0x00
/* Accel sensor sensitivity grain is 61 ug/LSB */
#define GAIN_UNIT_XL (61LL)
/* Gyro sensor sensitivity grain is 4.375 udps/LSB */
#define GAIN_UNIT_G (4375LL)
#define SENSOR_PI_DOUBLE (SENSOR_PI / 1000000.0)
#define SENSOR_DEG2RAD_DOUBLE (SENSOR_PI_DOUBLE / 180)
#define SENSOR_G_DOUBLE (SENSOR_G / 1000000.0)
#if CONFIG_ISM330DHCX_ACCEL_FS == 0
#define ISM330DHCX_ACCEL_FS_RUNTIME 1
#define ISM330DHCX_DEFAULT_ACCEL_FULLSCALE 0
#define ISM330DHCX_DEFAULT_ACCEL_SENSITIVITY GAIN_UNIT_XL
#elif CONFIG_ISM330DHCX_ACCEL_FS == 2
#define ISM330DHCX_DEFAULT_ACCEL_FULLSCALE 0
#define ISM330DHCX_DEFAULT_ACCEL_SENSITIVITY GAIN_UNIT_XL
#elif CONFIG_ISM330DHCX_ACCEL_FS == 4
#define ISM330DHCX_DEFAULT_ACCEL_FULLSCALE 2
#define ISM330DHCX_DEFAULT_ACCEL_SENSITIVITY (2.0 * GAIN_UNIT_XL)
#elif CONFIG_ISM330DHCX_ACCEL_FS == 8
#define ISM330DHCX_DEFAULT_ACCEL_FULLSCALE 3
#define ISM330DHCX_DEFAULT_ACCEL_SENSITIVITY (4.0 * GAIN_UNIT_XL)
#elif CONFIG_ISM330DHCX_ACCEL_FS == 16
#define ISM330DHCX_DEFAULT_ACCEL_FULLSCALE 1
#define ISM330DHCX_DEFAULT_ACCEL_SENSITIVITY (8.0 * GAIN_UNIT_XL)
#endif
#if (CONFIG_ISM330DHCX_ACCEL_ODR == 0)
#define ISM330DHCX_ACCEL_ODR_RUNTIME 1
#endif
#define GYRO_FULLSCALE_125 4
#if CONFIG_ISM330DHCX_GYRO_FS == 0
#define ISM330DHCX_GYRO_FS_RUNTIME 1
#define ISM330DHCX_DEFAULT_GYRO_FULLSCALE 4
#define ISM330DHCX_DEFAULT_GYRO_SENSITIVITY GAIN_UNIT_G
#elif CONFIG_ISM330DHCX_GYRO_FS == 125
#define ISM330DHCX_DEFAULT_GYRO_FULLSCALE 4
#define ISM330DHCX_DEFAULT_GYRO_SENSITIVITY GAIN_UNIT_G
#elif CONFIG_ISM330DHCX_GYRO_FS == 250
#define ISM330DHCX_DEFAULT_GYRO_FULLSCALE 0
#define ISM330DHCX_DEFAULT_GYRO_SENSITIVITY (2.0 * GAIN_UNIT_G)
#elif CONFIG_ISM330DHCX_GYRO_FS == 500
#define ISM330DHCX_DEFAULT_GYRO_FULLSCALE 1
#define ISM330DHCX_DEFAULT_GYRO_SENSITIVITY (4.0 * GAIN_UNIT_G)
#elif CONFIG_ISM330DHCX_GYRO_FS == 1000
#define ISM330DHCX_DEFAULT_GYRO_FULLSCALE 2
#define ISM330DHCX_DEFAULT_GYRO_SENSITIVITY (8.0 * GAIN_UNIT_G)
#elif CONFIG_ISM330DHCX_GYRO_FS == 2000
#define ISM330DHCX_DEFAULT_GYRO_FULLSCALE 3
#define ISM330DHCX_DEFAULT_GYRO_SENSITIVITY (16.0 * GAIN_UNIT_G)
#endif
#if (CONFIG_ISM330DHCX_GYRO_ODR == 0)
#define ISM330DHCX_GYRO_ODR_RUNTIME 1
#endif
struct ism330dhcx_config {
char *bus_name;
int (*bus_init)(struct device *dev);
#ifdef CONFIG_ISM330DHCX_TRIGGER
const char *int_gpio_port;
u8_t int_gpio_pin;
u8_t int_gpio_flags;
u8_t int_pin;
#endif /* CONFIG_ISM330DHCX_TRIGGER */
#ifdef DT_ST_ISM330DHCX_BUS_I2C
u16_t i2c_slv_addr;
#elif DT_ST_ISM330DHCX_BUS_SPI
struct spi_config spi_conf;
#if defined(DT_INST_0_ST_ISM330DHCX_CS_GPIOS_CONTROLLER)
const char *gpio_cs_port;
u8_t cs_gpio;
#endif /* DT_INST_0_ST_ISM330DHCX_CS_GPIOS_CONTROLLER */
#endif /* DT_ST_ISM330DHCX_BUS_I2C */
};
union samples {
u8_t raw[6];
struct {
s16_t axis[3];
};
} __aligned(2);
/* sensor data forward declaration (member definition is below) */
struct ism330dhcx_data;
struct ism330dhcx_tf {
int (*read_data)(struct ism330dhcx_data *data, u8_t reg_addr,
u8_t *value, u8_t len);
int (*write_data)(struct ism330dhcx_data *data, u8_t reg_addr,
u8_t *value, u8_t len);
int (*read_reg)(struct ism330dhcx_data *data, u8_t reg_addr,
u8_t *value);
int (*write_reg)(struct ism330dhcx_data *data, u8_t reg_addr,
u8_t value);
int (*update_reg)(struct ism330dhcx_data *data, u8_t reg_addr,
u8_t mask, u8_t value);
};
#define ISM330DHCX_SHUB_MAX_NUM_SLVS 2
struct ism330dhcx_data {
struct device *bus;
s16_t acc[3];
u32_t acc_gain;
s16_t gyro[3];
u32_t gyro_gain;
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
int temp_sample;
#endif
#if defined(CONFIG_ISM330DHCX_SENSORHUB)
u8_t ext_data[2][6];
u16_t magn_gain;
struct hts221_data {
s16_t x0;
s16_t x1;
s16_t y0;
s16_t y1;
} hts221;
#endif /* CONFIG_ISM330DHCX_SENSORHUB */
stmdev_ctx_t *ctx;
#ifdef DT_ST_ISM330DHCX_BUS_I2C
stmdev_ctx_t ctx_i2c;
#elif DT_ST_ISM330DHCX_BUS_SPI
stmdev_ctx_t ctx_spi;
#endif
u16_t accel_freq;
u8_t accel_fs;
u16_t gyro_freq;
u8_t gyro_fs;
#ifdef CONFIG_ISM330DHCX_TRIGGER
struct device *gpio;
struct gpio_callback gpio_cb;
sensor_trigger_handler_t handler_drdy_acc;
sensor_trigger_handler_t handler_drdy_gyr;
sensor_trigger_handler_t handler_drdy_temp;
#if defined(CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD)
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_ISM330DHCX_THREAD_STACK_SIZE);
struct k_thread thread;
struct k_sem gpio_sem;
#elif defined(CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD)
struct k_work work;
struct device *dev;
#endif
#endif /* CONFIG_ISM330DHCX_TRIGGER */
#if defined(DT_INST_0_ST_ISM330DHCX_CS_GPIOS_CONTROLLER)
struct spi_cs_control cs_ctrl;
#endif
};
int ism330dhcx_spi_init(struct device *dev);
int ism330dhcx_i2c_init(struct device *dev);
#if defined(CONFIG_ISM330DHCX_SENSORHUB)
int ism330dhcx_shub_init(struct device *dev);
int ism330dhcx_shub_fetch_external_devs(struct device *dev);
int ism330dhcx_shub_get_idx(enum sensor_channel type);
int ism330dhcx_shub_config(struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val);
#endif /* CONFIG_ISM330DHCX_SENSORHUB */
#ifdef CONFIG_ISM330DHCX_TRIGGER
int ism330dhcx_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int ism330dhcx_init_interrupt(struct device *dev);
#endif
#endif /* ZEPHYR_DRIVERS_SENSOR_ISM330DHCX_ISM330DHCX_H_ */

View file

@ -0,0 +1,53 @@
/* ST Microelectronics ISM330DHCX 6-axis IMU sensor driver
*
* Copyright (c) 2020 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://www.st.com/resource/en/datasheet/ism330dhcx.pdf
*/
#include <string.h>
#include <drivers/i2c.h>
#include <logging/log.h>
#include "ism330dhcx.h"
#ifdef DT_ST_ISM330DHCX_BUS_I2C
LOG_MODULE_DECLARE(ISM330DHCX, CONFIG_SENSOR_LOG_LEVEL);
static int ism330dhcx_i2c_read(struct device *dev, u8_t reg_addr,
u8_t *value, u8_t len)
{
struct ism330dhcx_data *data = dev->driver_data;
const struct ism330dhcx_config *cfg = dev->config->config_info;
return i2c_burst_read(data->bus, cfg->i2c_slv_addr,
reg_addr, value, len);
}
static int ism330dhcx_i2c_write(struct device *dev, u8_t reg_addr,
u8_t *value, u8_t len)
{
struct ism330dhcx_data *data = dev->driver_data;
const struct ism330dhcx_config *cfg = dev->config->config_info;
return i2c_burst_write(data->bus, cfg->i2c_slv_addr,
reg_addr, value, len);
}
int ism330dhcx_i2c_init(struct device *dev)
{
struct ism330dhcx_data *data = dev->driver_data;
data->ctx_i2c.read_reg = (stmdev_read_ptr) ism330dhcx_i2c_read,
data->ctx_i2c.write_reg = (stmdev_write_ptr) ism330dhcx_i2c_write,
data->ctx = &data->ctx_i2c;
data->ctx->handle = dev;
return 0;
}
#endif /* DT_ST_ISM330DHCX_BUS_I2C */

View file

@ -0,0 +1,789 @@
/* ST Microelectronics ISM330DHCX 6-axis IMU sensor driver
*
* Copyright (c) 2020 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://www.st.com/resource/en/datasheet/ism330dhcx.pdf
*/
#include <device.h>
#include <drivers/i2c.h>
#include <sys/byteorder.h>
#include <sys/__assert.h>
#include <sys/util.h>
#include <kernel.h>
#include <drivers/sensor.h>
#include <logging/log.h>
#include "ism330dhcx.h"
LOG_MODULE_DECLARE(ISM330DHCX, CONFIG_SENSOR_LOG_LEVEL);
#define ISM330DHCX_SHUB_DATA_OUT 0x02
#define ISM330DHCX_SHUB_SLV0_ADDR 0x15
#define ISM330DHCX_SHUB_SLV0_SUBADDR 0x16
#define ISM330DHCX_SHUB_SLV0_CONFIG 0x17
#define ISM330DHCX_SHUB_SLV1_ADDR 0x18
#define ISM330DHCX_SHUB_SLV1_SUBADDR 0x19
#define ISM330DHCX_SHUB_SLV1_CONFIG 0x1A
#define ISM330DHCX_SHUB_SLV2_ADDR 0x1B
#define ISM330DHCX_SHUB_SLV2_SUBADDR 0x1C
#define ISM330DHCX_SHUB_SLV2_CONFIG 0x1D
#define ISM330DHCX_SHUB_SLV3_ADDR 0x1E
#define ISM330DHCX_SHUB_SLV3_SUBADDR 0x1F
#define ISM330DHCX_SHUB_SLV3_CONFIG 0x20
#define ISM330DHCX_SHUB_SLV0_DATAWRITE 0x21
#define ISM330DHCX_SHUB_STATUS_MASTER 0x22
#define ISM330DHCX_SHUB_STATUS_SLV0_NACK BIT(3)
#define ISM330DHCX_SHUB_STATUS_ENDOP BIT(0)
#define ISM330DHCX_SHUB_SLVX_WRITE 0x0
#define ISM330DHCX_SHUB_SLVX_READ 0x1
static u8_t num_ext_dev;
static u8_t shub_ext[ISM330DHCX_SHUB_MAX_NUM_SLVS];
static int ism330dhcx_shub_write_slave_reg(struct ism330dhcx_data *data,
u8_t slv_addr, u8_t slv_reg,
u8_t *value, u16_t len);
static int ism330dhcx_shub_read_slave_reg(struct ism330dhcx_data *data,
u8_t slv_addr, u8_t slv_reg,
u8_t *value, u16_t len);
static void ism330dhcx_shub_enable(struct ism330dhcx_data *data, u8_t enable);
/*
* LIS2MDL magn device specific part
*/
#if defined(CONFIG_ISM330DHCX_EXT_LIS2MDL) || defined(CONFIG_ISM330DHCX_EXT_IIS2MDC)
#define LIS2MDL_CFG_REG_A 0x60
#define LIS2MDL_CFG_REG_B 0x61
#define LIS2MDL_CFG_REG_C 0x62
#define LIS2MDL_STATUS_REG 0x67
#define LIS2MDL_SW_RESET 0x20
#define LIS2MDL_ODR_10HZ 0x00
#define LIS2MDL_ODR_100HZ 0x0C
#define LIS2MDL_OFF_CANC 0x02
#define LIS2MDL_SENSITIVITY 1500
static int ism330dhcx_lis2mdl_init(struct ism330dhcx_data *data, u8_t i2c_addr)
{
u8_t mag_cfg[2];
data->magn_gain = LIS2MDL_SENSITIVITY;
/* sw reset device */
mag_cfg[0] = LIS2MDL_SW_RESET;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
LIS2MDL_CFG_REG_A, mag_cfg, 1);
k_sleep(K_MSEC(10)); /* turn-on time in ms */
/* configure mag */
mag_cfg[0] = LIS2MDL_ODR_10HZ;
mag_cfg[1] = LIS2MDL_OFF_CANC;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
LIS2MDL_CFG_REG_A, mag_cfg, 2);
return 0;
}
static const u16_t lis2mdl_map[] = {10, 20, 50, 100};
static int ism330dhcx_lis2mdl_odr_set(struct ism330dhcx_data *data,
u8_t i2c_addr, u16_t freq)
{
u8_t odr, cfg;
for (odr = 0; odr < ARRAY_SIZE(lis2mdl_map); odr++) {
if (freq == lis2mdl_map[odr]) {
break;
}
}
if (odr == ARRAY_SIZE(lis2mdl_map)) {
LOG_DBG("shub: LIS2MDL freq val %d not supported.", freq);
return -ENOTSUP;
}
cfg = (odr << 2);
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
LIS2MDL_CFG_REG_A, &cfg, 1);
ism330dhcx_shub_enable(data, 1);
return 0;
}
static int ism330dhcx_lis2mdl_conf(struct ism330dhcx_data *data, u8_t i2c_addr,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
switch (attr) {
case SENSOR_ATTR_SAMPLING_FREQUENCY:
return ism330dhcx_lis2mdl_odr_set(data, i2c_addr, val->val1);
default:
LOG_DBG("shub: LIS2MDL attribute not supported.");
return -ENOTSUP;
}
return 0;
}
#endif /* CONFIG_ISM330DHCX_EXT_LIS2MDL || CONFIG_ISM330DHCX_EXT_IIS2MDC */
/*
* HTS221 humidity device specific part
*/
#ifdef CONFIG_ISM330DHCX_EXT_HTS221
#define HTS221_AUTOINCREMENT BIT(7)
#define HTS221_REG_CTRL1 0x20
#define HTS221_ODR_1HZ 0x01
#define HTS221_BDU 0x04
#define HTS221_PD 0x80
#define HTS221_REG_CONV_START 0x30
static int lsmdso_hts221_read_conv_data(struct ism330dhcx_data *data,
u8_t i2c_addr)
{
u8_t buf[16], i;
struct hts221_data *ht = &data->hts221;
for (i = 0; i < sizeof(buf); i += 7) {
unsigned char len = MIN(7, sizeof(buf) - i);
if (ism330dhcx_shub_read_slave_reg(data, i2c_addr,
(HTS221_REG_CONV_START + i) |
HTS221_AUTOINCREMENT,
&buf[i], len) < 0) {
LOG_DBG("shub: failed to read hts221 conv data");
return -EIO;
}
}
ht->y0 = buf[0] / 2;
ht->y1 = buf[1] / 2;
ht->x0 = sys_le16_to_cpu(buf[6] | (buf[7] << 8));
ht->x1 = sys_le16_to_cpu(buf[10] | (buf[11] << 8));
return 0;
}
static int ism330dhcx_hts221_init(struct ism330dhcx_data *data, u8_t i2c_addr)
{
u8_t hum_cfg;
/* configure ODR and BDU */
hum_cfg = HTS221_ODR_1HZ | HTS221_BDU | HTS221_PD;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
HTS221_REG_CTRL1, &hum_cfg, 1);
return lsmdso_hts221_read_conv_data(data, i2c_addr);
}
static const u16_t hts221_map[] = {0, 1, 7, 12};
static int ism330dhcx_hts221_odr_set(struct ism330dhcx_data *data,
u8_t i2c_addr, u16_t freq)
{
u8_t odr, cfg;
for (odr = 0; odr < ARRAY_SIZE(hts221_map); odr++) {
if (freq == hts221_map[odr]) {
break;
}
}
if (odr == ARRAY_SIZE(hts221_map)) {
LOG_DBG("shub: HTS221 freq val %d not supported.", freq);
return -ENOTSUP;
}
cfg = odr | HTS221_BDU | HTS221_PD;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
HTS221_REG_CTRL1, &cfg, 1);
ism330dhcx_shub_enable(data, 1);
return 0;
}
static int ism330dhcx_hts221_conf(struct ism330dhcx_data *data, u8_t i2c_addr,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
switch (attr) {
case SENSOR_ATTR_SAMPLING_FREQUENCY:
return ism330dhcx_hts221_odr_set(data, i2c_addr, val->val1);
default:
LOG_DBG("shub: HTS221 attribute not supported.");
return -ENOTSUP;
}
return 0;
}
#endif /* CONFIG_ISM330DHCX_EXT_HTS221 */
/*
* LPS22HB baro/temp device specific part
*/
#ifdef CONFIG_ISM330DHCX_EXT_LPS22HB
#define LPS22HB_CTRL_REG1 0x10
#define LPS22HB_CTRL_REG2 0x11
#define LPS22HB_SW_RESET 0x04
#define LPS22HB_ODR_10HZ 0x20
#define LPS22HB_LPF_EN 0x08
#define LPS22HB_BDU_EN 0x02
static int ism330dhcx_lps22hb_init(struct ism330dhcx_data *data, u8_t i2c_addr)
{
u8_t baro_cfg[2];
/* sw reset device */
baro_cfg[0] = LPS22HB_SW_RESET;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
LPS22HB_CTRL_REG2, baro_cfg, 1);
k_sleep(K_MSEC(1)); /* turn-on time in ms */
/* configure device */
baro_cfg[0] = LPS22HB_ODR_10HZ | LPS22HB_LPF_EN | LPS22HB_BDU_EN;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
LPS22HB_CTRL_REG1, baro_cfg, 1);
return 0;
}
#endif /* CONFIG_ISM330DHCX_EXT_LPS22HB */
/*
* LPS22HH baro/temp device specific part
*/
#ifdef CONFIG_ISM330DHCX_EXT_LPS22HH
#define LPS22HH_CTRL_REG1 0x10
#define LPS22HH_CTRL_REG2 0x11
#define LPS22HH_SW_RESET 0x04
#define LPS22HH_IF_ADD_INC 0x10
#define LPS22HH_ODR_10HZ 0x20
#define LPS22HH_LPF_EN 0x08
#define LPS22HH_BDU_EN 0x02
static int ism330dhcx_lps22hh_init(struct ism330dhcx_data *data, u8_t i2c_addr)
{
u8_t baro_cfg[2];
/* sw reset device */
baro_cfg[0] = LPS22HH_SW_RESET;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
LPS22HH_CTRL_REG2, baro_cfg, 1);
k_sleep(K_MSEC(100)); /* turn-on time in ms */
/* configure device */
baro_cfg[0] = LPS22HH_IF_ADD_INC;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
LPS22HH_CTRL_REG2, baro_cfg, 1);
baro_cfg[0] = LPS22HH_ODR_10HZ | LPS22HH_LPF_EN | LPS22HH_BDU_EN;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
LPS22HH_CTRL_REG1, baro_cfg, 1);
return 0;
}
static const u16_t lps22hh_map[] = {0, 1, 10, 25, 50, 75, 100, 200};
static int ism330dhcx_lps22hh_odr_set(struct ism330dhcx_data *data,
u8_t i2c_addr, u16_t freq)
{
u8_t odr, cfg;
for (odr = 0; odr < ARRAY_SIZE(lps22hh_map); odr++) {
if (freq == lps22hh_map[odr]) {
break;
}
}
if (odr == ARRAY_SIZE(lps22hh_map)) {
LOG_DBG("shub: LPS22HH freq val %d not supported.", freq);
return -ENOTSUP;
}
cfg = (odr << 4) | LPS22HH_LPF_EN | LPS22HH_BDU_EN;
ism330dhcx_shub_write_slave_reg(data, i2c_addr,
LPS22HH_CTRL_REG1, &cfg, 1);
ism330dhcx_shub_enable(data, 1);
return 0;
}
static int ism330dhcx_lps22hh_conf(struct ism330dhcx_data *data, u8_t i2c_addr,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
switch (attr) {
case SENSOR_ATTR_SAMPLING_FREQUENCY:
return ism330dhcx_lps22hh_odr_set(data, i2c_addr, val->val1);
default:
LOG_DBG("shub: LPS22HH attribute not supported.");
return -ENOTSUP;
}
return 0;
}
#endif /* CONFIG_ISM330DHCX_EXT_LPS22HH */
/* List of supported external sensors */
static struct ism330dhcx_shub_slist {
enum sensor_channel type;
u8_t i2c_addr[2];
u8_t ext_i2c_addr;
u8_t wai_addr;
u8_t wai_val;
u8_t out_data_addr;
u8_t out_data_len;
u8_t sh_out_reg;
int (*dev_init)(struct ism330dhcx_data *data, u8_t i2c_addr);
int (*dev_conf)(struct ism330dhcx_data *data, u8_t i2c_addr,
enum sensor_channel chan, enum sensor_attribute attr,
const struct sensor_value *val);
} ism330dhcx_shub_slist[] = {
#if defined(CONFIG_ISM330DHCX_EXT_LIS2MDL) || defined(CONFIG_ISM330DHCX_EXT_IIS2MDC)
{
/* LIS2MDL */
.type = SENSOR_CHAN_MAGN_XYZ,
.i2c_addr = { 0x1E },
.wai_addr = 0x4F,
.wai_val = 0x40,
.out_data_addr = 0x68,
.out_data_len = 0x06,
.dev_init = (ism330dhcx_lis2mdl_init),
.dev_conf = (ism330dhcx_lis2mdl_conf),
},
#endif /* CONFIG_ISM330DHCX_EXT_LIS2MDL || CONFIG_ISM330DHCX_EXT_IIS2MDC */
#ifdef CONFIG_ISM330DHCX_EXT_HTS221
{
/* HTS221 */
.type = SENSOR_CHAN_HUMIDITY,
.i2c_addr = { 0x5F },
.wai_addr = 0x0F,
.wai_val = 0xBC,
.out_data_addr = 0x28 | HTS221_AUTOINCREMENT,
.out_data_len = 0x02,
.dev_init = (ism330dhcx_hts221_init),
.dev_conf = (ism330dhcx_hts221_conf),
},
#endif /* CONFIG_ISM330DHCX_EXT_HTS221 */
#ifdef CONFIG_ISM330DHCX_EXT_LPS22HB
{
/* LPS22HB */
.type = SENSOR_CHAN_PRESS,
.i2c_addr = { 0x5C, 0x5D },
.wai_addr = 0x0F,
.wai_val = 0xB1,
.out_data_addr = 0x28,
.out_data_len = 0x05,
.dev_init = (ism330dhcx_lps22hb_init),
},
#endif /* CONFIG_ISM330DHCX_EXT_LPS22HB */
#ifdef CONFIG_ISM330DHCX_EXT_LPS22HH
{
/* LPS22HH */
.type = SENSOR_CHAN_PRESS,
.i2c_addr = { 0x5C, 0x5D },
.wai_addr = 0x0F,
.wai_val = 0xB3,
.out_data_addr = 0x28,
.out_data_len = 0x05,
.dev_init = (ism330dhcx_lps22hh_init),
.dev_conf = (ism330dhcx_lps22hh_conf),
},
#endif /* CONFIG_ISM330DHCX_EXT_LPS22HH */
};
static inline void ism330dhcx_shub_wait_completed(struct ism330dhcx_data *data)
{
u16_t freq;
freq = (data->accel_freq == 0) ? 26 : data->accel_freq;
k_sleep((2000U / freq) + 1);
}
static inline void ism330dhcx_shub_embedded_en(struct ism330dhcx_data *data, bool on)
{
if (on) {
(void) ism330dhcx_mem_bank_set(data->ctx, ISM330DHCX_SENSOR_HUB_BANK);
} else {
(void) ism330dhcx_mem_bank_set(data->ctx, ISM330DHCX_USER_BANK);
}
k_busy_wait(150);
}
static int ism330dhcx_shub_read_embedded_regs(struct ism330dhcx_data *data,
u8_t reg_addr,
u8_t *value, int len)
{
ism330dhcx_shub_embedded_en(data, true);
if (ism330dhcx_read_reg(data->ctx, reg_addr, value, len) < 0) {
LOG_DBG("shub: failed to read external reg: %02x", reg_addr);
ism330dhcx_shub_embedded_en(data, false);
return -EIO;
}
ism330dhcx_shub_embedded_en(data, false);
return 0;
}
static int ism330dhcx_shub_write_embedded_regs(struct ism330dhcx_data *data,
u8_t reg_addr,
u8_t *value, u8_t len)
{
ism330dhcx_shub_embedded_en(data, true);
if (ism330dhcx_write_reg(data->ctx, reg_addr, value, len) < 0) {
LOG_DBG("shub: failed to write external reg: %02x", reg_addr);
ism330dhcx_shub_embedded_en(data, false);
return -EIO;
}
ism330dhcx_shub_embedded_en(data, false);
return 0;
}
static void ism330dhcx_shub_enable(struct ism330dhcx_data *data, u8_t enable)
{
/* Enable Accel @26hz */
if (!data->accel_freq) {
u8_t odr = (enable) ? 2 : 0;
if (ism330dhcx_xl_data_rate_set(data->ctx, odr) < 0) {
LOG_DBG("shub: failed to set XL sampling rate");
return;
}
}
ism330dhcx_shub_embedded_en(data, true);
if (ism330dhcx_sh_master_set(data->ctx, enable) < 0) {
LOG_DBG("shub: failed to set master on");
ism330dhcx_shub_embedded_en(data, false);
return;
}
ism330dhcx_shub_embedded_en(data, false);
}
/* must be called with master on */
static int ism330dhcx_shub_check_slv0_nack(struct ism330dhcx_data *data)
{
u8_t status;
if (ism330dhcx_shub_read_embedded_regs(data, ISM330DHCX_SHUB_STATUS_MASTER,
&status, 1) < 0) {
LOG_DBG("shub: error reading embedded reg");
return -EIO;
}
if (status & (ISM330DHCX_SHUB_STATUS_SLV0_NACK)) {
LOG_DBG("shub: SLV0 nacked");
return -EIO;
}
return 0;
}
/*
* use SLV0 for generic read to slave device
*/
static int ism330dhcx_shub_read_slave_reg(struct ism330dhcx_data *data,
u8_t slv_addr, u8_t slv_reg,
u8_t *value, u16_t len)
{
u8_t slave[3];
slave[0] = (slv_addr << 1) | ISM330DHCX_SHUB_SLVX_READ;
slave[1] = slv_reg;
slave[2] = (len & 0x7);
if (ism330dhcx_shub_write_embedded_regs(data, ISM330DHCX_SHUB_SLV0_ADDR,
slave, 3) < 0) {
LOG_DBG("shub: error writing embedded reg");
return -EIO;
}
/* turn SH on */
ism330dhcx_shub_enable(data, 1);
ism330dhcx_shub_wait_completed(data);
if (ism330dhcx_shub_check_slv0_nack(data) < 0) {
ism330dhcx_shub_enable(data, 0);
return -EIO;
}
/* read data from external slave */
ism330dhcx_shub_embedded_en(data, true);
if (ism330dhcx_read_reg(data->ctx, ISM330DHCX_SHUB_DATA_OUT,
value, len) < 0) {
LOG_DBG("shub: error reading sensor data");
ism330dhcx_shub_embedded_en(data, false);
return -EIO;
}
ism330dhcx_shub_embedded_en(data, false);
ism330dhcx_shub_enable(data, 0);
return 0;
}
/*
* use SLV0 to configure slave device
*/
static int ism330dhcx_shub_write_slave_reg(struct ism330dhcx_data *data,
u8_t slv_addr, u8_t slv_reg,
u8_t *value, u16_t len)
{
u8_t slv_cfg[3];
u8_t cnt = 0U;
while (cnt < len) {
slv_cfg[0] = (slv_addr << 1) & ~ISM330DHCX_SHUB_SLVX_READ;
slv_cfg[1] = slv_reg + cnt;
if (ism330dhcx_shub_write_embedded_regs(data,
ISM330DHCX_SHUB_SLV0_ADDR,
slv_cfg, 2) < 0) {
LOG_DBG("shub: error writing embedded reg");
return -EIO;
}
slv_cfg[0] = value[cnt];
if (ism330dhcx_shub_write_embedded_regs(data,
ISM330DHCX_SHUB_SLV0_DATAWRITE,
slv_cfg, 1) < 0) {
LOG_DBG("shub: error writing embedded reg");
return -EIO;
}
/* turn SH on */
ism330dhcx_shub_enable(data, 1);
ism330dhcx_shub_wait_completed(data);
if (ism330dhcx_shub_check_slv0_nack(data) < 0) {
ism330dhcx_shub_enable(data, 0);
return -EIO;
}
ism330dhcx_shub_enable(data, 0);
cnt++;
}
/* Put SLV0 in IDLE mode */
slv_cfg[0] = 0x7;
slv_cfg[1] = 0x0;
slv_cfg[2] = 0x0;
if (ism330dhcx_shub_write_embedded_regs(data, ISM330DHCX_SHUB_SLV0_ADDR,
slv_cfg, 3) < 0) {
LOG_DBG("shub: error writing embedded reg");
return -EIO;
}
return 0;
}
/*
* SLAVEs configurations:
*
* - SLAVE 0: used for configuring all slave devices
* - SLAVE 1: used as data read channel for external slave device #1
* - SLAVE 2: used as data read channel for external slave device #2
* - SLAVE 3: used for generic reads while data channel is enabled
*/
static int ism330dhcx_shub_set_data_channel(struct ism330dhcx_data *data)
{
u8_t n, i, slv_cfg[6];
struct ism330dhcx_shub_slist *sp;
/* Set data channel for slave devices */
for (n = 0; n < num_ext_dev; n++) {
sp = &ism330dhcx_shub_slist[shub_ext[n]];
i = n * 3;
slv_cfg[i] = (sp->ext_i2c_addr << 1) | ISM330DHCX_SHUB_SLVX_READ;
slv_cfg[i + 1] = sp->out_data_addr;
slv_cfg[i + 2] = sp->out_data_len;
}
if (ism330dhcx_shub_write_embedded_regs(data,
ISM330DHCX_SHUB_SLV1_ADDR,
slv_cfg, n*3) < 0) {
LOG_DBG("shub: error writing embedded reg");
return -EIO;
}
/* Configure the master */
ism330dhcx_aux_sens_on_t aux = ISM330DHCX_SLV_0_1_2;
if (ism330dhcx_sh_slave_connected_set(data->ctx, aux) < 0) {
LOG_DBG("shub: error setting aux sensors");
return -EIO;
}
ism330dhcx_write_once_t wo = ISM330DHCX_ONLY_FIRST_CYCLE;
if (ism330dhcx_sh_write_mode_set(data->ctx, wo) < 0) {
LOG_DBG("shub: error setting write once");
return -EIO;
}
/* turn SH on */
ism330dhcx_shub_enable(data, 1);
ism330dhcx_shub_wait_completed(data);
return 0;
}
int ism330dhcx_shub_get_idx(enum sensor_channel type)
{
u8_t n;
struct ism330dhcx_shub_slist *sp;
for (n = 0; n < num_ext_dev; n++) {
sp = &ism330dhcx_shub_slist[shub_ext[n]];
if (sp->type == type)
return n;
}
return -ENOTSUP;
}
int ism330dhcx_shub_fetch_external_devs(struct device *dev)
{
u8_t n;
struct ism330dhcx_data *data = dev->driver_data;
struct ism330dhcx_shub_slist *sp;
/* read data from external slave */
ism330dhcx_shub_embedded_en(data, true);
for (n = 0; n < num_ext_dev; n++) {
sp = &ism330dhcx_shub_slist[shub_ext[n]];
if (ism330dhcx_read_reg(data->ctx, sp->sh_out_reg,
data->ext_data[n], sp->out_data_len) < 0) {
LOG_DBG("shub: failed to read sample");
ism330dhcx_shub_embedded_en(data, false);
return -EIO;
}
}
ism330dhcx_shub_embedded_en(data, false);
return 0;
}
int ism330dhcx_shub_config(struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
struct ism330dhcx_data *data = dev->driver_data;
struct ism330dhcx_shub_slist *sp = NULL;
u8_t n;
for (n = 0; n < num_ext_dev; n++) {
sp = &ism330dhcx_shub_slist[shub_ext[n]];
if (sp->type == chan)
break;
}
if (n == num_ext_dev) {
LOG_DBG("shub: chan not supported");
return -ENOTSUP;
}
if (sp == NULL || sp->dev_conf == NULL) {
LOG_DBG("shub: chan not configurable");
return -ENOTSUP;
}
return sp->dev_conf(data, sp->ext_i2c_addr, chan, attr, val);
}
int ism330dhcx_shub_init(struct device *dev)
{
struct ism330dhcx_data *data = dev->driver_data;
u8_t i, n = 0, regn;
u8_t chip_id;
struct ism330dhcx_shub_slist *sp;
for (n = 0; n < ARRAY_SIZE(ism330dhcx_shub_slist); n++) {
if (num_ext_dev >= ISM330DHCX_SHUB_MAX_NUM_SLVS)
break;
chip_id = 0;
sp = &ism330dhcx_shub_slist[n];
/*
* The external sensor may have different I2C address.
* So, try them one by one until we read the correct
* chip ID.
*/
for (i = 0U; i < ARRAY_SIZE(sp->i2c_addr); i++) {
if (ism330dhcx_shub_read_slave_reg(data,
sp->i2c_addr[i],
sp->wai_addr,
&chip_id, 1) < 0) {
continue;
}
if (chip_id == sp->wai_val) {
break;
}
}
if (i >= ARRAY_SIZE(sp->i2c_addr)) {
LOG_DBG("shub: invalid chip id 0x%x", chip_id);
continue;
}
LOG_INF("shub: Ext Device Chip Id: 0x%02x", chip_id);
sp->ext_i2c_addr = sp->i2c_addr[i];
shub_ext[num_ext_dev++] = n;
}
if (num_ext_dev == 0) {
LOG_ERR("shub: no slave devices found");
return -EINVAL;
}
/* init external devices */
for (n = 0, regn = 0; n < num_ext_dev; n++) {
sp = &ism330dhcx_shub_slist[shub_ext[n]];
sp->sh_out_reg = ISM330DHCX_SHUB_DATA_OUT + regn;
regn += sp->out_data_len;
sp->dev_init(data, sp->ext_i2c_addr);
}
ism330dhcx_shub_set_data_channel(data);
return 0;
}

View file

@ -0,0 +1,126 @@
/* ST Microelectronics ISM330DHCX 6-axis IMU sensor driver
*
* Copyright (c) 2020 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://www.st.com/resource/en/datasheet/ism330dhcx.pdf
*/
#include <string.h>
#include "ism330dhcx.h"
#include <logging/log.h>
#ifdef DT_ST_ISM330DHCX_BUS_SPI
#define ISM330DHCX_SPI_READ (1 << 7)
LOG_MODULE_DECLARE(ISM330DHCX, CONFIG_SENSOR_LOG_LEVEL);
static int ism330dhcx_spi_read(struct device *dev, u8_t reg_addr,
u8_t *value, u8_t len)
{
struct ism330dhcx_data *data = dev->driver_data;
const struct ism330dhcx_config *cfg = dev->config->config_info;
const struct spi_config *spi_cfg = &cfg->spi_conf;
u8_t buffer_tx[2] = { reg_addr | ISM330DHCX_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->bus, spi_cfg, &tx, &rx)) {
return -EIO;
}
return 0;
}
static int ism330dhcx_spi_write(struct device *dev, u8_t reg_addr,
u8_t *value, u8_t len)
{
struct ism330dhcx_data *data = dev->driver_data;
const struct ism330dhcx_config *cfg = dev->config->config_info;
const struct spi_config *spi_cfg = &cfg->spi_conf;
u8_t buffer_tx[1] = { reg_addr & ~ISM330DHCX_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->bus, spi_cfg, &tx)) {
return -EIO;
}
return 0;
}
int ism330dhcx_spi_init(struct device *dev)
{
struct ism330dhcx_data *data = dev->driver_data;
data->ctx_spi.read_reg = (stmdev_read_ptr) ism330dhcx_spi_read,
data->ctx_spi.write_reg = (stmdev_write_ptr) ism330dhcx_spi_write,
data->ctx = &data->ctx_spi;
data->ctx->handle = dev;
#if defined(DT_INST_0_ST_ISM330DHCX_CS_GPIOS_CONTROLLER)
const struct ism330dhcx_config *cfg = dev->config->config_info;
/* handle SPI CS thru GPIO if it is the case */
data->cs_ctrl.gpio_dev = device_get_binding(cfg->gpio_cs_port);
if (!data->cs_ctrl.gpio_dev) {
LOG_ERR("Unable to get GPIO SPI CS device");
return -ENODEV;
}
data->cs_ctrl.gpio_pin = cfg->cs_gpio;
data->cs_ctrl.delay = 0;
LOG_DBG("SPI GPIO CS configured on %s:%u",
cfg->gpio_cs_port, cfg->cs_gpio);
#endif
return 0;
}
#endif /* DT_ST_ISM330DHCX_BUS_SPI */

View file

@ -0,0 +1,300 @@
/* ST Microelectronics ISM330DHCX 6-axis IMU sensor driver
*
* Copyright (c) 2020 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://www.st.com/resource/en/datasheet/ism330dhcx.pdf
*/
#include <kernel.h>
#include <drivers/sensor.h>
#include <drivers/gpio.h>
#include <logging/log.h>
#include "ism330dhcx.h"
LOG_MODULE_DECLARE(ISM330DHCX, CONFIG_SENSOR_LOG_LEVEL);
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
/**
* ism330dhcx_enable_t_int - TEMP enable selected int pin to generate interrupt
*/
static int ism330dhcx_enable_t_int(struct device *dev, int enable)
{
const struct ism330dhcx_config *cfg = dev->config->config_info;
struct ism330dhcx_data *ism330dhcx = dev->driver_data;
ism330dhcx_pin_int2_route_t int2_route;
if (enable) {
union axis1bit16_t buf;
/* dummy read: re-trigger interrupt */
ism330dhcx_temperature_raw_get(ism330dhcx->ctx, buf.u8bit);
}
/* set interrupt (TEMP DRDY interrupt is only on INT2) */
if (cfg->int_pin == 1)
return -EIO;
ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
(uint8_t *)&int2_route.int2_ctrl, 1);
int2_route.int2_ctrl.int2_drdy_temp = enable;
return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
(uint8_t *)&int2_route.int2_ctrl, 1);
}
#endif
/**
* ism330dhcx_enable_xl_int - XL enable selected int pin to generate interrupt
*/
static int ism330dhcx_enable_xl_int(struct device *dev, int enable)
{
const struct ism330dhcx_config *cfg = dev->config->config_info;
struct ism330dhcx_data *ism330dhcx = dev->driver_data;
if (enable) {
union axis3bit16_t buf;
/* dummy read: re-trigger interrupt */
ism330dhcx_acceleration_raw_get(ism330dhcx->ctx, buf.u8bit);
}
/* set interrupt */
if (cfg->int_pin == 1) {
ism330dhcx_pin_int1_route_t int1_route;
ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT1_CTRL,
(uint8_t *)&int1_route.int1_ctrl, 1);
int1_route.int1_ctrl.int1_drdy_xl = enable;
return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT1_CTRL,
(uint8_t *)&int1_route.int1_ctrl, 1);
} else {
ism330dhcx_pin_int2_route_t int2_route;
ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
(uint8_t *)&int2_route.int2_ctrl, 1);
int2_route.int2_ctrl.int2_drdy_xl = enable;
return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
(uint8_t *)&int2_route.int2_ctrl, 1);
}
}
/**
* ism330dhcx_enable_g_int - Gyro enable selected int pin to generate interrupt
*/
static int ism330dhcx_enable_g_int(struct device *dev, int enable)
{
const struct ism330dhcx_config *cfg = dev->config->config_info;
struct ism330dhcx_data *ism330dhcx = dev->driver_data;
if (enable) {
union axis3bit16_t buf;
/* dummy read: re-trigger interrupt */
ism330dhcx_angular_rate_raw_get(ism330dhcx->ctx, buf.u8bit);
}
/* set interrupt */
if (cfg->int_pin == 1) {
ism330dhcx_pin_int1_route_t int1_route;
ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT1_CTRL,
(uint8_t *)&int1_route.int1_ctrl, 1);
int1_route.int1_ctrl.int1_drdy_g = enable;
return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT1_CTRL,
(uint8_t *)&int1_route.int1_ctrl, 1);
} else {
ism330dhcx_pin_int2_route_t int2_route;
ism330dhcx_read_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
(uint8_t *)&int2_route.int2_ctrl, 1);
int2_route.int2_ctrl.int2_drdy_g = enable;
return ism330dhcx_write_reg(ism330dhcx->ctx, ISM330DHCX_INT2_CTRL,
(uint8_t *)&int2_route.int2_ctrl, 1);
}
}
/**
* ism330dhcx_trigger_set - link external trigger to event data ready
*/
int ism330dhcx_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct ism330dhcx_data *ism330dhcx = dev->driver_data;
if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
ism330dhcx->handler_drdy_acc = handler;
if (handler) {
return ism330dhcx_enable_xl_int(dev, ISM330DHCX_EN_BIT);
} else {
return ism330dhcx_enable_xl_int(dev, ISM330DHCX_DIS_BIT);
}
} else if (trig->chan == SENSOR_CHAN_GYRO_XYZ) {
ism330dhcx->handler_drdy_gyr = handler;
if (handler) {
return ism330dhcx_enable_g_int(dev, ISM330DHCX_EN_BIT);
} else {
return ism330dhcx_enable_g_int(dev, ISM330DHCX_DIS_BIT);
}
}
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
else if (trig->chan == SENSOR_CHAN_DIE_TEMP) {
ism330dhcx->handler_drdy_temp = handler;
if (handler) {
return ism330dhcx_enable_t_int(dev, ISM330DHCX_EN_BIT);
} else {
return ism330dhcx_enable_t_int(dev, ISM330DHCX_DIS_BIT);
}
}
#endif
return -ENOTSUP;
}
/**
* ism330dhcx_handle_interrupt - handle the drdy event
* read data and call handler if registered any
*/
static void ism330dhcx_handle_interrupt(void *arg)
{
struct device *dev = arg;
struct ism330dhcx_data *ism330dhcx = dev->driver_data;
struct sensor_trigger drdy_trigger = {
.type = SENSOR_TRIG_DATA_READY,
};
const struct ism330dhcx_config *cfg = dev->config->config_info;
ism330dhcx_status_reg_t status;
while (1) {
if (ism330dhcx_status_reg_get(ism330dhcx->ctx, &status) < 0) {
LOG_DBG("failed reading status reg");
return;
}
if ((status.xlda == 0) && (status.gda == 0)
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
&& (status.tda == 0)
#endif
) {
break;
}
if ((status.xlda) && (ism330dhcx->handler_drdy_acc != NULL)) {
ism330dhcx->handler_drdy_acc(dev, &drdy_trigger);
}
if ((status.gda) && (ism330dhcx->handler_drdy_gyr != NULL)) {
ism330dhcx->handler_drdy_gyr(dev, &drdy_trigger);
}
#if defined(CONFIG_ISM330DHCX_ENABLE_TEMP)
if ((status.tda) && (ism330dhcx->handler_drdy_temp != NULL)) {
ism330dhcx->handler_drdy_temp(dev, &drdy_trigger);
}
#endif
}
gpio_pin_interrupt_configure(ism330dhcx->gpio, cfg->int_gpio_pin,
GPIO_INT_EDGE_TO_ACTIVE);
}
static void ism330dhcx_gpio_callback(struct device *dev,
struct gpio_callback *cb, u32_t pins)
{
struct ism330dhcx_data *ism330dhcx =
CONTAINER_OF(cb, struct ism330dhcx_data, gpio_cb);
const struct ism330dhcx_config *cfg = dev->config->config_info;
ARG_UNUSED(pins);
gpio_pin_interrupt_configure(ism330dhcx->gpio, cfg->int_gpio_pin,
GPIO_INT_DISABLE);
#if defined(CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD)
k_sem_give(&ism330dhcx->gpio_sem);
#elif defined(CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD)
k_work_submit(&ism330dhcx->work);
#endif /* CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD */
}
#ifdef CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD
static void ism330dhcx_thread(int dev_ptr, int unused)
{
struct device *dev = INT_TO_POINTER(dev_ptr);
struct ism330dhcx_data *ism330dhcx = dev->driver_data;
ARG_UNUSED(unused);
while (1) {
k_sem_take(&ism330dhcx->gpio_sem, K_FOREVER);
ism330dhcx_handle_interrupt(dev);
}
}
#endif /* CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD */
#ifdef CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD
static void ism330dhcx_work_cb(struct k_work *work)
{
struct ism330dhcx_data *ism330dhcx =
CONTAINER_OF(work, struct ism330dhcx_data, work);
ism330dhcx_handle_interrupt(ism330dhcx->dev);
}
#endif /* CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD */
int ism330dhcx_init_interrupt(struct device *dev)
{
struct ism330dhcx_data *ism330dhcx = dev->driver_data;
const struct ism330dhcx_config *cfg = dev->config->config_info;
int ret;
/* setup data ready gpio interrupt (INT1 or INT2) */
ism330dhcx->gpio = device_get_binding(cfg->int_gpio_port);
if (ism330dhcx->gpio == NULL) {
LOG_ERR("Cannot get pointer to %s device", cfg->int_gpio_port);
return -EINVAL;
}
#if defined(CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD)
k_sem_init(&ism330dhcx->gpio_sem, 0, UINT_MAX);
k_thread_create(&ism330dhcx->thread, ism330dhcx->thread_stack,
CONFIG_ISM330DHCX_THREAD_STACK_SIZE,
(k_thread_entry_t)ism330dhcx_thread, dev,
0, NULL, K_PRIO_COOP(CONFIG_ISM330DHCX_THREAD_PRIORITY),
0, K_NO_WAIT);
#elif defined(CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD)
ism330dhcx->work.handler = ism330dhcx_work_cb;
ism330dhcx->dev = dev;
#endif /* CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD */
ret = gpio_pin_configure(ism330dhcx->gpio, cfg->int_gpio_pin,
GPIO_INPUT | cfg->int_gpio_flags);
if (ret < 0) {
LOG_ERR("Could not configure gpio");
return ret;
}
gpio_init_callback(&ism330dhcx->gpio_cb,
ism330dhcx_gpio_callback,
BIT(cfg->int_gpio_pin));
if (gpio_add_callback(ism330dhcx->gpio, &ism330dhcx->gpio_cb) < 0) {
LOG_ERR("Could not set gpio callback");
return -EIO;
}
/* enable interrupt on int1/int2 in pulse mode */
if (ism330dhcx_int_notification_set(ism330dhcx->ctx,
ISM330DHCX_ALL_INT_PULSED) < 0) {
LOG_ERR("Could not set pulse mode");
return -EIO;
}
return gpio_pin_interrupt_configure(ism330dhcx->gpio, cfg->int_gpio_pin,
GPIO_INT_EDGE_TO_ACTIVE);
}

View file

@ -0,0 +1,20 @@
# Copyright (c) 2020 STMicroelectronics
# SPDX-License-Identifier: Apache-2.0
description: |
STMicroelectronics ISM330DHCX 6-axis IMU (Inertial Measurement Unit) sensor
accessed through I2C bus
compatible: "st,ism330dhcx"
include: i2c-device.yaml
properties:
drdy-gpios:
type: phandle-array
required: false
description: DRDY pin
This pin defaults to active high when produced by the sensor.
The property value should ensure the flags properly describe
the signal that is presented to the driver.

View file

@ -0,0 +1,20 @@
# Copyright (c) 2020 STMicroelectronics
# SPDX-License-Identifier: Apache-2.0
description: |
STMicroelectronics ISM330DHCX 6-axis IMU (Inertial Measurement Unit) sensor
accessed through SPI bus
compatible: "st,ism330dhcx"
include: spi-device.yaml
properties:
drdy-gpios:
type: phandle-array
required: false
description: DRDY pin
This pin defaults to active high when produced by the sensor.
The property value should ensure the flags properly describe
the signal that is presented to the driver.

View file

@ -110,6 +110,16 @@
#define DT_INST_0_PANASONIC_AMG88XX_INT_GPIOS_PIN 0
#endif
#ifndef DT_INST_0_ST_ISM330DHCX_LABEL
#define DT_INST_0_ST_ISM330DHCX_LABEL ""
#define DT_INST_0_ST_ISM330DHCX_BUS_NAME ""
#define DT_INST_0_ST_ISM330DHCX_BASE_ADDRESS 0
#define DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_FLAGS 0
#define DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_PIN 0
#define DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_CONTROLLER ""
#define DT_ST_ISM330DHCX_BUS_I2C 1
#endif
#ifndef DT_INST_0_ST_HTS221_LABEL
#define DT_INST_0_ST_HTS221_LABEL ""
#define DT_INST_0_ST_HTS221_BUS_NAME ""
@ -506,6 +516,17 @@
#define DT_ST_IIS2DLPC_BUS_SPI 1
#endif
#ifndef DT_INST_0_ST_ISM330DHCX_LABEL
#define DT_INST_0_ST_ISM330DHCX_LABEL ""
#define DT_INST_0_ST_ISM330DHCX_BUS_NAME ""
#define DT_INST_0_ST_ISM330DHCX_SPI_MAX_FREQUENCY 100000
#define DT_INST_0_ST_ISM330DHCX_BASE_ADDRESS 1
#define DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_CONTROLLER ""
#define DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_FLAGS 0
#define DT_INST_0_ST_ISM330DHCX_DRDY_GPIOS_PIN 0
#define DT_ST_ISM330DHCX_BUS_SPI 1
#endif
#ifndef DT_INST_0_ST_IIS3DHHC_LABEL
#define DT_INST_0_ST_IIS3DHHC_LABEL ""
#define DT_INST_0_ST_IIS3DHHC_BASE_ADDRESS 0

View file

@ -8,3 +8,4 @@ CONFIG_SENSOR=y
CONFIG_IIS2DLPC=y
CONFIG_LIS2DW12=y
CONFIG_STTS751=y
CONFIG_ISM330DHCX=y

View file

@ -11,3 +11,5 @@ CONFIG_LIS2DW12=y
CONFIG_LIS2DW12_TRIGGER_OWN_THREAD=y
CONFIG_STTS751=y
CONFIG_STTS751_TRIGGER_OWN_THREAD=y
CONFIG_ISM330DHCX=y
CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD=y