sensor: rm3100: Add streaming mode
Compatible trigger: DRDY. Tested with Sensor Shell commands. Signed-off-by: Luis Ubieda <luisf@croxel.com>
This commit is contained in:
parent
4dfe251986
commit
a13be2f320
8 changed files with 336 additions and 2 deletions
|
@ -7,3 +7,6 @@ zephyr_library_sources(
|
|||
rm3100.c
|
||||
rm3100_decoder.c
|
||||
)
|
||||
zephyr_library_sources_ifdef(CONFIG_RM3100_STREAM
|
||||
rm3100_stream.c
|
||||
)
|
||||
|
|
|
@ -11,3 +11,10 @@ config RM3100
|
|||
select SENSOR_ASYNC_API
|
||||
help
|
||||
Enable driver for PNI RM3100 high-accuracy 3-axis magnetometer.
|
||||
|
||||
config RM3100_STREAM
|
||||
bool "RM3100 Streaming Mode"
|
||||
depends on $(dt_compat_any_has_prop,$(DT_COMPAT_PNI_RM3100),int-gpios)
|
||||
help
|
||||
Enable streaming mode for the RM3100 sensor.
|
||||
This mode allows for continuous data output at a specified rate.
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "rm3100_reg.h"
|
||||
#include "rm3100_bus.h"
|
||||
#include "rm3100_decoder.h"
|
||||
#include "rm3100_stream.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
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
|
|||
|
||||
if (!cfg->is_streaming) {
|
||||
rm3100_submit_one_shot(dev, iodev_sqe);
|
||||
} else if (IS_ENABLED(CONFIG_RM3100_STREAM)) {
|
||||
rm3100_stream_submit(dev, iodev_sqe);
|
||||
} else {
|
||||
LOG_ERR("Streaming not supported");
|
||||
rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
|
||||
|
@ -149,6 +152,14 @@ static int rm3100_init(const struct device *dev)
|
|||
}
|
||||
LOG_DBG("RM3100 chip ID confirmed: 0x%02x", val);
|
||||
|
||||
if (IS_ENABLED(CONFIG_RM3100_STREAM)) {
|
||||
err = rm3100_stream_init(dev);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Failed to set up stream config: %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t cycle_count[] = {
|
||||
sys_be16_to_cpu(RM3100_CYCLE_COUNT_DEFAULT),
|
||||
sys_be16_to_cpu(RM3100_CYCLE_COUNT_DEFAULT),
|
||||
|
@ -196,7 +207,9 @@ static int rm3100_init(const struct device *dev)
|
|||
RTIO_DEFINE(rm3100_rtio_ctx_##inst, 8, 8); \
|
||||
I2C_DT_IODEV_DEFINE(rm3100_bus_##inst, DT_DRV_INST(inst)); \
|
||||
\
|
||||
static const struct rm3100_config rm3100_cfg_##inst; \
|
||||
static const struct rm3100_config rm3100_cfg_##inst = { \
|
||||
.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
|
||||
}; \
|
||||
\
|
||||
static struct rm3100_data rm3100_data_##inst = { \
|
||||
.rtio = { \
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#define ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/rtio/rtio.h>
|
||||
#include "rm3100_reg.h"
|
||||
|
||||
|
@ -21,6 +22,10 @@ struct rm3100_encoded_data {
|
|||
uint64_t timestamp;
|
||||
uint8_t channels : 3;
|
||||
uint16_t cycle_count;
|
||||
uint8_t status;
|
||||
struct {
|
||||
bool drdy : 1;
|
||||
} events;
|
||||
} header;
|
||||
union {
|
||||
uint8_t payload[RM3100_TOTAL_BYTES];
|
||||
|
@ -33,7 +38,21 @@ struct rm3100_encoded_data {
|
|||
};
|
||||
|
||||
struct rm3100_config {
|
||||
uint32_t unused; /* Will be expanded with stremaing-mode to hold int-gpios */
|
||||
struct gpio_dt_spec int_gpio;
|
||||
};
|
||||
|
||||
struct rm3100_stream {
|
||||
struct gpio_callback cb;
|
||||
const struct device *dev;
|
||||
struct rtio_iodev_sqe *iodev_sqe;
|
||||
struct {
|
||||
struct {
|
||||
bool drdy : 1;
|
||||
} enabled;
|
||||
struct {
|
||||
enum sensor_stream_data_opt drdy;
|
||||
} opt;
|
||||
} settings;
|
||||
};
|
||||
|
||||
struct rm3100_data {
|
||||
|
@ -45,6 +64,9 @@ struct rm3100_data {
|
|||
struct {
|
||||
uint8_t odr;
|
||||
} settings;
|
||||
#if defined(CONFIG_RM3100_STREAM)
|
||||
struct rm3100_stream stream;
|
||||
#endif /* CONFIG_RM3100_STREAM */
|
||||
};
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_ */
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
#ifndef ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_REG_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_REG_H_
|
||||
|
||||
/* Address value has a read bit */
|
||||
#define REG_READ_BIT BIT(7)
|
||||
|
||||
/* RM3100 register addresses */
|
||||
#define RM3100_REG_CMM 0x01 /* Continuous measurement mode */
|
||||
#define RM3100_REG_CCX_MSB 0x04 /* Cycle Count X LSB */
|
||||
|
|
261
drivers/sensor/pni/rm3100/rm3100_stream.c
Normal file
261
drivers/sensor/pni/rm3100/rm3100_stream.c
Normal file
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Croxel, Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/drivers/sensor_clock.h>
|
||||
#include <zephyr/rtio/rtio.h>
|
||||
#include <zephyr/sys/check.h>
|
||||
#include "rm3100_stream.h"
|
||||
#include "rm3100_decoder.h"
|
||||
#include "rm3100.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(RM3100_STREAM, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static void rm3100_complete_result(struct rtio *ctx, const struct rtio_sqe *sqe, void *arg)
|
||||
{
|
||||
const struct device *dev = (const struct device *)arg;
|
||||
struct rm3100_data *data = dev->data;
|
||||
struct rtio_iodev_sqe *iodev_sqe = data->stream.iodev_sqe;
|
||||
struct rtio_cqe *cqe;
|
||||
int err = 0;
|
||||
struct rm3100_encoded_data *edata = sqe->userdata;
|
||||
|
||||
edata->header.events.drdy = ((edata->header.status != 0) &&
|
||||
data->stream.settings.enabled.drdy);
|
||||
edata->header.channels = 0;
|
||||
|
||||
if (!edata->header.events.drdy) {
|
||||
LOG_ERR("Status register does not have DRDY bit set: 0x%02x",
|
||||
edata->header.status);
|
||||
} else if (data->stream.settings.opt.drdy == SENSOR_STREAM_DATA_INCLUDE) {
|
||||
edata->header.channels |= rm3100_encode_channel(SENSOR_CHAN_MAGN_XYZ);
|
||||
}
|
||||
|
||||
do {
|
||||
cqe = rtio_cqe_consume(ctx);
|
||||
if (cqe != NULL) {
|
||||
err = cqe->result;
|
||||
rtio_cqe_release(ctx, cqe);
|
||||
}
|
||||
} while (cqe != NULL);
|
||||
|
||||
if (err) {
|
||||
rtio_iodev_sqe_err(iodev_sqe, err);
|
||||
} else {
|
||||
rtio_iodev_sqe_ok(iodev_sqe, 0);
|
||||
}
|
||||
|
||||
LOG_DBG("Streaming read-out complete");
|
||||
}
|
||||
|
||||
static void rm3100_stream_get_data(const struct device *dev)
|
||||
{
|
||||
struct rm3100_data *data = dev->data;
|
||||
uint64_t cycles;
|
||||
int err;
|
||||
|
||||
CHECKIF(!data->stream.iodev_sqe) {
|
||||
LOG_WRN("No RTIO submission with an INT GPIO event");
|
||||
return;
|
||||
}
|
||||
|
||||
struct rtio_iodev_sqe *iodev_sqe = data->stream.iodev_sqe;
|
||||
uint8_t *buf;
|
||||
uint32_t buf_len;
|
||||
uint32_t min_buf_len = sizeof(struct rm3100_encoded_data);
|
||||
struct rm3100_encoded_data *edata;
|
||||
|
||||
err = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
|
||||
|
||||
data->stream.iodev_sqe = NULL;
|
||||
rtio_iodev_sqe_err(iodev_sqe, err);
|
||||
return;
|
||||
}
|
||||
|
||||
edata = (struct rm3100_encoded_data *)buf;
|
||||
|
||||
err = sensor_clock_get_cycles(&cycles);
|
||||
CHECKIF(err) {
|
||||
LOG_ERR("Failed to get timestamp: %d", err);
|
||||
|
||||
data->stream.iodev_sqe = NULL;
|
||||
rtio_iodev_sqe_err(iodev_sqe, err);
|
||||
return;
|
||||
}
|
||||
edata->header.timestamp = sensor_clock_cycles_to_ns(cycles);
|
||||
|
||||
struct rtio_sqe *status_wr_sqe = rtio_sqe_acquire(data->rtio.ctx);
|
||||
struct rtio_sqe *status_rd_sqe = rtio_sqe_acquire(data->rtio.ctx);
|
||||
struct rtio_sqe *write_sqe = rtio_sqe_acquire(data->rtio.ctx);
|
||||
struct rtio_sqe *read_sqe = rtio_sqe_acquire(data->rtio.ctx);
|
||||
struct rtio_sqe *complete_sqe = rtio_sqe_acquire(data->rtio.ctx);
|
||||
|
||||
if (!write_sqe || !read_sqe || !complete_sqe) {
|
||||
LOG_ERR("Failed to acquire RTIO SQEs");
|
||||
rtio_sqe_drop_all(data->rtio.ctx);
|
||||
|
||||
data->stream.iodev_sqe = NULL;
|
||||
rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t val;
|
||||
|
||||
val = RM3100_REG_STATUS;
|
||||
|
||||
rtio_sqe_prep_tiny_write(status_wr_sqe,
|
||||
data->rtio.iodev,
|
||||
RTIO_PRIO_HIGH,
|
||||
&val,
|
||||
1,
|
||||
NULL);
|
||||
status_wr_sqe->flags |= RTIO_SQE_TRANSACTION;
|
||||
|
||||
rtio_sqe_prep_read(status_rd_sqe,
|
||||
data->rtio.iodev,
|
||||
RTIO_PRIO_HIGH,
|
||||
&edata->header.status,
|
||||
sizeof(edata->header.status),
|
||||
NULL);
|
||||
status_rd_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART;
|
||||
status_rd_sqe->flags |= RTIO_SQE_CHAINED;
|
||||
|
||||
val = RM3100_REG_MX;
|
||||
|
||||
rtio_sqe_prep_tiny_write(write_sqe,
|
||||
data->rtio.iodev,
|
||||
RTIO_PRIO_HIGH,
|
||||
&val,
|
||||
1,
|
||||
NULL);
|
||||
write_sqe->flags |= RTIO_SQE_TRANSACTION;
|
||||
|
||||
rtio_sqe_prep_read(read_sqe,
|
||||
data->rtio.iodev,
|
||||
RTIO_PRIO_HIGH,
|
||||
edata->payload,
|
||||
sizeof(edata->payload),
|
||||
NULL);
|
||||
read_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART;
|
||||
read_sqe->flags |= RTIO_SQE_CHAINED;
|
||||
|
||||
rtio_sqe_prep_callback_no_cqe(complete_sqe,
|
||||
rm3100_complete_result,
|
||||
(void *)dev,
|
||||
buf);
|
||||
|
||||
rtio_submit(data->rtio.ctx, 0);
|
||||
}
|
||||
|
||||
static void rm3100_gpio_callback(const struct device *gpio_dev,
|
||||
struct gpio_callback *cb,
|
||||
uint32_t pins)
|
||||
{
|
||||
struct rm3100_stream *stream = CONTAINER_OF(cb,
|
||||
struct rm3100_stream,
|
||||
cb);
|
||||
const struct device *dev = stream->dev;
|
||||
const struct rm3100_config *cfg = dev->config;
|
||||
int err;
|
||||
|
||||
/* Disable interrupts */
|
||||
err = gpio_pin_interrupt_configure_dt(&cfg->int_gpio,
|
||||
GPIO_INT_MODE_DISABLED);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to disable interrupt");
|
||||
return;
|
||||
}
|
||||
|
||||
rm3100_stream_get_data(dev);
|
||||
}
|
||||
|
||||
static inline bool settings_changed(const struct rm3100_stream *a,
|
||||
const struct rm3100_stream *b)
|
||||
{
|
||||
return (a->settings.enabled.drdy != b->settings.enabled.drdy) ||
|
||||
(a->settings.opt.drdy != b->settings.opt.drdy);
|
||||
}
|
||||
|
||||
void rm3100_stream_submit(const struct device *dev,
|
||||
struct rtio_iodev_sqe *iodev_sqe)
|
||||
{
|
||||
const struct sensor_read_config *read_config = iodev_sqe->sqe.iodev->data;
|
||||
struct rm3100_data *data = dev->data;
|
||||
const struct rm3100_config *cfg = dev->config;
|
||||
int err;
|
||||
|
||||
if ((read_config->count != 1) ||
|
||||
(read_config->triggers[0].trigger != SENSOR_TRIG_DATA_READY)) {
|
||||
LOG_ERR("Only SENSOR_TRIG_DATA_READY is supported");
|
||||
rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store context for next submission (handled within callbacks) */
|
||||
data->stream.iodev_sqe = iodev_sqe;
|
||||
|
||||
data->stream.settings.enabled.drdy = true;
|
||||
data->stream.settings.opt.drdy = read_config->triggers[0].opt;
|
||||
|
||||
err = gpio_pin_interrupt_configure_dt(&cfg->int_gpio,
|
||||
GPIO_INT_LEVEL_ACTIVE);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to enable interrupts");
|
||||
|
||||
data->stream.iodev_sqe = NULL;
|
||||
rtio_iodev_sqe_err(iodev_sqe, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int rm3100_stream_init(const struct device *dev)
|
||||
{
|
||||
const struct rm3100_config *cfg = dev->config;
|
||||
struct rm3100_data *data = dev->data;
|
||||
int err;
|
||||
|
||||
/** Needed to get back the device handle from the callback context */
|
||||
data->stream.dev = dev;
|
||||
|
||||
if (!cfg->int_gpio.port) {
|
||||
LOG_ERR("Interrupt GPIO not supplied");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!gpio_is_ready_dt(&cfg->int_gpio)) {
|
||||
LOG_ERR("Interrupt GPIO not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to configure interrupt GPIO");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
gpio_init_callback(&data->stream.cb,
|
||||
rm3100_gpio_callback,
|
||||
BIT(cfg->int_gpio.pin));
|
||||
|
||||
err = gpio_add_callback(cfg->int_gpio.port, &data->stream.cb);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to add interrupt callback");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = gpio_pin_interrupt_configure_dt(&cfg->int_gpio,
|
||||
GPIO_INT_MODE_DISABLED);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to configure interrupt as disabled");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
18
drivers/sensor/pni/rm3100/rm3100_stream.h
Normal file
18
drivers/sensor/pni/rm3100/rm3100_stream.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Croxel Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_RM3100_STREAM_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_RM3100_STREAM_H_
|
||||
|
||||
#include "rm3100.h"
|
||||
|
||||
int rm3100_stream_init(const struct device *dev);
|
||||
|
||||
void rm3100_stream_submit(const struct device *dev,
|
||||
struct rtio_iodev_sqe *iodev_sqe);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_RM3100_STREAM_H_ */
|
|
@ -26,6 +26,13 @@ compatible: "pni,rm3100"
|
|||
include: [sensor-device.yaml, i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
The INT signal default configuration is active-high. The
|
||||
property value should ensure the flags properly describe the
|
||||
signal that is presented to the driver.
|
||||
|
||||
odr:
|
||||
type: int
|
||||
default: 0x96
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue