Skip to content

Commit 02c8b3b

Browse files
sample: adc_stream: Add sample app for ADC stream
Add sample application to demonstrate new ADC stream APIs. Two tests are also added: - ad4052-stream - max32-stream Signed-off-by: Vladislav Pejic <vladislav.pejic@orioninc.com>
1 parent e2969a2 commit 02c8b3b

File tree

9 files changed

+297
-0
lines changed

9 files changed

+297
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(adc_stream)
7+
8+
target_sources(app PRIVATE src/main.c)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
.. zephyr:code-sample:: adc_stream
2+
:name: Generic ADC stream
3+
:relevant-api: adc_interface
4+
5+
Get data from a ADC using stream.
6+
7+
Overview
8+
********
9+
10+
This sample application demonstrates how to use ADC stream APIs.
11+
12+
Building and Running
13+
********************
14+
15+
This sample supports one ADC. ADC needs to be aliased as ``adc0`` in devicetree.
16+
For example:
17+
18+
.. code-block:: devicetree
19+
20+
/ {
21+
aliases {
22+
adc0 = &ad4052;
23+
};
24+
};
25+
26+
Make sure the aliase are in devicetree, then build and run with:
27+
28+
.. zephyr-app-commands::
29+
:zephyr-app: samples/drivers/adc/adc_stream
30+
:board: <board to use>
31+
:goals: build flash
32+
:compact:
33+
34+
Sample Output
35+
=============
36+
37+
.. code-block:: console
38+
39+
ADC data for adc405@0 (0.000074) 942995000ns
40+
ADC data for adc405@0 (0.000446) 963059000ns
41+
ADC data for adc405@0 (0.000297) 983124000ns
42+
ADC data for adc405@0 (0.000446) 1003189000ns
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) 2025 Analog Devices, Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
CONFIG_SPI=y
4+
CONFIG_SPI_RTIO=y
5+
CONFIG_AD405X_STREAM=y
6+
CONFIG_COUNTER=y
7+
CONFIG_COUNTER_TIMER_MAX32=y
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright (c) 2025 Analog Devices, Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
&arduino_spi {
8+
adc4052_eval_ad4052_ardz: adc4052@0 {
9+
sampling-period = <20000>; /*uS*/
10+
};
11+
};
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2025 Analog Devices, Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/ {
8+
aliases {
9+
adc0 = &adc;
10+
};
11+
12+
zephyr,user {
13+
io-channels = <&adc 0>;
14+
};
15+
};
16+
17+
&adc_clk_ext_p0_9 {
18+
power-source = <MAX32_VSEL_VDDIOH>;
19+
};
20+
21+
&adc {
22+
reg = <0x40034000 0x1000>;
23+
pinctrl-0 = <&adc_clk_ext_p0_9>;
24+
pinctrl-names = "default";
25+
26+
/* ADC parameters set up 25KSPS sampling rate */
27+
track-count = <96>;
28+
idle-count = <183>;
29+
30+
#address-cells = <1>;
31+
#size-cells = <0>;
32+
#io-channel-cells = <1>;
33+
status = "okay";
34+
compatible = "adi,max32-adc";
35+
36+
channel@0 {
37+
reg = <0>;
38+
zephyr,gain = "ADC_GAIN_1";
39+
zephyr,reference = "ADC_REF_INTERNAL";
40+
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
41+
zephyr,resolution = <12>;
42+
zephyr,vref-mv = <1250>;
43+
};
44+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Copyright (c) 2025 Analog Devices, Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
CONFIG_ADC_MAX32_STREAM=y
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_ADC=y
2+
CONFIG_GPIO=y
3+
CONFIG_ADC_STREAM=y
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
sample:
2+
name: ADC stream sample
3+
common:
4+
tags: adc
5+
harness: console
6+
harness_config:
7+
type: one_line
8+
regex:
9+
- "^ADC data for [^@]+@\\d+ \\([0-9.]+\\) \\d+n$"
10+
tests:
11+
sample.driver.adc_stream:
12+
filter: dt_alias_exists("adc0")
13+
sample.driver.adc_stream.ad4052-stream:
14+
extra_args:
15+
- SHIELD=eval_ad4052_ardz
16+
- EXTRA_CONF_FILE=ad4052-stream.conf
17+
- DTC_OVERLAY_FILE=boards/apard32690_max32690_m4_ad4052.overlay
18+
platform_allow:
19+
- apard32690/max32690/m4
20+
sample.driver.adc_stream.max32-stream:
21+
extra_args:
22+
- EXTRA_CONF_FILE=max32-stream.conf
23+
- DTC_OVERLAY_FILE=boards/apard32690_max32690_m4_max32.overlay
24+
platform_allow:
25+
- apard32690/max32690/m4
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright (c) 2024 Centro de Inovacao EDGE
3+
* Copyright (c) 2025 Analog Devices, Inc.
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
10+
#include <zephyr/device.h>
11+
#include <zephyr/drivers/adc.h>
12+
#include <zephyr/sys/util_macro.h>
13+
#include <zephyr/rtio/rtio.h>
14+
#include <zephyr/kernel.h>
15+
#include <zephyr/dsp/print_format.h>
16+
17+
/* ADC node from the devicetree. */
18+
#define SAMPLE_ADC_NODE DT_ALIAS(adc0)
19+
20+
#define SAMPLE_DT_SPEC_AND_COMMA(node_id, prop, idx) ADC_DT_SPEC_GET_BY_IDX(node_id, idx),
21+
22+
#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)
23+
/* Data of ADC io-channels specified in devicetree. */
24+
static const struct adc_dt_spec adc_channels[] = {
25+
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, SAMPLE_DT_SPEC_AND_COMMA)
26+
};
27+
static const int adc_channels_count = ARRAY_SIZE(adc_channels);
28+
#endif
29+
30+
static void init_adc(void)
31+
{
32+
int i, ret;
33+
34+
ret = adc_is_ready_dt(&adc_channels[0]);
35+
36+
for (i = 0; i < adc_channels_count; i++) {
37+
ret = adc_channel_setup_dt(&adc_channels[i]);
38+
}
39+
}
40+
41+
/* Define triggers for ADC stream. Trigger stands for an event that
42+
* will cause the ADC to read data and perform an operation on it.
43+
*
44+
* Format for each trigger is:
45+
* {trigger, operation to be done on the data associated to the trigger}
46+
*
47+
* For more information about supported triggers and data operations,
48+
* refer to the enums adc_trigger_type and adc_stream_data_opt.
49+
*/
50+
#define SAMPLE_ADC_TRIGGERS \
51+
{ADC_TRIG_FIFO_FULL, ADC_STREAM_DATA_INCLUDE}, \
52+
{ADC_TRIG_FIFO_WATERMARK, ADC_STREAM_DATA_INCLUDE}
53+
54+
ADC_DT_STREAM_IODEV(iodev, SAMPLE_ADC_NODE, adc_channels, SAMPLE_ADC_TRIGGERS);
55+
56+
/* Mempool is used for sharing ADC data that has been read between ADC driver and the application.
57+
* Data read in the ADC driver is stored in the mempool and can be processed in the application.
58+
* Current values are set to support range of ADC drivers and can be optimized for smaller memory
59+
* footprint. sizeof(void *) is used so memory blocks are properly aligned for different
60+
* platforms.
61+
*/
62+
RTIO_DEFINE_WITH_MEMPOOL(adc_ctx, 16, 16, 20, 256, sizeof(void *));
63+
64+
static int print_adc_stream(const struct device *adc, struct rtio_iodev *local_iodev)
65+
{
66+
int rc = 0;
67+
const struct adc_decoder_api *decoder;
68+
struct rtio_cqe *cqe;
69+
uint8_t *buf;
70+
uint32_t buf_len;
71+
struct rtio_sqe *handles;
72+
73+
/* Start the streams */
74+
adc_stream(local_iodev, &adc_ctx, NULL, &handles);
75+
76+
while (1) {
77+
/* CQE is a RTIO completion event. It is created in a ADC driver that is
78+
* doing the streaming when there is a batch of data to be processed
79+
* or some error occurred during ADC streaming. CQE contains result of
80+
* data reading and userdata (if one is passed in adc_stream).
81+
* It is used to retrieve mempool buffer where data is stored.
82+
*/
83+
cqe = rtio_cqe_consume_block(&adc_ctx);
84+
85+
if (cqe->result != 0) {
86+
printk("async read failed %d\n", cqe->result);
87+
return cqe->result;
88+
}
89+
90+
rc = rtio_cqe_get_mempool_buffer(&adc_ctx, cqe, &buf, &buf_len);
91+
92+
if (rc != 0) {
93+
printk("get mempool buffer failed %d\n", rc);
94+
return rc;
95+
}
96+
97+
rtio_cqe_release(&adc_ctx, cqe);
98+
99+
rc = adc_get_decoder(adc, &decoder);
100+
101+
if (rc != 0) {
102+
printk("sensor_get_decoder failed %d\n", rc);
103+
return rc;
104+
}
105+
106+
/* Frame iterator values when data comes from a FIFO */
107+
uint32_t adc_fit = 0;
108+
struct adc_data adc_data = {0};
109+
110+
/* Number of accelerometer data frames */
111+
uint16_t frame_count;
112+
113+
rc = decoder->get_frame_count(buf, 0, &frame_count);
114+
115+
if (rc != 0) {
116+
printk("get_frame_count failed %d\n", rc);
117+
return rc;
118+
}
119+
120+
/* Decode all available accelerometer sample frames */
121+
for (int i = 0; i < frame_count; i++) {
122+
decoder->decode(buf, 0, &adc_fit, 1, &adc_data);
123+
124+
printk("ADC data for %s (%" PRIq(6) ") %lluns\n", adc->name,
125+
PRIq_arg(adc_data.readings[0].value, 6, adc_data.shift),
126+
(adc_data.header.base_timestamp_ns
127+
+ adc_data.readings[0].timestamp_delta));
128+
}
129+
130+
rtio_release_buffer(&adc_ctx, buf, buf_len);
131+
}
132+
133+
return rc;
134+
}
135+
136+
int main(void)
137+
{
138+
int ret;
139+
struct adc_sequence sequence;
140+
struct adc_read_config *read_cfg = iodev.data;
141+
142+
read_cfg->sequence = &sequence;
143+
144+
init_adc();
145+
ret = adc_sequence_init_dt(&adc_channels[0], &sequence);
146+
if (ret < 0) {
147+
printk("Failed to initialize ADC sequence: %d\n", ret);
148+
return 0;
149+
}
150+
151+
print_adc_stream(adc_channels[0].dev, &iodev);
152+
153+
return 0;
154+
}

0 commit comments

Comments
 (0)