Skip to content

Commit 1a3dd63

Browse files
Emil-Juhlkartben
authored andcommitted
drivers: sensor: ti: opt3001: add trigger support
The ti,opt3001 ambient light sensor is capable of asserting its interrupt gpio upon conversion completion. The interrupt gpio can then be used to trigger a reading of the conversion result automatically. Due to this, it makes sense for the driver to implement the sensor trigger functionality. Add a devicetree property, int-gpios, to the ti,opt3001 bindings to allow describing the gpio used for getting interrupts from the ti,opt3001. Implement the sensor trigger functionality with options for using the global worker thread or letting the ti,opt3001 create its own thread for servicing the interrupts. In both cases, when an interrupt is received the driver will read a new measurement from the chip and clear the interrupt. The sample is then ready to be read by the user of the sensor. When manually triggering a sample_fetch, the trigger handler is not invoked. Signed-off-by: Emil Dahl Juhl <[email protected]>
1 parent c8bcf1b commit 1a3dd63

File tree

9 files changed

+300
-4
lines changed

9 files changed

+300
-4
lines changed

drivers/sensor/ti/opt3001/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
zephyr_library()
44

55
zephyr_library_sources(opt3001.c)
6+
zephyr_library_sources_ifdef(CONFIG_OPT3001_TRIGGER opt3001_trigger.c)

drivers/sensor/ti/opt3001/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,12 @@ config OPT3001
1010
select I2C
1111
help
1212
Enable driver for OPT3001 light sensors.
13+
14+
if OPT3001
15+
16+
module = OPT3001
17+
thread_priority = 10
18+
thread_stack_size = 1024
19+
source "drivers/sensor/Kconfig.trigger_template"
20+
21+
endif # OPT3001

drivers/sensor/ti/opt3001/opt3001.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 2019 Actinius
3+
* Copyright (c) 2025 Bang & Olufsen A/S
34
*
45
* SPDX-License-Identifier: Apache-2.0
56
*/
@@ -16,7 +17,7 @@
1617

1718
LOG_MODULE_REGISTER(opt3001, CONFIG_SENSOR_LOG_LEVEL);
1819

