drivers: sensor: lsm6dso: add support for tilt and tap triggers

Tilt or Tap can be selected via config and further tap parameters can be
configured via DT.

Signed-off-by: Ivan Wagner <ivan.wagner@tecinvent.ch>
This commit is contained in:
Ivan Wagner 2025-04-24 13:26:59 +02:00 committed by David Leach
commit b9cd08b2ea
7 changed files with 411 additions and 34 deletions

View file

@ -60,6 +60,16 @@ config LSM6DSO_THREAD_STACK_SIZE
help
Stack size of thread used by the driver to handle interrupts.
config LSM6DSO_TILT
bool "Tilt detection"
help
Enable tilt detection
config LSM6DSO_TAP
bool "Tap and Tap-Tap detection"
help
Enable tap (single/double) detection
endif # LSM6DSO_TRIGGER
config LSM6DSO_ENABLE_TEMP

View file

@ -869,6 +869,17 @@ static int lsm6dso_init(const struct device *dev)
* Instantiation macros used when a device is on a SPI bus.
*/
#ifdef CONFIG_LSM6DSO_TAP
#define LSM6DSO_CONFIG_TAP(inst) \
.tap_mode = DT_INST_PROP(inst, tap_mode), \
.tap_threshold = DT_INST_PROP(inst, tap_threshold), \
.tap_shock = DT_INST_PROP(inst, tap_shock), \
.tap_latency = DT_INST_PROP(inst, tap_latency), \
.tap_quiet = DT_INST_PROP(inst, tap_quiet),
#else
#define LSM6DSO_CONFIG_TAP(inst)
#endif /* CONFIG_LSM6DSO_TAP */
#ifdef CONFIG_LSM6DSO_TRIGGER
#define LSM6DSO_CFG_IRQ(inst) \
.trig_enabled = true, \
@ -892,7 +903,8 @@ static int lsm6dso_init(const struct device *dev)
.gyro_pm = DT_INST_PROP(inst, gyro_pm), \
.gyro_odr = DT_INST_PROP(inst, gyro_odr), \
.gyro_range = DT_INST_PROP(inst, gyro_range), \
.drdy_pulsed = DT_INST_PROP(inst, drdy_pulsed), \
.drdy_pulsed = DT_INST_PROP(inst, drdy_pulsed), \
LSM6DSO_CONFIG_TAP(inst) \
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, irq_gpios), \
(LSM6DSO_CFG_IRQ(inst)), ())

View file

@ -63,6 +63,13 @@ struct lsm6dso_config {
const struct gpio_dt_spec gpio_drdy;
uint8_t int_pin;
bool trig_enabled;
#ifdef CONFIG_LSM6DSO_TAP
uint8_t tap_mode;
uint8_t tap_threshold[3];
uint8_t tap_shock;
uint8_t tap_latency;
uint8_t tap_quiet;
#endif /* CONFIG_LSM6DSO_TAP */
#endif /* CONFIG_LSM6DSO_TRIGGER */
};
@ -106,6 +113,18 @@ struct lsm6dso_data {
sensor_trigger_handler_t handler_drdy_temp;
const struct sensor_trigger *trig_drdy_temp;
#ifdef CONFIG_LSM6DSO_TILT
sensor_trigger_handler_t handler_tilt;
const struct sensor_trigger *trig_tilt;
#endif /* CONFIG_LSM6DSO_TILT */
#ifdef CONFIG_LSM6DSO_TAP
sensor_trigger_handler_t handler_tap;
const struct sensor_trigger *trig_tap;
sensor_trigger_handler_t handler_double_tap;
const struct sensor_trigger *trig_double_tap;
#endif /* CONFIG_LSM6DSO_TAP */
#if defined(CONFIG_LSM6DSO_TRIGGER_OWN_THREAD)
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_LSM6DSO_THREAD_STACK_SIZE);
struct k_thread thread;

View file

