Skip to content

Commit 4aef791

Browse files
ubiedakartben
authored andcommitted
sensor: icm45686: Add Triggers functionality
Only working with SENSOR_TRIG_DATA_READY so far. Signed-off-by: Luis Ubieda <luisf@croxel.com>
1 parent 05e8a65 commit 4aef791

File tree

7 files changed

+337
-1
lines changed

7 files changed

+337
-1
lines changed

drivers/sensor/tdk/icm45686/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ zephyr_library_sources(
1010
zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API
1111
icm45686_decoder.c
1212
)
13+
zephyr_library_sources_ifdef(CONFIG_ICM45686_TRIGGER
14+
icm45686_trigger.c
15+
)

drivers/sensor/tdk/icm45686/Kconfig

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Copyright (c) 2025 CogniPilot Foundation
33
# SPDX-License-Identifier: Apache-2.0
44

5-
config ICM45686
5+
menuconfig ICM45686
66
bool "ICM45686 High-precision 6-Axis Motion Tracking Device"
77
default y
88
depends on DT_HAS_INVENSENSE_ICM45686_ENABLED
@@ -11,3 +11,47 @@ config ICM45686
1111
help
1212
Enable driver for ICM45686 High-precision 6-axis motion
1313
tracking device.
14+
15+
if ICM45686
16+
17+
choice
18+
prompt "Trigger mode"
19+
default ICM45686_TRIGGER_GLOBAL_THREAD
20+
help
21+
Specify the type of triggering to be used by the driver
22+
23+
config ICM45686_TRIGGER_NONE
24+
bool "No trigger"
25+
26+
config ICM45686_TRIGGER_GLOBAL_THREAD
27+
bool "Use global thread"
28+
depends on GPIO
29+
depends on $(dt_compat_any_has_prop,$(DT_COMPAT_INVENSENSE_ICM45686),int-gpios)
30+
select ICM45686_TRIGGER
31+
32+
config ICM45686_TRIGGER_OWN_THREAD
33+
bool "Use own thread"
34+
depends on GPIO
35+
depends on $(dt_compat_any_has_prop,$(DT_COMPAT_INVENSENSE_ICM45686),int-gpios)
36+
select ICM45686_TRIGGER
37+
38+
endchoice
39+
40+
config ICM45686_TRIGGER
41+
bool
42+
43+
config ICM45686_THREAD_PRIORITY
44+
int "Own thread priority"
45+
depends on ICM45686_TRIGGER_OWN_THREAD
46+
default 10
47+
help
48+
The priority of the thread used for handling triggers.
49+
50+
config ICM45686_THREAD_STACK_SIZE
51+
int "Own thread stack size"
52+
depends on ICM45686_TRIGGER_OWN_THREAD
53+
default 1024
54+
help
55+
The thread stack size.
56+
57+
endif # ICM45686

drivers/sensor/tdk/icm45686/icm45686.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "icm45686_reg.h"
2222
#include "icm45686_bus.h"
2323
#include "icm45686_decoder.h"
24+
#include "icm45686_trigger.h"
2425