19-
static int opt3001_reg_read(const struct device *dev, uint8_t reg,
20+
int opt3001_reg_read(const struct device *dev, uint8_t reg,
2021
uint16_t *val)
2122
{
2223
const struct opt3001_config *config = dev->config;
@@ -45,7 +46,7 @@ static int opt3001_reg_write(const struct device *dev, uint8_t reg,
4546
return i2c_write_dt(&config->i2c, tx_buf, sizeof(tx_buf));
4647
}
4748

48-
static int opt3001_reg_update(const struct device *dev, uint8_t reg,
49+
int opt3001_reg_update(const struct device *dev, uint8_t reg,
4950
uint16_t mask, uint16_t val)
5051
{
5152
uint16_t old_val;
@@ -85,6 +86,7 @@ static int opt3001_channel_get(const struct device *dev,
8586
struct sensor_value *val)
8687
{
8788
struct opt3001_data *drv_data = dev->data;
89+
uint16_t sample = drv_data->sample;
8890
int32_t uval;
8991

9092
if (chan != SENSOR_CHAN_LIGHT) {
@@ -99,15 +101,17 @@ static int opt3001_channel_get(const struct device *dev,
99101
* lux is the integer obtained using the following formula:
100102
* (2^(exponent value)) * 0.01 * mantissa value
101103
*/
102-
uval = (1 << (drv_data->sample >> OPT3001_SAMPLE_EXPONENT_SHIFT))
103-
* (drv_data->sample & OPT3001_MANTISSA_MASK);
104+
uval = (1 << (sample >> OPT3001_SAMPLE_EXPONENT_SHIFT)) * (sample & OPT3001_MANTISSA_MASK);
104105
val->val1 = uval / 100;
105106
val->val2 = (uval % 100) * 10000;
106107

107108
return 0;
108109
}
109110

110111
static DEVICE_API(sensor, opt3001_driver_api) = {
112+
#ifdef CONFIG_OPT3001_TRIGGER
113+
.trigger_set = opt3001_trigger_set,
114+
#endif
111115
.sample_fetch = opt3001_sample_fetch,
112116
.channel_get = opt3001_channel_get,
113117
};
@@ -156,6 +160,13 @@ int opt3001_init(const struct device *dev)
156160
return -EINVAL;
157161
}
158162

163+
#ifdef CONFIG_OPT3001_TRIGGER
164+
if (opt3001_init_interrupt(dev)) {
165+
LOG_ERR("Failed to initialize interrupt");
166+
return -EIO;
167+
}
168+
#endif
169+
159170
return 0;
160171
}
161172

@@ -164,6 +175,8 @@ int opt3001_init(const struct device *dev)
164175
\
165176
static const struct opt3001_config opt3001_config_##inst = { \
166177
.i2c = I2C_DT_SPEC_INST_GET(inst), \
178+
IF_ENABLED(CONFIG_OPT3001_TRIGGER, \
179+
(.gpio_int = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}),)) \
167180
}; \
168181
\
169182
SENSOR_DEVICE_DT_INST_DEFINE(inst, opt3001_init, NULL, \

drivers/sensor/ti/opt3001/opt3001.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 2019 Actinius
3+
* Copyright (c) 2025 Bang & Olufsen A/S
34
*
45
* SPDX-License-Identifier: Apache-2.0
56
*/
@@ -9,9 +10,13 @@
910

1011
#include <zephyr/sys/util.h>
1112
#include <zephyr/drivers/i2c.h>
13+
#include <zephyr/drivers/gpio.h>
14+
#include <zephyr/drivers/sensor.h>
15+
#include <zephyr/kernel.h>
1216

1317
#define OPT3001_REG_RESULT 0x00
1418
#define OPT3001_REG_CONFIG 0x01
19+
#define OPT3001_REG_LOW_LIMIT 0x02
1520
#define OPT3001_REG_MANUFACTURER_ID 0x7E
1621
#define OPT3001_REG_DEVICE_ID 0x7F
1722

@@ -21,15 +26,51 @@
2126
#define OPT3001_CONVERSION_MODE_MASK (BIT(10) | BIT(9))
2227
#define OPT3001_CONVERSION_MODE_CONTINUOUS (BIT(10) | BIT(9))
2328

29+
#define OPT3001_LIMIT_EXPONENT_MASK (BIT(15) | BIT(14) | BIT(13) | BIT(12))
30+
#define OPT3001_LIMIT_EXPONENT_DEFAULT 0x0000
31+
2432
#define OPT3001_SAMPLE_EXPONENT_SHIFT 12
2533
#define OPT3001_MANTISSA_MASK 0xfff
2634

35+
#ifdef CONFIG_OPT3001_TRIGGER
36+
int opt3001_init_interrupt(const struct device *dev);
37+
38+
int opt3001_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
39+
sensor_trigger_handler_t handler);
40+
41+
int opt3001_reg_update(const struct device *dev, uint8_t reg, uint16_t mask, uint16_t val);
42+
43+
int opt3001_reg_read(const struct device *dev, uint8_t reg, uint16_t *val);
44+
45+
#endif
46+
2747
struct opt3001_data {
2848
uint16_t sample;
49+
50+
#ifdef CONFIG_OPT3001_TRIGGER
51+
const struct device *dev;
52+
struct gpio_callback gpio_cb_int;
53+
54+
const struct sensor_trigger *trigger;
55+
sensor_trigger_handler_t handler;
56+
struct k_mutex handler_mutex;
57+
58+
#if defined(CONFIG_OPT3001_TRIGGER_OWN_THREAD)
59+
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_OPT3001_THREAD_STACK_SIZE);
60+
struct k_sem gpio_sem;
61+
struct k_thread thread;
62+
#elif defined(CONFIG_OPT3001_TRIGGER_GLOBAL_THREAD)
63+
struct k_work work_int;
64+
#endif
65+
66+
#endif
2967
};
3068

3169
struct opt3001_config {
3270
struct i2c_dt_spec i2c;
71+
#ifdef CONFIG_OPT3001_TRIGGER
72+
const struct gpio_dt_spec gpio_int;
73+
#endif
3374
};
3475