@ -48,6 +48,193 @@ static int lsm6dso_enable_t_int(const struct device *dev, int enable)
}
#endif
#if defined(CONFIG_LSM6DSO_TILT)
static int lsm6dso_enable_tilt_int(const struct device *dev, int enable)
{
const struct lsm6dso_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
int ret = 0;
lsm6dso_emb_sens_t sens;
sens.tilt = PROPERTY_ENABLE;
ret += lsm6dso_embedded_sens_set(ctx, &sens);
if (ret < 0) {
LOG_ERR("Failed to enable tilt");
return -EIO;
}
if (cfg->int_pin == 1) {
lsm6dso_pin_int1_route_t route;
lsm6dso_pin_int1_route_get(ctx, &route);
route.tilt = enable;
ret += lsm6dso_pin_int1_route_set(ctx, route);
if (ret < 0) {
LOG_ERR("Failed to set int1 route");
return -EIO;
}
} else {
lsm6dso_pin_int2_route_t route;
lsm6dso_pin_int2_route_get(ctx, NULL, &route);
route.tilt = enable;
ret += lsm6dso_pin_int2_route_set(ctx, NULL, route);
if (ret < 0) {
LOG_ERR("Failed to set int2 route");
return -EIO;
}
}
return 0;
}
#endif
#if defined(CONFIG_LSM6DSO_TAP)
static int lsm6dso_enable_tap(const struct device *dev, int enable)
{
const struct lsm6dso_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
lsm6dso_odr_xl_t odr;
if (lsm6dso_xl_data_rate_get(ctx, &odr) < 0) {
LOG_ERR("Unable to read accelerometer ODR");
return -EIO;
}
if (odr < LSM6DSO_XL_ODR_417Hz) {
LOG_WRN("Minimum recommended accelerometer ODR is 417Hz for tap mode");
}
LOG_INF("TAP: tap mode is %d", cfg->tap_mode);
if (lsm6dso_tap_mode_set(ctx, cfg->tap_mode) < 0) {
LOG_ERR("Failed to select tap trigger mode");
return -EIO;
}
LOG_INF("TAP: ths_x is %02x", cfg->tap_threshold[0]);
if (lsm6dso_tap_threshold_x_set(ctx, cfg->tap_threshold[0]) < 0) {
LOG_ERR("Failed to set tap X axis threshold");
return -EIO;
}
LOG_DBG("TAP: ths_y is %02x", cfg->tap_threshold[1]);
if (lsm6dso_tap_threshold_y_set(ctx, cfg->tap_threshold[1]) < 0) {
LOG_ERR("Failed to set tap Y axis threshold");
return -EIO;
}
LOG_DBG("TAP: ths_z is %02x", cfg->tap_threshold[2]);
if (lsm6dso_tap_threshold_z_set(ctx, cfg->tap_threshold[2]) < 0) {
LOG_ERR("Failed to set tap Z axis threshold");
return -EIO;
}
if (cfg->tap_threshold[0] > 0) {
LOG_DBG("TAP: tap_x enabled");
if (lsm6dso_tap_detection_on_x_set(ctx, enable) < 0) {
LOG_ERR("Failed to set tap detection on X axis");
return -EIO;
}
}
if (cfg->tap_threshold[1] > 0) {
LOG_DBG("TAP: tap_y enabled");
if (lsm6dso_tap_detection_on_y_set(ctx, enable) < 0) {
LOG_ERR("Failed to set tap detection on Y axis");
return -EIO;
}
}
if (cfg->tap_threshold[2] > 0) {
LOG_DBG("TAP: tap_z enabled");
if (lsm6dso_tap_detection_on_z_set(ctx, enable) < 0) {
LOG_ERR("Failed to set tap detection on Z axis");
return -EIO;
}
}
LOG_DBG("TAP: shock is %02x", cfg->tap_shock);
if (lsm6dso_tap_shock_set(ctx, cfg->tap_shock) < 0) {
LOG_ERR("Failed to set tap shock duration");
return -EIO;
}
LOG_DBG("TAP: latency is %02x", cfg->tap_latency);
if (lsm6dso_tap_dur_set(ctx, cfg->tap_latency) < 0) {
LOG_ERR("Failed to set tap latency");
return -EIO;
}
/* Set tap quiet */
LOG_DBG("TAP: quiet time is %02x", cfg->tap_quiet);
if (lsm6dso_tap_quiet_set(ctx, cfg->tap_quiet) < 0) {
LOG_ERR("Failed to set tap quiet time");
return -EIO;
}
return 0;
}
static int lsm6dso_enable_single_tap_int(const struct device *dev, int enable)
{
const struct lsm6dso_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
if (cfg->int_pin == 1) {
lsm6dso_pin_int1_route_t route;
lsm6dso_pin_int1_route_get(ctx, &route);
route.single_tap = enable;
return lsm6dso_pin_int1_route_set(ctx, route);
} else {
lsm6dso_pin_int2_route_t route;
lsm6dso_pin_int2_route_get(ctx, NULL, &route);
route.single_tap = enable;
return lsm6dso_pin_int2_route_set(ctx, NULL, route);
}
return 0;
}
static int lsm6dso_enable_double_tap_int(const struct device *dev, int enable)
{
const struct lsm6dso_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
if (cfg->int_pin == 1) {
lsm6dso_pin_int1_route_t route;
lsm6dso_pin_int1_route_get(ctx, &route);
route.double_tap = enable;
return lsm6dso_pin_int1_route_set(ctx, route);
} else {
lsm6dso_pin_int2_route_t route;
lsm6dso_pin_int2_route_get(ctx, NULL, &route);
route.double_tap = enable;
return lsm6dso_pin_int2_route_set(ctx, NULL, route);
}
}
#endif /* CONFIG_LSM6DSO_TAP */
/**
* lsm6dso_enable_xl_int - XL enable selected int pin to generate interrupt
*/
@ -134,36 +321,86 @@ int lsm6dso_trigger_set(const struct device *dev,
return -ENOTSUP;
}
if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
lsm6dso->handler_drdy_acc = handler;
lsm6dso->trig_drdy_acc = trig;
if (handler) {
return lsm6dso_enable_xl_int(dev, LSM6DSO_EN_BIT);
} else {
return lsm6dso_enable_xl_int(dev, LSM6DSO_DIS_BIT);
if (trig->type == SENSOR_TRIG_DATA_READY) {
if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
lsm6dso->handler_drdy_acc = handler;
lsm6dso->trig_drdy_acc = trig;
if (handler) {
return lsm6dso_enable_xl_int(dev, LSM6DSO_EN_BIT);
} else {
return lsm6dso_enable_xl_int(dev, LSM6DSO_DIS_BIT);
}
} else if (trig->chan == SENSOR_CHAN_GYRO_XYZ) {
lsm6dso->handler_drdy_gyr = handler;
lsm6dso->trig_drdy_gyr = trig;
if (handler) {
return lsm6dso_enable_g_int(dev, LSM6DSO_EN_BIT);
} else {
return lsm6dso_enable_g_int(dev, LSM6DSO_DIS_BIT);
}
}
} else if (trig->chan == SENSOR_CHAN_GYRO_XYZ) {
lsm6dso->handler_drdy_gyr = handler;
lsm6dso->trig_drdy_gyr = trig;
if (handler) {
return lsm6dso_enable_g_int(dev, LSM6DSO_EN_BIT);
} else {
return lsm6dso_enable_g_int(dev, LSM6DSO_DIS_BIT);
}
}
#if defined(CONFIG_LSM6DSO_ENABLE_TEMP)
else if (trig->chan == SENSOR_CHAN_DIE_TEMP) {
lsm6dso->handler_drdy_temp = handler;
lsm6dso->trig_drdy_temp = trig;
if (handler) {
return lsm6dso_enable_t_int(dev, LSM6DSO_EN_BIT);
} else {
return lsm6dso_enable_t_int(dev, LSM6DSO_DIS_BIT);
else if (trig->chan == SENSOR_CHAN_DIE_TEMP) {
lsm6dso->handler_drdy_temp = handler;
lsm6dso->trig_drdy_temp = trig;
if (handler) {
return lsm6dso_enable_t_int(dev, LSM6DSO_EN_BIT);
} else {
return lsm6dso_enable_t_int(dev, LSM6DSO_DIS_BIT);
}
}
#endif
else {
return -ENOTSUP;
}
}
#if defined(CONFIG_LSM6DSO_TILT)
else if (trig->type == SENSOR_TRIG_TILT) {
lsm6dso->handler_tilt = handler;
lsm6dso->trig_tilt = trig;
if (handler) {
return lsm6dso_enable_tilt_int(dev, LSM6DSO_EN_BIT);
} else {
return lsm6dso_enable_tilt_int(dev, LSM6DSO_DIS_BIT);
}
}
#endif
#if defined(CONFIG_LSM6DSO_TAP)
else if (trig->type == SENSOR_TRIG_TAP || trig->type == SENSOR_TRIG_DOUBLE_TAP) {
lsm6dso_enable_tap(dev, handler ? LSM6DSO_EN_BIT : LSM6DSO_DIS_BIT);
/* Set interrupt */
if (trig->type == SENSOR_TRIG_TAP) {
lsm6dso->handler_tap = handler;
lsm6dso->trig_tap = trig;
if (handler) {
return lsm6dso_enable_single_tap_int(dev, LSM6DSO_EN_BIT);
} else {
return lsm6dso_enable_single_tap_int(dev, LSM6DSO_DIS_BIT);
}
} else if (trig->type == SENSOR_TRIG_DOUBLE_TAP) {
lsm6dso->handler_double_tap = handler;
lsm6dso->trig_double_tap = trig;
if (handler) {
return lsm6dso_enable_double_tap_int(dev, LSM6DSO_EN_BIT);
} else {
return lsm6dso_enable_double_tap_int(dev, LSM6DSO_DIS_BIT);
}
}
}
#endif /* CONFIG_LSM6DSO_TAP */
return -ENOTSUP;
}
/**
@ -175,37 +412,63 @@ static void lsm6dso_handle_interrupt(const struct device *dev)
struct lsm6dso_data *lsm6dso = dev->data;
const struct lsm6dso_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
lsm6dso_status_reg_t status;
lsm6dso_all_sources_t sources;
bool pending_status = false;
while (1) {
if (lsm6dso_status_reg_get(ctx, &status) < 0) {
LOG_DBG("failed reading status reg");
if (lsm6dso_all_sources_get(ctx, &sources) < 0) {
LOG_ERR("failed reading all sources");
return;
}
if ((status.xlda == 0) && (status.gda == 0)
if ((sources.drdy_xl && lsm6dso->handler_drdy_acc != NULL) ||
(sources.drdy_g && lsm6dso->handler_drdy_gyr != NULL)
#if defined(CONFIG_LSM6DSO_ENABLE_TEMP)
&& (status.tda == 0)
|| (sources.drdy_temp && lsm6dso->handler_drdy_temp != NULL)
#endif
) {
break;
) {
pending_status = true;
} else {
pending_status = false;
}
if ((status.xlda) && (lsm6dso->handler_drdy_acc != NULL)) {
if ((sources.drdy_xl) && (lsm6dso->handler_drdy_acc != NULL)) {
lsm6dso->handler_drdy_acc(dev, lsm6dso->trig_drdy_acc);
}
if ((status.gda) && (lsm6dso->handler_drdy_gyr != NULL)) {
if ((sources.drdy_g) && (lsm6dso->handler_drdy_gyr != NULL)) {
lsm6dso->handler_drdy_gyr(dev, lsm6dso->trig_drdy_gyr);
}
#if defined(CONFIG_LSM6DSO_ENABLE_TEMP)
if ((status.tda) && (lsm6dso->handler_drdy_temp != NULL)) {
if ((sources.drdy_temp) && (lsm6dso->handler_drdy_temp != NULL)) {
lsm6dso->handler_drdy_temp(dev, lsm6dso->trig_drdy_temp);
}
#endif
if (!pending_status) {
break;
}
}
#if defined(CONFIG_LSM6DSO_TILT)
if (sources.tilt && (lsm6dso->handler_tilt != NULL)) {
lsm6dso->handler_tilt(dev, lsm6dso->trig_tilt);
}
#endif /* CONFIG_LSM6DSO_TILT */
#if defined(CONFIG_LSM6DSO_TAP)
if (sources.single_tap && lsm6dso->handler_tap != NULL) {
lsm6dso->handler_tap(dev, lsm6dso->trig_tap);
}
if (sources.double_tap && lsm6dso->handler_double_tap != NULL) {
lsm6dso->handler_double_tap(dev, lsm6dso->trig_double_tap);
}
#endif /* CONFIG_LSM6DSO_TAP */
gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy,
GPIO_INT_EDGE_TO_ACTIVE);
}
@ -285,6 +548,7 @@ int lsm6dso_init_interrupt(const struct device *dev)
return ret;
}
gpio_init_callback(&lsm6dso->gpio_cb,
lsm6dso_gpio_callback,
BIT(cfg->gpio_drdy.pin));