2526
#include <zephyr/logging/log.h>
2627
LOG_MODULE_REGISTER(ICM45686, CONFIG_SENSOR_LOG_LEVEL);
@@ -224,6 +225,9 @@ static void icm45686_submit(const struct device *dev, struct rtio_iodev_sqe *iod
224225
static DEVICE_API(sensor, icm45686_driver_api) = {
225226
.sample_fetch = icm45686_sample_fetch,
226227
.channel_get = icm45686_channel_get,
228+
#if defined(CONFIG_ICM45686_TRIGGER)
229+
.trigger_set = icm45686_trigger_set,
230+
#endif /* CONFIG_ICM45686_TRIGGER */
227231
#if defined(CONFIG_SENSOR_ASYNC_API)
228232
.get_decoder = icm45686_get_decoder,
229233
.submit = icm45686_submit,
@@ -344,6 +348,14 @@ static int icm45686_init(const struct device *dev)
344348
return err;
345349
}
346350

351+
if (IS_ENABLED(CONFIG_ICM45686_TRIGGER)) {
352+
err = icm45686_trigger_init(dev);
353+
if (err) {
354+
LOG_ERR("Failed to initialize triggers: %d", err);
355+
return err;
356+
}
357+
}
358+
347359
LOG_DBG("Init OK");
348360

349361
return 0;
@@ -382,6 +394,7 @@ static int icm45686_init(const struct device *dev)
382394
.lpf = DT_INST_PROP_OR(inst, gyro_lpf, 0), \
383395
}, \
384396
}, \
397+
.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
385398
}; \
386399
static struct icm45686_data icm45686_data_##inst = { \
387400
.edata.header = { \

drivers/sensor/tdk/icm45686/icm45686.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,33 @@ struct icm45686_encoded_data {
4848
struct icm45686_encoded_payload payload;
4949
};
5050

51+
struct icm45686_triggers {
52+
struct gpio_callback cb;
53+
const struct device *dev;
54+
struct k_mutex lock;
55+
struct {
56+
struct sensor_trigger trigger;
57+
sensor_trigger_handler_t handler;
58+
} entry;
59+
#if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD)
60+
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_ICM45686_THREAD_STACK_SIZE);
61+
struct k_thread thread;
62+
struct k_sem sem;
63+
#elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD)
64+
struct k_work work;
65+
#endif
66+
};
67+
5168
struct icm45686_data {
5269
struct {
5370
struct rtio_iodev *iodev;
5471
struct rtio *ctx;
5572
} rtio;
5673
/** Single-shot encoded data instance to support fetch/get API */
5774
struct icm45686_encoded_data edata;
75+
#if defined(CONFIG_ICM45686_TRIGGER)
76+
struct icm45686_triggers triggers;
77+
#endif /* CONFIG_ICM45686_TRIGGER */
5878
};
5979

6080
struct icm45686_config {
@@ -72,6 +92,7 @@ struct icm45686_config {
7292
uint8_t lpf : 3;
7393
} gyro;
7494
} settings;
95+
struct gpio_dt_spec int_gpio;
7596
};
7697

