Skip to content

Commit 9f85a70

Browse files
teburdnashif
authored andcommitted
doc: Revamp sensor docs
Docs now start at a high level, discussing sensors, attributes, channels, and reading data from them. Followed by more detailed usage guides in how to solve common problems sensor users may run into. Additionally renames the sensor_api sphinx tag to sensor Signed-off-by: Tom Burdick <[email protected]>
1 parent 2979cf5 commit 9f85a70

File tree

16 files changed

+684
-222
lines changed

16 files changed

+684
-222
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2024 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/drivers/sensor.h>
8+
9+
#define ACCEL_TRIGGERS \
10+
{ SENSOR_TRIG_DRDY, SENSOR_STREAM_DATA_INCLUDE }, \
11+
{ SENSOR_TRIG_TAP, SENSOR_STREAM_DATA_NOP }
12+
#define ACCEL_ALIAS(id) DT_ALIAS(CONCAT(accel, id))
13+
#define ACCEL_IODEV_SYM(id) CONCAT(accel_iodev, id)
14+
#define ACCEL_IODEV_PTR(id) &ACCEL_IODEV_SYM(id)
15+
#define ACCEL_DEFINE_IODEV(id) \
16+
SENSOR_DT_STREAM_IODEV( \
17+
ACCEL_IODEV_SYM(id), \
18+
ACCEL_ALIAS(id), \
19+
ACCEL_TRIGGERS \
20+
);
21+
22+
#define NUM_SENSORS 2
23+
24+
LISTIFY(NUM_SENSORS, ACCEL_DEFINE_IODEV, (;));
25+
26+
struct sensor_iodev *iodevs[NUM_SENSORS] = { LISTIFY(NUM_SENSORS, ACCEL_IODEV_PTR, (,)) };
27+
28+
RTIO_DEFINE_WITH_MEMPOOL(accel_ctx, NUM_SENSORS, NUM_SENSORS, NUM_SENSORS, 16, sizeof(void *));
29+
30+
int main(void)
31+
{
32+
int rc;
33+
uint32_t accel_frame_iter = 0;
34+
struct sensor_three_axis_data accel_data[2] = {0};
35+
struct sensor_decoder_api *decoder;
36+
struct rtio_cqe *cqe;
37+
uint8_t *buf;
38+
uint32_t buf_len;
39+
struct rtio_sqe *handles[2];
40+
41+
/* Start the streams */
42+
for (int i = 0; i < NUM_SENSORS; i++) {
43+
sensor_stream(iodevs[i], &accel_ctx, NULL, &handles[i]);
44+
}
45+
46+
while (1) {
47+
cqe = rtio_cqe_consume_block(&accel_ctx);
48+
49+
if (cqe->result != 0) {
50+
printk("async read failed %d\n", cqe->result);
51+
return;
52+
}
53+
54+
rc = rtio_cqe_get_mempool_buffer(&accel_ctx, cqe, &buf, &buf_len);
55+
56+
if (rc != 0) {
57+
printk("get mempool buffer failed %d\n", rc);
58+
return;
59+
}
60+
61+
struct device *sensor = ((struct sensor_read_config *)
62+
((struct rtio_iodev *)cqe->userdata)->data)->sensor;
63+
64+
rtio_cqe_release(&accel_ctx, cqe);
65+
66+
rc = sensor_get_decoder(sensor, &decoder);
67+
68+
if (rc != 0) {
69+
printk("sensor_get_decoder failed %d\n", rc);
70+
return;
71+
}
72+
73+
/* Frame iterator values when data comes from a FIFO */
74+
uint32_t accel_fit = 0;
75+
76+
/* Number of accelerometer data frames */
77+
uint32_t frame_count;
78+
79+
rc = decoder->get_frame_count(buf, {SENSOR_CHAN_ACCEL_XYZ, 0},
80+
&frame_count);
81+
if (rc != 0) {
82+
printk("sensor_get_decoder failed %d\n", rc);
83+
return;
84+
}
85+
86+
/* If a tap has occurred lets print it out */
87+
if (decoder->has_trigger(buf, SENSOR_TRIG_TAP)) {
88+
printk("Tap! Sensor %s\n", dev->name);
89+
}
90+
91+
/* Decode all available accelerometer sample frames */
92+
for (int i = 0; i < frame_count; i++) {
93+
decoder->decode(buf, {SENSOR_CHAN_ACCEL_XYZ, 0},
94+
accel_fit, 1, &accel_data);
95+
printk("Accel data for %s " PRIsensor_three_axis_data "\n",
96+
dev->name,
97+
PRIsensor_three_axis_data_arg(accel_data, 0));
98+
}
99+
100+
rtio_release_buffer(&accel_ctx, buf, buf_len);
101+
}
102+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.. _sensor-attribute:
2+
3+
Sensor Attributes
4+
#################
5+
6+
:dfn:`Attributes`, enumerated in :c:enum:`sensor_attribute`, are immutable and
7+
mutable properties of a sensor and its channels.
8+
9+
Attributes allow for obtaining metadata and changing configuration of a sensor.
10+
Common configuration parameters like channel scale, sampling frequency, adjusting
11+
channel offsets, signal filtering, power modes, on chip buffers, and event
12+
handling options are very common. Attributes provide a flexible API for
13+
inspecting and manipulating such device properties.
14+
15+
Attributes are specified using :c:enum:`sensor_attribute` which can be used with
16+
:c:func:`sensor_attr_get` and :c:func:`sensor_attr_set` to get and set a sensors
17+
attributes.
18+
19+
A quick example...
20+
21+
.. code-block:: c
22+
23+
const struct device *accel_dev = DEVICE_DT_GET(DT_ALIAS(accel0));
24+
struct sensor_value accel_sample_rate;
25+
int rc;
26+
27+
rc = sensor_attr_get(accel_dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &accel_sample_rate);
28+
if (rc != 0) {
29+
printk("Failed to get sampling frequency\n");
30+
}
31+
32+
printk("Sample rate for accel %p is %d.06%d\n", accel_dev, accel_sample_rate.val1, accel_sample_rate.val2*1000000);
33+
34+
accel_sample_rate.val1 = 2000;
35+
36+
rc = sensor_attr_set(accel_dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, accel_sample_rate);
37+
if (rc != 0) {
38+
printk("Failed to set sampling frequency\n");
39+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
.. _sensor-channel:
2+
3+
Sensor Channels
4+
###############
5+
6+
:dfn:`Channels`, enumerated in :c:enum:`sensor_channel`, are quantities that
7+
a sensor device can measure.
8+
9+
Sensors may have multiple channels, either to represent different axes of
10+
the same physical property (e.g. acceleration); or because they can measure
11+
different properties altogether (ambient temperature, pressure and
12+
humidity). Sensors may have multiple channels of the same measurement type to
13+
enable measuring many readings of perhaps temperature, light intensity, amperage,
14+
voltage, or capacitance for example.
15+
16+
A channel is specified in Zephyr using a :c:struct:`sensor_chan_spec` which is a
17+
pair containing both the channel type (:c:enum:`sensor_channel`) and channel index.
18+
At times only :c:enum:`sensor_channel` is used but this should be considered
19+
historical since the introduction of :c:struct:`sensor_chan_spec` for Zephyr 3.7.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Device Tree
2+
###########
3+
4+
In the context of sensors device tree provides the initial hardware configuration
5+
for sensors on a per device level. Each device must specify a device tree binding
6+
in Zephyr, and ideally, a set of hardware configuration options for things such
7+
as channel power modes, data rates, filters, decimation, and scales. These can
8+
then be used in a boards devicetree to configure a sensor to its initial state.
9+
10+
.. code-block:: dts
11+
12+
#include <zephyr/dt-bindings/icm42688.h>
13+
14+
&spi0 {
15+
/* SPI bus options here, not shown */
16+
17+
accel_gyro0: icm42688p@0 {
18+
compatible = "invensense,icm42688";
19+
reg = <0>;
20+
int-gpios = <&pioc 6 GPIO_ACTIVE_HIGH>; /* SoC specific pin to select for interrupt line */
21+
spi-max-frequency = <DT_FREQ_M(24)>; /* Maximum SPI bus frequency */
22+
accel-pwr-mode = <ICM42688_ACCEL_LN>; /* Low noise mode */
23+
accel-odr = <ICM42688_ACCEL_ODR_2000>; /* 2000 Hz sampling */
24+
accel-fs = <ICM42688_ACCEL_FS_16>; /* 16G scale */
25+
gyro-pwr-mode = <ICM42688_GYRO_LN>; /* Low noise mode */
26+
gyro-odr = <ICM42688_GYRO_ODR_2000>; /* 2000 Hz sampling */
27+
gyro-fs = <ICM42688_GYRO_FS_16>; /* 16G scale */
28+
};
29+
};
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
.. _sensor-fetch-and-get:
2+
3+
Fetch and Get
4+
#############
5+
6+
The stable and long existing APIs for reading sensor data and handling triggers
7+
are:
8+
9+
* :c:func:`sensor_sample_fetch`
10+
* :c:func:`sensor_sample_fetch_chan`
11+
* :c:func:`sensor_channel_get`
12+
* :c:func:`sensor_trigger_set`
13+
14+
These functions work together. The fetch APIs block the calling context which
15+
must be a thread until the requested :c:enum:`sensor_channel` (or all channels)
16+
has been obtained and stored into the driver instance's private data.
17+
18+
The channel data most recently fetched can then be obtained as a
19+
:c:struct:`sensor_value` by calling :c:func:`sensor_channel_get` for each channel
20+
type.
21+
22+
.. warning::
23+
It should be noted that calling fetch and get from multiple contexts without
24+
a locking mechanism is undefined and most sensor drivers do not attempt to
25+
internally provide exclusive access to the device during or between these
26+
calls.
27+
28+
Polling
29+
*******
30+
31+
Using fetch and get sensor can be read in a polling manner from software threads.
32+
33+
34+
.. literalinclude:: ../../../../samples/sensor/magn_polling/src/main.c
35+
:language: c
36+
37+
Triggers
38+
********
39+
40+
Triggers in the stable API require enabling triggers with a device
41+
specific Kconfig. The device specific Kconfig typically allows selecting the
42+
context the trigger runs. The application then needs to register a callback with
43+
a function signature matching :c:type:`sensor_trigger_handler_t` using
44+
:c:func:`sensor_trigger_set` for the specific triggers (events) to listen for.
45+
46+
.. note::
47+
Triggers may not be set from user mode threads, and the callback is not
48+
run in a user mode context.
49+
50+
There are typically two options provided for each driver where to run trigger
51+
handlers. Either the trigger handler is run using the system work queue thread
52+
(:ref:`workqueues_v2`) or a dedicated thread. A great example can be found in
53+
the BMI160 driver which has Kconfig options for selecting a trigger mode.
54+
See :kconfig:option:`CONFIG_BMI160_TRIGGER_NONE`,
55+
:kconfig:option:`CONFIG_BMI160_TRIGGER_GLOBAL_THREAD` (work queue),
56+
:kconfig:option:`CONFIG_BMI160_TRIGGER_OWN_THREAD` (dedicated thread).
57+
58+
Some notable attributes of using a driver dedicated thread vs the system work
59+
queue.
60+
61+
* Driver dedicated threads have dedicated stack (RAM) which only gets used for
62+
that single trigger handler function.
63+
* Driver dedicated threads *do* get their own priority typically which lets you
64+
prioritize trigger handling among other threads.
65+
* Driver dedicated threads will not have head of line blocking if the driver
66+
needs time to handle the trigger.
67+
68+
.. note::
69+
In all cases it's very likely there will be variable delays from the actual
70+
interrupt to your callback function being run. In the work queue
71+
(GLOBAL_THREAD) case the work queue itself can be the source of variable
72+
latency!
73+
74+
.. literalinclude:: tap_count.c
75+
:language: c

0 commit comments

Comments
 (0)