View file

@ -146,3 +146,67 @@ properties:
description: |
Selects the pulsed mode for data-ready interrupt when enabled,
and the latched mode when disabled.
# tap and tap-tap configuration section
# All default values are selected to match the power-up values.
# tap and tap-tap events can be generated on INT1 only.
tap-mode:
type: int
default: 0
description: |
Tap mode. Default is power-up configuration.
- 0 # LSM6DSO_DT_SINGLE_TAP
- 1 # LSM6DSO_DT_SINGLE_DOUBLE_TAP
enum: [0, 1]
tap-threshold:
type: array
default: [0, 0, 0]
description: |
Tap X/Y/Z axes threshold. Default is power-up configuration.
(X/Y/Z values range from 0x00 to 0x1F)
Thresholds to start the tap-event detection procedure on the X/Y/Z axes.
Threshold values for each axis are unsigned 5-bit corresponding
to a 2g acceleration full-scale range. A threshold value equal to zero
corresponds to disable the tap detection on that axis.
For example, if you want to set the threshold for X to 12, for Z to 14
and want to disable tap detection on Y, you should specify in Device Tree
tap-threshold = <12>, <0>, <14>
which is equivalent to X = 12 * 2g/32 = 750mg and Z = 14 * 2g/32 = 875mg.
tap-shock:
type: int
default: 0x0
description: |
Tap shock value. Default is power-up configuration.
(values range from 0x0 to 0x3)
This register represents the maximum time of an over-threshold signal
detection to be recognized as a tap event. Where 0 equals 4*1/ODR and
1LSB = 8*1/ODR.
tap-latency:
type: int
default: 0x0
description: |
Tap latency. Default is power-up configuration.
(values range from 0x0 to 0xF)
When double-tap recognition is enabled, this register expresses the
maximum time between two successive detected taps to determine a
double-tap event. Where 0 equals 16*1/ODR and 1LSB = 32*1/ODR.
tap-quiet:
type: int
default: 0x0
description: |
Expected quiet time after a tap detection. Default is power-up configuration.
(values range from 0x0 to 0x3)
This register represents the time after the first detected tap in which
there must not be any overthreshold event. Where 0 equals 2*1/ODR
and 1LSB = 4*1/ODR.

View file

@ -272,6 +272,10 @@ enum sensor_trigger_type {
/** Trigger fires when the FIFO becomes full. */
SENSOR_TRIG_FIFO_FULL,
/** Trigger fires when a tilt is detected. */
SENSOR_TRIG_TILT,
/**
* Number of all common sensor triggers.
*/

View file

@ -42,4 +42,8 @@
#define LSM6DSO_DT_ODR_6667Hz 0xa
#define LSM6DSO_DT_ODR_1Hz6 0xb
/* Tap mode */
#define LSM6DSO_DT_SINGLE_TAP 0
#define LSM6DSO_DT_SINGLE_DOUBLE_TAP 1
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_ST_LSM6DSO_H_ */