7798
static inline void icm45686_accel_ms(struct icm45686_encoded_data *edata,

drivers/sensor/tdk/icm45686/icm45686_reg.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
#define REG_TEMP_DATA1_UI 0x0C
3333
#define REG_TEMP_DATA0_UI 0x0D
3434
#define REG_PWR_MGMT0 0x10
35+
#define REG_INT1_CONFIG0 0x16
36+
#define REG_INT1_CONFIG1 0x17
37+
#define REG_INT1_CONFIG2 0x18
38+
#define REG_INT1_STATUS0 0x19
39+
#define REG_INT1_STATUS1 0x1A
3540
#define REG_ACCEL_CONFIG0 0x1B
3641
#define REG_GYRO_CONFIG0 0x1C
3742
#define REG_DRIVE_CONFIG0 0x32
@@ -67,6 +72,18 @@
6772

6873
#define REG_IPREG_SYS2_REG_131_ACCEL_LPFBW_SEL(val) (val & BIT_MASK(3))
6974

75+
#define REG_INT1_CONFIG0_STATUS_EN_DRDY(val) (((val) & BIT_MASK(1)) << 2)
76+
#define REG_INT1_CONFIG0_STATUS_EN_FIFO_THS(val) (((val) & BIT_MASK(1)) << 1)
77+
#define REG_INT1_CONFIG0_STATUS_EN_FIFO_FULL(val) ((val) & BIT_MASK(1))
78+
79+
#define REG_INT1_CONFIG2_EN_OPEN_DRAIN(val) (((val) & BIT_MASK(1)) << 2)
80+
#define REG_INT1_CONFIG2_EN_LATCH_MODE(val) (((val) & BIT_MASK(1)) << 1)
81+
#define REG_INT1_CONFIG2_EN_ACTIVE_HIGH(val) ((val) & BIT_MASK(1))
82+
83+
#define REG_INT1_STATUS0_DRDY(val) (((val) & BIT_MASK(1)) << 2)
84+
#define REG_INT1_STATUS0_FIFO_THS(val) (((val) & BIT_MASK(1)) << 1)
85+
#define REG_INT1_STATUS0_FIFO_FULL(val) ((val) & BIT_MASK(1))
86+
7087
/* Misc. Defines */
7188
#define WHO_AM_I_ICM45686 0xE9
7289

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/*
2+
* Copyright (c) 2022 Intel Corporation
3+
* Copyright (c) 2023 Google LLC
4+
* Copyright (c) 2025 Croxel Inc.
5+
* Copyright (c) 2025 CogniPilot Foundation
6+
*
7+
* SPDX-License-Identifier: Apache-2.0
8+
*/
9+
10+
#include <zephyr/drivers/sensor.h>
11+
#include <zephyr/drivers/gpio.h>
12+
13+
#include "icm45686.h"
14+
#include "icm45686_bus.h"
15+
#include "icm45686_trigger.h"
16+
17+
#include <zephyr/logging/log.h>
18+
LOG_MODULE_REGISTER(ICM45686_TRIGGER, CONFIG_SENSOR_LOG_LEVEL);
19+
20+
static void icm45686_gpio_callback(const struct device *dev,
21+
struct gpio_callback *cb,
22+
uint32_t pins)
23+
{
24+
struct icm45686_data *data = CONTAINER_OF(cb,
25+
struct icm45686_data,
26+
triggers.cb);
27+
28+
ARG_UNUSED(dev);
29+
ARG_UNUSED(pins);
30+
31+
#if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD)
32+
k_sem_give(&data->triggers.sem);
33+
#elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD)
34+
k_work_submit(&data->triggers.work);
35+
#endif
36+
}
37+
38+
static void icm45686_thread_cb(const struct device *dev)
39+
{
40+
struct icm45686_data *data = dev->data;
41+
42+
(void)k_mutex_lock(&data->triggers.lock, K_FOREVER);
43+
44+
if (data->triggers.entry.handler) {
45+
data->triggers.entry.handler(dev, &data->triggers.entry.trigger);
46+
}
47+
48+
(void)k_mutex_unlock(&data->triggers.lock);
49+
}
50+
51+
#if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD)
52+
53+
static void icm45686_thread(void *p1, void *p2, void *p3)
54+
{
55+
ARG_UNUSED(p2);
56+
ARG_UNUSED(p3);
57+
58+
struct icm45686_data *data = p1;
59+
60+
while (true) {
61+
k_sem_take(&data->triggers.sem, K_FOREVER);
62+
63+
icm45686_thread_cb(data->triggers.dev);
64+
}
65+
}
66+
67+
#elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD)
68+
69+
static void icm45686_work_handler(struct k_work *work)
70+
{
71+
struct icm45686_data *data = CONTAINER_OF(work,
72+
struct icm45686_data,
73+
triggers.work);
74+
75+
icm45686_thread_cb(data->triggers.dev);
76+
}
77+
78+
#endif
79+
80+
static int icm45686_enable_drdy(const struct device *dev, bool enable)
81+
{
82+
uint8_t val;
83+
int err;
84+
85+
err = icm45686_bus_read(dev, REG_INT1_CONFIG0, &val, 1);
86+
if (err) {
87+
return err;
88+
}
89+
90+
val &= ~REG_INT1_CONFIG0_STATUS_EN_DRDY(true);
91+
err = icm45686_bus_write(dev, REG_INT1_CONFIG0, &val, 1);
92+
if (err) {
93+
return err;
94+
}
95+
96+
if (enable) {
97+
val |= REG_INT1_CONFIG0_STATUS_EN_DRDY(true);
98+
}
99+
100+
return icm45686_bus_write(dev, REG_INT1_CONFIG0, &val, 1);
101+
}
102+
103+
int icm45686_trigger_set(const struct device *dev,
104+
const struct sensor_trigger *trig,
105+
sensor_trigger_handler_t handler)
106+
{
107+
int err = 0;
108+
struct icm45686_data *data = dev->data;
109+
110+
(void)k_mutex_lock(&data->triggers.lock, K_FOREVER);
111+
112+
switch (trig->type) {
113+
case SENSOR_TRIG_DATA_READY:
114+
data->triggers.entry.trigger = *trig;
115+
data->triggers.entry.handler = handler;
116+
117+
if (handler) {
118+
/* Enable data ready interrupt */
119+
err = icm45686_enable_drdy(dev, true);
120+
} else {
121+
/* Disable data ready interrupt */
122+
err = icm45686_enable_drdy(dev, false);
123+
}
124+
break;
125+
default:
126+
err = -ENOTSUP;
127+
break;
128+
}
129+
130+
(void)k_mutex_unlock(&data->triggers.lock);
131+
132+
return err;
133+
}
134+
135+
int icm45686_trigger_init(const struct device *dev)
136+
{
137+
const struct icm45686_config *cfg = dev->config;
138+
struct icm45686_data *data = dev->data;
139+
uint8_t val = 0;
140+
int err;
141+
142+
err = k_mutex_init(&data->triggers.lock);
143+
__ASSERT_NO_MSG(!err);
144+
145+
/** Needed to get back the device handle from the callback context */
146+
data->triggers.dev = dev;
147+
148+
#if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD)
149+
150+
err = k_sem_init(&data->triggers.sem, 0, 1);
151+
__ASSERT_NO_MSG(!err);
152+
153+
(void)k_thread_create(&data->triggers.thread,
154+
data->triggers.thread_stack,
155+
K_KERNEL_STACK_SIZEOF(data->triggers.thread_stack),
156+
icm45686_thread,
157+
data,
158+
NULL,
159+
NULL,
160+
K_PRIO_COOP(CONFIG_ICM45686_THREAD_PRIORITY),
161+
0,
162+
K_NO_WAIT);
163+
164+
#elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD)
165+
k_work_init(&data->triggers.work, icm45686_work_handler);
166+
#endif
167+
168+
if (!cfg->int_gpio.port) {
169+
LOG_ERR("Interrupt GPIO not supplied");
170+
return -ENODEV;
171+
}
172+
173+
if (!gpio_is_ready_dt(&cfg->int_gpio)) {
174+
LOG_ERR("Interrupt GPIO not ready");
175+
return -ENODEV;
176+
}
177+
178+
err = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
179+
if (err) {
180+
LOG_ERR("Failed to configure interrupt GPIO");
181+
return -EIO;
182+
}
183+
184+
gpio_init_callback(&data->triggers.cb,
185+
icm45686_gpio_callback,
186+
BIT(cfg->int_gpio.pin));
187+
188+
err = gpio_add_callback(cfg->int_gpio.port, &data->triggers.cb);
189+
if (err) {
190+
LOG_ERR("Failed to add interrupt callback");
191+
return -EIO;
192+
}
193+
194+
err = gpio_pin_interrupt_configure_dt(&cfg->int_gpio,
195+
GPIO_INT_EDGE_TO_ACTIVE);
196+
if (err) {
197+
LOG_ERR("Failed to configure interrupt");
198+
}
199+
200+
err = icm45686_bus_write(dev, REG_INT1_CONFIG0, &val, 1);
201+
if (err) {
202+
LOG_ERR("Failed to disable all INTs");
203+
}
204+
205+
val = REG_INT1_CONFIG2_EN_OPEN_DRAIN(false) |
206+
REG_INT1_CONFIG2_EN_ACTIVE_HIGH(true);
207+
208+
err = icm45686_bus_write(dev, REG_INT1_CONFIG2, &val, 1);
209+
if (err) {
210+
LOG_ERR("Failed to configure INT as push-pull: %d", err);
211+
}
212+
213+
return err;
214+
}

0 commit comments

Comments
 (0)