3576
#endif /* _SENSOR_OPT3001_ */
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/*
2+
* Copyright (c) 2025 Bang & Olufsen A/S
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT ti_opt3001
8+
9+
#include "opt3001.h"
10+
11+
#include <zephyr/device.h>
12+
#include <zephyr/drivers/gpio.h>
13+
#include <zephyr/drivers/i2c.h>
14+
#include <zephyr/drivers/sensor.h>
15+
#include <zephyr/kernel.h>
16+
#include <zephyr/logging/log.h>
17+
#include <zephyr/sys/util.h>
18+
19+
LOG_MODULE_DECLARE(opt3001, CONFIG_SENSOR_LOG_LEVEL);
20+
21+
static void opt3001_thread_cb(const struct device *dev)
22+
{
23+
const struct opt3001_config *cfg = dev->config;
24+
struct opt3001_data *dat = dev->data;
25+
uint16_t reg_cfg;
26+
int ret;
27+
28+
/* read result then clear interrupt by reading REG_CONFIG */
29+
ret = opt3001_reg_read(dat->dev, OPT3001_REG_RESULT, &dat->sample);
30+
if (ret) {
31+
LOG_ERR("Failed to read result, ret: %d", ret);
32+
return;
33+
}
34+
35+
ret = opt3001_reg_read(dat->dev, OPT3001_REG_CONFIG, &reg_cfg);
36+
if (ret) {
37+
LOG_ERR("Failed to read config, ret: %d", ret);
38+
return;
39+
}
40+
41+
ret = k_mutex_lock(&dat->handler_mutex, K_FOREVER);
42+
if (ret) {
43+
LOG_ERR("Failed to lock trigger mutex, ret: %d", ret);
44+
return;
45+
}
46+
47+
if (dat->handler) {
48+
dat->handler(dat->dev, dat->trigger);
49+
}
50+
51+
/* recheck handler in case it was removed during the handler invocation */
52+
if (dat->handler) {
53+
/* enable interrupt again */
54+
gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
55+
}
56+
57+
ret = k_mutex_unlock(&dat->handler_mutex);
58+
if (ret) {
59+
LOG_ERR("Failed to unlock trigger mutex, ret: %d", ret);
60+
}
61+
}
62+
63+
static void opt3001_isr(const struct device *dev, struct gpio_callback *gpio_cb, uint32_t pins)
64+
{
65+
struct opt3001_data *dat = CONTAINER_OF(gpio_cb, struct opt3001_data, gpio_cb_int);
66+
const struct opt3001_config *cfg = dat->dev->config;
67+
68+
ARG_UNUSED(pins);
69+
70+
/* disable interrupt from gpio during handling */
71+
gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE);
72+
73+
#if defined(CONFIG_OPT3001_TRIGGER_OWN_THREAD)
74+
k_sem_give(&dat->gpio_sem);
75+
#elif defined(CONFIG_OPT3001_TRIGGER_GLOBAL_THREAD)
76+
k_work_submit(&dat->work_int);
77+
#endif
78+
}
79+
80+
#ifdef CONFIG_OPT3001_TRIGGER_OWN_THREAD
81+
static void opt3001_thread(void *p1, void *p2, void *p3)
82+
{
83+
ARG_UNUSED(p2);
84+
ARG_UNUSED(p3);
85+
86+
struct opt3001_data *dat = p1;
87+
88+
while (1) {
89+
k_sem_take(&dat->gpio_sem, K_FOREVER);
90+
opt3001_thread_cb(dat->dev);
91+
}
92+
}
93+
#endif
94+
95+
#ifdef CONFIG_OPT3001_TRIGGER_GLOBAL_THREAD
96+
static void opt3001_int_work(struct k_work *work)
97+
{
98+
struct opt3001_data *dat = CONTAINER_OF(work, struct opt3001_data, work_int);
99+
100+
opt3001_thread_cb(dat->dev);
101+
}
102+
#endif
103+
104+
int opt3001_init_interrupt(const struct device *dev)
105+
{
106+
const struct opt3001_config *cfg = dev->config;
107+
struct opt3001_data *dat = dev->data;
108+
int ret = 0;
109+
110+
/* setup interrupt if provided */
111+
if (!cfg->gpio_int.port) {
112+
LOG_DBG("int-gpios not provided, continuing without support for trigger");
113+
return 0;
114+
}
115+
116+
dat->dev = dev;
117+
118+
if (!gpio_is_ready_dt(&cfg->gpio_int)) {
119+
LOG_ERR("Interrupt gpio not ready");
120+
return -ENODEV;
121+
}
122+
123+
ret = k_mutex_init(&dat->handler_mutex);
124+
if (ret) {
125+
LOG_ERR("Failed to init trigger mutex, ret: %d", ret);
126+
return ret;
127+
}
128+
129+
ret = gpio_pin_configure_dt(&cfg->gpio_int, GPIO_INPUT);
130+
if (ret) {
131+
LOG_ERR("Failed to configure int-gpios, ret: %d", ret);
132+
return ret;
133+
}
134+
135+
ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE);
136+
if (ret) {
137+
LOG_ERR("Failed to enable interrupt on int-gpios, ret: %d", ret);
138+
return ret;
139+
}
140+
141+
gpio_init_callback(&dat->gpio_cb_int, opt3001_isr, BIT(cfg->gpio_int.pin));
142+
143+
ret = gpio_add_callback(cfg->gpio_int.port, &dat->gpio_cb_int);
144+
if (ret) {
145+
LOG_ERR("Failed to add callback to int-gpios, ret: %d", ret);
146+
return ret;
147+
}
148+
149+
#if defined(CONFIG_OPT3001_TRIGGER_OWN_THREAD)
150+
k_sem_init(&dat->gpio_sem, 0, K_SEM_MAX_LIMIT);
151+
k_thread_create(&dat->thread, dat->thread_stack, CONFIG_OPT3001_THREAD_STACK_SIZE,
152+
opt3001_thread, dat, NULL, NULL,
153+
K_PRIO_COOP(CONFIG_OPT3001_THREAD_PRIORITY), 0, K_NO_WAIT);
154+
k_thread_name_set(&dat->thread, "opt3001_trigger");
155+
156+
#elif defined(CONFIG_OPT3001_TRIGGER_GLOBAL_THREAD)
157+
k_work_init(&dat->work_int, opt3001_int_work);
158+
#endif
159+
160+
return 0;
161+
}
162+
163+
int opt3001_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
164+
sensor_trigger_handler_t handler)
165+
{
166+
const struct opt3001_config *cfg = dev->config;
167+
struct opt3001_data *dat = dev->data;
168+
uint16_t reg_cfg;
169+
int ret;
170+
171+
/* only allow trigger on a device with int-gpios specified */
172+
if (!cfg->gpio_int.port) {
173+
return -ENOSYS;
174+
}
175+
176+
if (trig->chan != SENSOR_CHAN_LIGHT) {
177+
return -EINVAL;
178+
}
179+
180+
if (trig->type != SENSOR_TRIG_DATA_READY) {
181+
return -EINVAL;
182+
}
183+
184+
ret = k_mutex_lock(&dat->handler_mutex, K_FOREVER);
185+
if (ret) {
186+
LOG_ERR("Failed to lock trigger mutex, ret: %d", ret);
187+
return ret;
188+
}
189+
190+
dat->trigger = trig;
191+
dat->handler = handler;
192+
193+
ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_int,
194+
handler ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE);
195+
if (ret) {
196+
LOG_ERR("Failed to configure gpio interrupt, ret: %d", ret);
197+
return ret;
198+
}
199+
200+
/* enable/disable interrupt on every conversion */
201+
ret = opt3001_reg_update(dev, OPT3001_REG_LOW_LIMIT, OPT3001_LIMIT_EXPONENT_MASK,
202+
handler ? OPT3001_LIMIT_EXPONENT_MASK
203+
: OPT3001_LIMIT_EXPONENT_DEFAULT);
204+
if (ret) {
205+
LOG_ERR("Failed to enable interrupt on conversions, ret: %d", ret);
206+
return ret;
207+
}
208+
209+
/* clear any asserted interrupt by reading REG_CONFIG */
210+
ret = opt3001_reg_read(dat->dev, OPT3001_REG_CONFIG, &reg_cfg);
211+
if (ret) {
212+
LOG_ERR("Failed to read config, ret: %d", ret);
213+
return ret;
214+
}
215+
216+
ret = k_mutex_unlock(&dat->handler_mutex);
217+
if (ret) {
218+
LOG_ERR("Failed to unlock trigger mutex, ret: %d", ret);
219+
return ret;
220+
}
221+
222+
return 0;
223+
}

0 commit comments

Comments
 (0)