sensor: rm3100: Basic functionality
This patch introduces rm3100 magnetometer sensor, with basic support (only read-decode). This driver has bus support for I2C. Signed-off-by: Luis Ubieda <luisf@croxel.com>
This commit is contained in:
parent
8f8b223ff2
commit
c1f3e2c712
13 changed files with 640 additions and 0 deletions
|
@ -22,6 +22,7 @@ add_subdirectory(nordic)
|
|||
add_subdirectory(nuvoton)
|
||||
add_subdirectory(nxp)
|
||||
add_subdirectory(pixart)
|
||||
add_subdirectory(pni)
|
||||
add_subdirectory(realtek)
|
||||
add_subdirectory(renesas)
|
||||
add_subdirectory(rohm)
|
||||
|
|
|
@ -108,6 +108,7 @@ source "drivers/sensor/nordic/Kconfig"
|
|||
source "drivers/sensor/nuvoton/Kconfig"
|
||||
source "drivers/sensor/nxp/Kconfig"
|
||||
source "drivers/sensor/pixart/Kconfig"
|
||||
source "drivers/sensor/pni/Kconfig"
|
||||
source "drivers/sensor/realtek/Kconfig"
|
||||
source "drivers/sensor/renesas/Kconfig"
|
||||
source "drivers/sensor/rohm/Kconfig"
|
||||
|
|
7
drivers/sensor/pni/CMakeLists.txt
Normal file
7
drivers/sensor/pni/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Copyright (c) 2025 Croxel, Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
add_subdirectory_ifdef(CONFIG_RM3100 rm3100)
|
||||
# zephyr-keep-sorted-stop
|
7
drivers/sensor/pni/Kconfig
Normal file
7
drivers/sensor/pni/Kconfig
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Copyright (c) 2025 Croxel, Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
source "drivers/sensor/pni/rm3100/Kconfig"
|
||||
# zephyr-keep-sorted-stop
|
9
drivers/sensor/pni/rm3100/CMakeLists.txt
Normal file
9
drivers/sensor/pni/rm3100/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2025 Croxel, Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources(
|
||||
rm3100.c
|
||||
rm3100_decoder.c
|
||||
)
|
13
drivers/sensor/pni/rm3100/Kconfig
Normal file
13
drivers/sensor/pni/rm3100/Kconfig
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright (c) 2025 Croxel, Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config RM3100
|
||||
bool "RM3100 3-Axis Magnetometer"
|
||||
default y
|
||||
depends on DT_HAS_PNI_RM3100_ENABLED
|
||||
select I2C
|
||||
select I2C_RTIO
|
||||
select SENSOR_ASYNC_API
|
||||
help
|
||||
Enable driver for PNI RM3100 high-accuracy 3-axis magnetometer.
|
181
drivers/sensor/pni/rm3100/rm3100.c
Normal file
181
drivers/sensor/pni/rm3100/rm3100.c
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Croxel, Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT pni_rm3100
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/rtio/rtio.h>
|
||||
#include <zephyr/rtio/work.h>
|
||||
#include <zephyr/sys/check.h>
|
||||
|
||||
#include "rm3100.h"
|
||||
#include "rm3100_reg.h"
|
||||
#include "rm3100_bus.h"
|
||||
#include "rm3100_decoder.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(RM3100, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static void rm3100_complete_result(struct rtio *ctx, const struct rtio_sqe *sqe, void *arg)
|
||||
{
|
||||
struct rtio_iodev_sqe *iodev_sqe = (struct rtio_iodev_sqe *)sqe->userdata;
|
||||
struct rtio_cqe *cqe;
|
||||
int err = 0;
|
||||
|
||||
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("One-shot fetch completed");
|
||||
}
|
||||
|
||||
static void rm3100_submit_one_shot(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
|
||||
{
|
||||
const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
|
||||
const struct sensor_chan_spec *const channels = cfg->channels;
|
||||
const size_t num_channels = cfg->count;
|
||||
uint32_t min_buf_len = sizeof(struct rm3100_encoded_data);
|
||||
int err;
|
||||
uint8_t *buf;
|
||||
uint32_t buf_len;
|
||||
struct rm3100_encoded_data *edata;
|
||||
struct rm3100_data *data = dev->data;
|
||||
|
||||
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);
|
||||
rtio_iodev_sqe_err(iodev_sqe, err);
|
||||
return;
|
||||
}
|
||||
|
||||
edata = (struct rm3100_encoded_data *)buf;
|
||||
|
||||
err = rm3100_encode(dev, channels, num_channels, buf);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to encode sensor data");
|
||||
rtio_iodev_sqe_err(iodev_sqe, err);
|
||||
return;
|
||||
}
|
||||
|
||||
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_iodev_sqe_err(iodev_sqe, -ENOMEM);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t 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,
|
||||
iodev_sqe);
|
||||
|
||||
rtio_submit(data->rtio.ctx, 0);
|
||||
}
|
||||
|
||||
static void rm3100_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
|
||||
{
|
||||
const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
|
||||
|
||||
if (!cfg->is_streaming) {
|
||||
rm3100_submit_one_shot(dev, iodev_sqe);
|
||||
} else {
|
||||
LOG_ERR("Streaming not supported");
|
||||
rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
|
||||
}
|
||||
}
|
||||
|
||||
/* This will be implemented later */
|
||||
static DEVICE_API(sensor, rm3100_driver_api) = {
|
||||
/* API functions will be added here */
|
||||
.submit = rm3100_submit,
|
||||
.get_decoder = rm3100_get_decoder,
|
||||
};
|
||||
|
||||
static int rm3100_init(const struct device *dev)
|
||||
{
|
||||
uint8_t val;
|
||||
int err;
|
||||
|
||||
/* Check device ID to make sure we can talk to the sensor */
|
||||
err = rm3100_bus_read(dev, RM3100_REG_REVID, &val, 1);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Failed to read chip ID");
|
||||
return err;
|
||||
} else if (val != RM3100_REVID_VALUE) {
|
||||
LOG_ERR("Invalid chip ID: 0x%02x, expected 0x%02x",
|
||||
val, RM3100_REVID_VALUE);
|
||||
return -ENODEV;
|
||||
}
|
||||
LOG_DBG("RM3100 chip ID confirmed: 0x%02x", val);
|
||||
|
||||
/** Enable Continuous measurement on all axis */
|
||||
val = RM3100_CMM_ALL_AXIS;
|
||||
|
||||
err = rm3100_bus_write(dev, RM3100_REG_CMM, &val, 1);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Failed to set sensor in Continuous Measurement Mode: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RM3100_DEFINE(inst) \
|
||||
\
|
||||
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 struct rm3100_data rm3100_data_##inst = { \
|
||||
.rtio = { \
|
||||
.iodev = &rm3100_bus_##inst, \
|
||||
.ctx = &rm3100_rtio_ctx_##inst, \
|
||||
}, \
|
||||
}; \
|
||||
\
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(inst, rm3100_init, NULL, \
|
||||
&rm3100_data_##inst, \
|
||||
&rm3100_cfg_##inst, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&rm3100_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(RM3100_DEFINE)
|
46
drivers/sensor/pni/rm3100/rm3100.h
Normal file
46
drivers/sensor/pni/rm3100/rm3100.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Croxel, Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/rtio/rtio.h>
|
||||
#include "rm3100_reg.h"
|
||||
|
||||
/* RM3100 produces 3 bytes (24-bit) of data per axis */
|
||||
#define RM3100_BYTES_PER_AXIS 3
|
||||
#define RM3100_TOTAL_BYTES (RM3100_BYTES_PER_AXIS * 3)
|
||||
|
||||
struct rm3100_encoded_data {
|
||||
struct {
|
||||
uint64_t timestamp;
|
||||
uint8_t channels : 3;
|
||||
} header;
|
||||
union {
|
||||
uint8_t payload[RM3100_TOTAL_BYTES];
|
||||
struct {
|
||||
uint32_t x : 24;
|
||||
uint32_t y : 24;
|
||||
uint32_t z : 24;
|
||||
} __attribute__((__packed__)) magn;
|
||||
};
|
||||
};
|
||||
|
||||
struct rm3100_config {
|
||||
uint32_t unused; /* Will be expanded with stremaing-mode to hold int-gpios */
|
||||
};
|
||||
|
||||
struct rm3100_data {
|
||||
/* RTIO context */
|
||||
struct {
|
||||
struct rtio_iodev *iodev;
|
||||
struct rtio *ctx;
|
||||
} rtio;
|
||||
};
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_H_ */
|
93
drivers/sensor/pni/rm3100/rm3100_bus.h
Normal file
93
drivers/sensor/pni/rm3100/rm3100_bus.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Croxel Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_RM3100_BUS_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_RM3100_BUS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/rtio/rtio.h>
|
||||
|
||||
#include "rm3100.h"
|
||||
#include "rm3100_reg.h"
|
||||
|
||||
static inline int rm3100_bus_read(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t *buf,
|
||||
uint16_t len)
|
||||
{
|
||||
struct rm3100_data *data = dev->data;
|
||||
struct rtio *ctx = data->rtio.ctx;
|
||||
struct rtio_iodev *iodev = data->rtio.iodev;
|
||||
struct rtio_sqe *write_sqe = rtio_sqe_acquire(ctx);
|
||||
struct rtio_sqe *read_sqe = rtio_sqe_acquire(ctx);
|
||||
struct rtio_cqe *cqe;
|
||||
int err;
|
||||
|
||||
if (!write_sqe || !read_sqe) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rtio_sqe_prep_write(write_sqe, iodev, RTIO_PRIO_HIGH, ®, 1, NULL);
|
||||
write_sqe->flags |= RTIO_SQE_TRANSACTION;
|
||||
rtio_sqe_prep_read(read_sqe, iodev, RTIO_PRIO_HIGH, buf, len, NULL);
|
||||
read_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART;
|
||||
|
||||
err = rtio_submit(ctx, 2);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
do {
|
||||
cqe = rtio_cqe_consume(ctx);
|
||||
if (cqe != NULL) {
|
||||
err = cqe->result;
|
||||
rtio_cqe_release(ctx, cqe);
|
||||
}
|
||||
} while (cqe != NULL);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int rm3100_bus_write(const struct device *dev,
|
||||
uint8_t reg,
|
||||
const uint8_t *buf,
|
||||
uint16_t len)
|
||||
{
|
||||
struct rm3100_data *data = dev->data;
|
||||
struct rtio *ctx = data->rtio.ctx;
|
||||
struct rtio_iodev *iodev = data->rtio.iodev;
|
||||
struct rtio_sqe *write_reg_sqe = rtio_sqe_acquire(ctx);
|
||||
struct rtio_sqe *write_buf_sqe = rtio_sqe_acquire(ctx);
|
||||
struct rtio_cqe *cqe;
|
||||
int err;
|
||||
|
||||
if (!write_reg_sqe || !write_buf_sqe) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rtio_sqe_prep_write(write_reg_sqe, iodev, RTIO_PRIO_HIGH, ®, 1, NULL);
|
||||
write_reg_sqe->flags |= RTIO_SQE_TRANSACTION;
|
||||
rtio_sqe_prep_write(write_buf_sqe, iodev, RTIO_PRIO_HIGH, buf, len, NULL);
|
||||
write_buf_sqe->iodev_flags |= RTIO_IODEV_I2C_STOP;
|
||||
|
||||
err = rtio_submit(ctx, 2);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
do {
|
||||
cqe = rtio_cqe_consume(ctx);
|
||||
if (cqe != NULL) {
|
||||
err = cqe->result;
|
||||
rtio_cqe_release(ctx, cqe);
|
||||
}
|
||||
} while (cqe != NULL);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_RM3100_BUS_H_ */
|
220
drivers/sensor/pni/rm3100/rm3100_decoder.c
Normal file
220
drivers/sensor/pni/rm3100/rm3100_decoder.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* 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/sys/util.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include "rm3100.h"
|
||||
|
||||
uint8_t rm3100_encode_channel(enum sensor_channel chan)
|
||||
{
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_MAGN_X:
|
||||
return BIT(0);
|
||||
case SENSOR_CHAN_MAGN_Y:
|
||||
return BIT(1);
|
||||
case SENSOR_CHAN_MAGN_Z:
|
||||
return BIT(2);
|
||||
case SENSOR_CHAN_ALL:
|
||||
case SENSOR_CHAN_MAGN_XYZ:
|
||||
return BIT(0) | BIT(1) | BIT(2);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int rm3100_encode(const struct device *dev,
|
||||
const struct sensor_chan_spec *const channels,
|
||||
size_t num_channels,
|
||||
uint8_t *buf)
|
||||
{
|
||||
struct rm3100_encoded_data *edata = (struct rm3100_encoded_data *)buf;
|
||||
uint64_t cycles;
|
||||
int err;
|
||||
|
||||
edata->header.channels = 0;
|
||||
|
||||
for (size_t i = 0; i < num_channels; i++) {
|
||||
edata->header.channels |= rm3100_encode_channel(channels[i].chan_type);
|
||||
}
|
||||
|
||||
err = sensor_clock_get_cycles(&cycles);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
edata->header.timestamp = sensor_clock_cycles_to_ns(cycles);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm3100_decoder_get_size_info(struct sensor_chan_spec chan_spec,
|
||||
size_t *base_size,
|
||||
size_t *frame_size)
|
||||
{
|
||||
switch (chan_spec.chan_type) {
|
||||
case SENSOR_CHAN_MAGN_X:
|
||||
case SENSOR_CHAN_MAGN_Y:
|
||||
case SENSOR_CHAN_MAGN_Z:
|
||||
*base_size = sizeof(struct sensor_q31_data);
|
||||
*frame_size = sizeof(struct sensor_q31_sample_data);
|
||||
return 0;
|
||||
case SENSOR_CHAN_MAGN_XYZ:
|
||||
*base_size = sizeof(struct sensor_three_axis_data);
|
||||
*frame_size = sizeof(struct sensor_three_axis_data);
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm3100_decoder_get_frame_count(const uint8_t *buffer,
|
||||
struct sensor_chan_spec chan_spec,
|
||||
uint16_t *frame_count)
|
||||
{
|
||||
struct rm3100_encoded_data *edata = (struct rm3100_encoded_data *)buffer;
|
||||
|
||||
if (chan_spec.chan_idx != 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
uint8_t channel_request = rm3100_encode_channel(chan_spec.chan_type);
|
||||
|
||||
if (((edata->header.channels & channel_request) != channel_request)) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
switch (chan_spec.chan_type) {
|
||||
case SENSOR_CHAN_MAGN_X:
|
||||
case SENSOR_CHAN_MAGN_Y:
|
||||
case SENSOR_CHAN_MAGN_Z:
|
||||
case SENSOR_CHAN_MAGN_XYZ:
|
||||
*frame_count = 1;
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int rm3100_convert_raw_to_q31(uint32_t raw_reading, q31_t *out, int8_t *shift)
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
raw_reading = sys_be24_to_cpu(raw_reading);
|
||||
value = sign_extend(raw_reading, 23);
|
||||
|
||||
/** Convert to Gauss, assuming 1 LSB = 75 uT, given default Cycle-Counting (200).
|
||||
* We can represent the largest sample (2^23 LSB) in Gauss with 11 bits.
|
||||
*/
|
||||
*shift = 11;
|
||||
|
||||
int64_t micro_tesla_scaled = ((int64_t)value << (31 - *shift)) / 75;
|
||||
int64_t gauss_scaled = (int64_t)micro_tesla_scaled / 100;
|
||||
|
||||
*out = gauss_scaled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm3100_decoder_decode(const uint8_t *buffer,
|
||||
struct sensor_chan_spec chan_spec,
|
||||
uint32_t *fit,
|
||||
uint16_t max_count,
|
||||
void *data_out)
|
||||
{
|
||||
struct rm3100_encoded_data *edata = (struct rm3100_encoded_data *)buffer;
|
||||
uint8_t channel_request;
|
||||
|
||||
if (*fit != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (max_count == 0 || chan_spec.chan_idx != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (chan_spec.chan_type) {
|
||||
case SENSOR_CHAN_MAGN_X:
|
||||
case SENSOR_CHAN_MAGN_Y:
|
||||
case SENSOR_CHAN_MAGN_Z: {
|
||||
channel_request = rm3100_encode_channel(chan_spec.chan_type);
|
||||
if ((edata->header.channels & channel_request) != channel_request) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
struct sensor_q31_data *out = (struct sensor_q31_data *)data_out;
|
||||
|
||||
out->header.base_timestamp_ns = edata->header.timestamp;
|
||||
out->header.reading_count = 1;
|
||||
|
||||
uint32_t raw_reading;
|
||||
|
||||
if (chan_spec.chan_type == SENSOR_CHAN_MAGN_X) {
|
||||
raw_reading = edata->magn.x;
|
||||
} else if (chan_spec.chan_type == SENSOR_CHAN_MAGN_Y) {
|
||||
raw_reading = edata->magn.y;
|
||||
} else {
|
||||
raw_reading = edata->magn.z;
|
||||
}
|
||||
|
||||
rm3100_convert_raw_to_q31(raw_reading, &out->readings->value, &out->shift);
|
||||
|
||||
*fit = 1;
|
||||
return 1;
|
||||
}
|
||||
case SENSOR_CHAN_MAGN_XYZ: {
|
||||
channel_request = rm3100_encode_channel(chan_spec.chan_type);
|
||||
if ((edata->header.channels & channel_request) != channel_request) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
struct sensor_three_axis_data *out = (struct sensor_three_axis_data *)data_out;
|
||||
|
||||
out->header.base_timestamp_ns = edata->header.timestamp;
|
||||
out->header.reading_count = 1;
|
||||
|
||||
rm3100_convert_raw_to_q31(edata->magn.x, &out->readings[0].x, &out->shift);
|
||||
rm3100_convert_raw_to_q31(edata->magn.y, &out->readings[0].y, &out->shift);
|
||||
rm3100_convert_raw_to_q31(edata->magn.z, &out->readings[0].z, &out->shift);
|
||||
|
||||
*fit = 1;
|
||||
return 1;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool rm3100_decoder_has_trigger(const uint8_t *buffer,
|
||||
enum sensor_trigger_type trigger)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SENSOR_DECODER_API_DT_DEFINE() = {
|
||||
.get_frame_count = rm3100_decoder_get_frame_count,
|
||||
.get_size_info = rm3100_decoder_get_size_info,
|
||||
.decode = rm3100_decoder_decode,
|
||||
.has_trigger = rm3100_decoder_has_trigger,
|
||||
};
|
||||
|
||||
int rm3100_get_decoder(const struct device *dev,
|
||||
const struct sensor_decoder_api **decoder)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
*decoder = &SENSOR_DECODER_NAME();
|
||||
|
||||
return 0;
|
||||
}
|
25
drivers/sensor/pni/rm3100/rm3100_decoder.h
Normal file
25
drivers/sensor/pni/rm3100/rm3100_decoder.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Croxel Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_RM3100_DECODER_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_RM3100_DECODER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include "rm3100.h"
|
||||
|
||||
int rm3100_encode(const struct device *dev,
|
||||
const struct sensor_chan_spec *const channels,
|
||||
size_t num_channels,
|
||||
uint8_t *buf);
|
||||
|
||||
uint8_t rm3100_encode_channel(enum sensor_channel chan);
|
||||
|
||||
int rm3100_get_decoder(const struct device *dev,
|
||||
const struct sensor_decoder_api **decoder);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_RM3100_DECODER_H_ */
|
24
drivers/sensor/pni/rm3100/rm3100_reg.h
Normal file
24
drivers/sensor/pni/rm3100/rm3100_reg.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Croxel, Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_REG_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_REG_H_
|
||||
|
||||
/* RM3100 register addresses */
|
||||
#define RM3100_REG_CMM 0x01 /* Continuous measurement mode */
|
||||
#define RM3100_REG_MX 0x24 /* Measurement results X (3 bytes) */
|
||||
#define RM3100_REG_MY 0x27 /* Measurement results Y (3 bytes) */
|
||||
#define RM3100_REG_MZ 0x2A /* Measurement results Z (3 bytes) */
|
||||
#define RM3100_REG_STATUS 0x34 /* Status of DRDY */
|
||||
#define RM3100_REG_REVID 0x36 /* Hardware revision ID */
|
||||
|
||||
/* Default values */
|
||||
#define RM3100_REVID_VALUE 0x22 /* Expected REVID register value */
|
||||
|
||||
#define RM3100_CMM_ALL_AXIS 0x71
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_PNI_RM3100_REG_H_ */
|
13
dts/bindings/sensor/pni,rm3100.yaml
Normal file
13
dts/bindings/sensor/pni,rm3100.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright (c) 2025 Croxel, Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
PNI RM3100 3-axis Magnetometer.
|
||||
|
||||
The RM3100 can use either I2C or SPI as a communication bus.
|
||||
This driver currently supports I2C only.
|
||||
|
||||
compatible: "pni,rm3100"
|
||||
|
||||
include: [sensor-device.yaml, i2c-device.yaml]
|
Loading…
Add table
Add a link
Reference in a new issue