Skip to content

Commit 5b05024

Browse files
committed
sensor: rm3100: Add streaming mode
Compatible trigger: DRDY. Tested with Sensor Shell commands. Signed-off-by: Luis Ubieda <[email protected]>
1 parent 3a2152f commit 5b05024

File tree

8 files changed

+336
-2
lines changed

8 files changed

+336
-2
lines changed

drivers/sensor/pni/rm3100/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ zephyr_library_sources(
77
rm3100.c
88
rm3100_decoder.c
99
)
10+
zephyr_library_sources_ifdef(CONFIG_RM3100_STREAM
11+
rm3100_stream.c
12+
)

drivers/sensor/pni/rm3100/Kconfig

+7
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,10 @@ config RM3100
1111
select SENSOR_ASYNC_API
1212
help
1313
Enable driver for PNI RM3100 high-accuracy 3-axis magnetometer.
14+
15+
config RM3100_STREAM
16+
bool "RM3100 Streaming Mode"
17+
depends on $(dt_compat_any_has_prop,$(DT_COMPAT_PNI_RM3100),int-gpios)
18+
help
19+
Enable streaming mode for the RM3100 sensor.
20+
This mode allows for continuous data output at a specified rate.

drivers/sensor/pni/rm3100/rm3100.c

+14-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "rm3100_reg.h"
2121
#include "rm3100_bus.h"
2222
#include "rm3100_decoder.h"
23+
#include "rm3100_stream.h"
2324

2425
#include <zephyr/logging/log.h>
2526
LOG_MODULE_REGISTER(RM3100, CONFIG_SENSOR_LOG_LEVEL);
@@ -118,6 +119,8 @@ static void rm3100_submit(const struct device *dev, struct rtio_iodev_sqe *iodev
118119

119120
if (!cfg->is_streaming) {
120121
rm3100_submit_one_shot(dev, iodev_sqe);
122+
} else if (IS_ENABLED(CONFIG_RM3100_STREAM)) {
123+
rm3100_stream_submit(dev, iodev_sqe);
121124
} else {
122125
LOG_ERR("Streaming not supported");
123126
rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
@@ -149,6 +152,14 @@ static int rm3100_init(const struct device *dev)
149152
}
150153
LOG_DBG("RM3100 chip ID confirmed: 0x%02x", val);
151154

155+
if (IS_ENABLED(CONFIG_RM3100_STREAM)) {
156+
err = rm3100_stream_init(dev);
157+
if (err < 0) {
158+
LOG_ERR("Failed to set up stream config: %d", err);
159+
return err;
160+
}
161+
}
162+
152163
uint16_t cycle_count[] = {
153164
sys_be16_to_cpu(RM3100_CYCLE_COUNT_DEFAULT),
154165
sys_be16_to_cpu(RM3100_CYCLE_COUNT_DEFAULT),
@@ -196,7 +207,9 @@ static int rm3100_init(const struct device *dev)
196207
RTIO_DEFINE(rm3100_rtio_ctx_##inst, 8, 8); \
197208
I2C_DT_IODEV_DEFINE(rm3100_bus_##inst, DT_DRV_INST(inst)); \
198209
\
199-
static const struct rm3100_config rm3100_cfg_##inst; \
210+
static const struct rm3100_config rm3100_cfg_##inst = { \
211+
.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
212+
}; \
200213
\
201214
static struct rm3100_data rm3100_data_##inst = { \
202215
.rtio = { \

drivers/sensor/pni/rm3100/rm3100.h

+23-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#define ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_
1010

1111
#include <zephyr/drivers/sensor.h>
12+
#include <zephyr/drivers/gpio.h>
1213
#include <zephyr/rtio/rtio.h>
1314
#include "rm3100_reg.h"
1415

@@ -21,6 +22,10 @@ struct rm3100_encoded_data {
2122
uint64_t timestamp;
2223
uint8_t channels : 3;
2324
uint16_t cycle_count;
25+
uint8_t status;
26+
struct {
27+
bool drdy : 1;
28+
} events;
2429
} header;
2530
union {
2631
uint8_t payload[RM3100_TOTAL_BYTES];
@@ -33,7 +38,21 @@ struct rm3100_encoded_data {
3338
};
3439

3540
struct rm3100_config {
36-
uint32_t unused; /* Will be expanded with stremaing-mode to hold int-gpios */
41+
struct gpio_dt_spec int_gpio;
42+
};
43+
44+
struct rm3100_stream {
45+
struct gpio_callback cb;
46+
const struct device *dev;
47+
struct rtio_iodev_sqe *iodev_sqe;
48+
struct {
49+
struct {
50+
bool drdy : 1;
51+
} enabled;
52+
struct {
53+
enum sensor_stream_data_opt drdy;
54+
} opt;
55+
} settings;
3756
};
3857

3958
struct rm3100_data {
@@ -45,6 +64,9 @@ struct rm3100_data {
4564
struct {
4665
uint8_t odr;
4766
} settings;
67+
#if defined(CONFIG_RM3100_STREAM)
68+
struct rm3100_stream stream;
69+
#endif /* CONFIG_RM3100_STREAM */
4870
};
4971

5072
#endif /* ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_ */

drivers/sensor/pni/rm3100/rm3100_reg.h

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#ifndef ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_REG_H_
99
#define ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_REG_H_
1010

11+
/* Address value has a read bit */
12+
#define REG_READ_BIT BIT(7)
13+
1114
/* RM3100 register addresses */
1215
#define RM3100_REG_CMM 0x01 /* Continuous measurement mode */
1316
#define RM3100_REG_CCX_MSB 0x04 /* Cycle Count X LSB */
+261
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
/*
2+
* Copyright (c) 2025 Croxel, Inc.
3+
* Copyright (c) 2025 CogniPilot Foundation
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <zephyr/drivers/sensor.h>
9+
#include <zephyr/drivers/sensor_clock.h>
10+
#include <zephyr/rtio/rtio.h>
11+
#include <zephyr/sys/check.h>
12+
#include "rm3100_stream.h"
13+
#include "rm3100_decoder.h"
14+
#include "rm3100.h"
15+
16+
#include <zephyr/logging/log.h>
17+
LOG_MODULE_REGISTER(RM3100_STREAM, CONFIG_SENSOR_LOG_LEVEL);
18+
19+
static void rm3100_complete_result(struct rtio *ctx, const struct rtio_sqe *sqe, void *arg)
20+
{
21+
const struct device *dev = (const struct device *)arg;
22+
struct rm3100_data *data = dev->data;
23+
struct rtio_iodev_sqe *iodev_sqe = data->stream.iodev_sqe;
24+
struct rtio_cqe *cqe;
25+
int err = 0;
26+
struct rm3100_encoded_data *edata = sqe->userdata;
27+
28+
edata->header.events.drdy = ((edata->header.status != 0) &&
29+
data->stream.settings.enabled.drdy);
30+
edata->header.channels = 0;
31+
32+
if (!edata->header.events.drdy) {
33+
LOG_ERR("Status register does not have DRDY bit set: 0x%02x",
34+
edata->header.status);
35+
} else if (data->stream.settings.opt.drdy == SENSOR_STREAM_DATA_INCLUDE) {
36+
edata->header.channels |= rm3100_encode_channel(SENSOR_CHAN_MAGN_XYZ);
37+
}
38+
39+
do {
40+
cqe = rtio_cqe_consume(ctx);
41+
if (cqe != NULL) {
42+
err = cqe->result;
43+
rtio_cqe_release(ctx, cqe);
44+
}
45+
} while (cqe != NULL);
46+
47+
if (err) {
48+
rtio_iodev_sqe_err(iodev_sqe, err);
49+
} else {
50+
rtio_iodev_sqe_ok(iodev_sqe, 0);
51+
}
52+
53+
LOG_DBG("Streaming read-out complete");
54+
}
55+
56+
static void rm3100_stream_get_data(const struct device *dev)
57+
{
58+
struct rm3100_data *data = dev->data;
59+
uint64_t cycles;
60+
int err;
61+
62+
CHECKIF(!data->stream.iodev_sqe) {
63+
LOG_WRN("No RTIO submission with an INT GPIO event");
64+
return;
65+
}
66+
67+
struct rtio_iodev_sqe *iodev_sqe = data->stream.iodev_sqe;
68+
uint8_t *buf;
69+
uint32_t buf_len;
70+
uint32_t min_buf_len = sizeof(struct rm3100_encoded_data);
71+
struct rm3100_encoded_data *edata;
72+
73+
err = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
74+
if (err) {
75+
LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
76+
77+
data->stream.iodev_sqe = NULL;
78+
rtio_iodev_sqe_err(iodev_sqe, err);
79+
return;
80+
}
81+
82+
edata = (struct rm3100_encoded_data *)buf;
83+
84+
err = sensor_clock_get_cycles(&cycles);
85+
CHECKIF(err) {
86+
LOG_ERR("Failed to get timestamp: %d", err);
87+
88+
data->stream.iodev_sqe = NULL;
89+
rtio_iodev_sqe_err(iodev_sqe, err);
90+
return;
91+
}
92+
edata->header.timestamp = sensor_clock_cycles_to_ns(cycles);
93+
94+
struct rtio_sqe *status_wr_sqe = rtio_sqe_acquire(data->rtio.ctx);
95+
struct rtio_sqe *status_rd_sqe = rtio_sqe_acquire(data->rtio.ctx);
96+
struct rtio_sqe *write_sqe = rtio_sqe_acquire(data->rtio.ctx);
97+
struct rtio_sqe *read_sqe = rtio_sqe_acquire(data->rtio.ctx);
98+
struct rtio_sqe *complete_sqe = rtio_sqe_acquire(data->rtio.ctx);
99+
100+
if (!write_sqe || !read_sqe || !complete_sqe) {
101+
LOG_ERR("Failed to acquire RTIO SQEs");
102+
rtio_sqe_drop_all(data->rtio.ctx);
103+
104+
data->stream.iodev_sqe = NULL;
105+
rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
106+
return;
107+
}
108+
109+
uint8_t val;
110+
111+
val = RM3100_REG_STATUS;
112+
113+
rtio_sqe_prep_tiny_write(status_wr_sqe,
114+
data->rtio.iodev,
115+
RTIO_PRIO_HIGH,
116+
&val,
117+
1,
118+
NULL);
119+
status_wr_sqe->flags |= RTIO_SQE_TRANSACTION;
120+
121+
rtio_sqe_prep_read(status_rd_sqe,
122+
data->rtio.iodev,
123+
RTIO_PRIO_HIGH,
124+
&edata->header.status,
125+
sizeof(edata->header.status),
126+
NULL);
127+
status_rd_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART;
128+
status_rd_sqe->flags |= RTIO_SQE_CHAINED;
129+
130+
val = RM3100_REG_MX;
131+
132+
rtio_sqe_prep_tiny_write(write_sqe,
133+
data->rtio.iodev,
134+
RTIO_PRIO_HIGH,
135+
&val,
136+
1,
137+
NULL);
138+
write_sqe->flags |= RTIO_SQE_TRANSACTION;
139+
140+
rtio_sqe_prep_read(read_sqe,
141+
data->rtio.iodev,
142+
RTIO_PRIO_HIGH,
143+
edata->payload,
144+
sizeof(edata->payload),
145+
NULL);
146+
read_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART;
147+
read_sqe->flags |= RTIO_SQE_CHAINED;
148+
149+
rtio_sqe_prep_callback_no_cqe(complete_sqe,
150+
rm3100_complete_result,
151+
(void *)dev,
152+
buf);
153+
154+
rtio_submit(data->rtio.ctx, 0);
155+
}
156+
157+
static void rm3100_gpio_callback(const struct device *gpio_dev,
158+
struct gpio_callback *cb,
159+
uint32_t pins)
160+
{
161+
struct rm3100_stream *stream = CONTAINER_OF(cb,
162+
struct rm3100_stream,
163+
cb);
164+
const struct device *dev = stream->dev;
165+
const struct rm3100_config *cfg = dev->config;
166+
int err;
167+
168+
/* Disable interrupts */
169+
err = gpio_pin_interrupt_configure_dt(&cfg->int_gpio,
170+
GPIO_INT_MODE_DISABLED);
171+
if (err) {
172+
LOG_ERR("Failed to disable interrupt");
173+
return;
174+
}
175+
176+
rm3100_stream_get_data(dev);
177+
}
178+
179+
static inline bool settings_changed(const struct rm3100_stream *a,
180+
const struct rm3100_stream *b)
181+
{
182+
return (a->settings.enabled.drdy != b->settings.enabled.drdy) ||
183+
(a->settings.opt.drdy != b->settings.opt.drdy);
184+
}
185+
186+
void rm3100_stream_submit(const struct device *dev,
187+
struct rtio_iodev_sqe *iodev_sqe)
188+
{
189+
const struct sensor_read_config *read_config = iodev_sqe->sqe.iodev->data;
190+
struct rm3100_data *data = dev->data;
191+
const struct rm3100_config *cfg = dev->config;
192+
int err;
193+
194+
if ((read_config->count != 1) ||
195+
(read_config->triggers[0].trigger != SENSOR_TRIG_DATA_READY)) {
196+
LOG_ERR("Only SENSOR_TRIG_DATA_READY is supported");
197+
rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
198+
return;
199+
}
200+
201+
/* Store context for next submission (handled within callbacks) */
202+
data->stream.iodev_sqe = iodev_sqe;
203+
204+
data->stream.settings.enabled.drdy = true;
205+
data->stream.settings.opt.drdy = read_config->triggers[0].opt;
206+
207+
err = gpio_pin_interrupt_configure_dt(&cfg->int_gpio,
208+
GPIO_INT_LEVEL_ACTIVE);
209+
if (err) {
210+
LOG_ERR("Failed to enable interrupts");
211+
212+
data->stream.iodev_sqe = NULL;
213+
rtio_iodev_sqe_err(iodev_sqe, err);
214+
return;
215+
}
216+
}
217+
218+
int rm3100_stream_init(const struct device *dev)
219+
{
220+
const struct rm3100_config *cfg = dev->config;
221+
struct rm3100_data *data = dev->data;
222+
int err;
223+
224+
/** Needed to get back the device handle from the callback context */
225+
data->stream.dev = dev;
226+
227+
if (!cfg->int_gpio.port) {
228+
LOG_ERR("Interrupt GPIO not supplied");
229+
return -ENODEV;
230+
}
231+
232+
if (!gpio_is_ready_dt(&cfg->int_gpio)) {
233+
LOG_ERR("Interrupt GPIO not ready");
234+
return -ENODEV;
235+
}
236+
237+
err = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
238+
if (err) {
239+
LOG_ERR("Failed to configure interrupt GPIO");
240+
return -EIO;
241+
}
242+
243+
gpio_init_callback(&data->stream.cb,
244+
rm3100_gpio_callback,
245+
BIT(cfg->int_gpio.pin));
246+
247+
err = gpio_add_callback(cfg->int_gpio.port, &data->stream.cb);
248+
if (err) {
249+
LOG_ERR("Failed to add interrupt callback");
250+
return -EIO;
251+
}
252+
253+
err = gpio_pin_interrupt_configure_dt(&cfg->int_gpio,
254+
GPIO_INT_MODE_DISABLED);
255+
if (err) {
256+
LOG_ERR("Failed to configure interrupt as disabled");
257+
return -EIO;
258+
}
259+
260+
return 0;
261+
}

0 commit comments

Comments
 (0)