samples: drivers: adc: add adc sequence sample

Add sequence usage sample for adc driver.

Signed-off-by: Paulo Santos <pauloroberto.santos@edge.ufal.br>
This commit is contained in:
Paulo Santos 2024-02-28 15:56:11 -03:00 committed by Carles Cufí
parent 71fe0f413b
commit 5243a76c31
32 changed files with 682 additions and 0 deletions

View file

@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ADC)
target_sources(app PRIVATE src/main.c)

View file

@ -0,0 +1,12 @@
# Copyright (c) 2024 Centro de Inovacao EDGE.
# SPDX-License-Identifier: Apache-2.0
config SEQUENCE_SAMPLES
int "Number of samples to be made on the sequence for each channel."
default 5
config SEQUENCE_RESOLUTION
int "Set the resolution of the sequence readings."
default 12
source "Kconfig.zephyr"

View file

@ -0,0 +1,66 @@
.. zephyr:code-sample:: adc_sequence
:name: Analog-to-Digital Converter (ADC) sequence sample
:relevant-api: adc_interface
Read analog inputs from ADC channels, using a sequence.
Overview
********
This sample demonstrates how to use the :ref:`ADC driver API <adc_api>` using sequences.
Depending on the target board, it reads ADC samples from two channels
and prints the readings on the console, based on the sequence specifications.
Notice how for the whole sequence reading, only one call to the :c:func:`adc_read` API is made.
If voltage of the used reference can be obtained, the raw readings are converted to millivolts.
This example constructs an adc device and setups its channels, according to the
given devicetree configuration.
Building and Running
********************
Make sure that the ADC is enabled (``status = "okay";``) and has each channel as a
child node, with your desired settings like gain, reference, or acquisition time and
oversampling setting (if used). It is also needed to provide an alias ``adc0`` for the
desired adc. See :zephyr_file:`boards/nrf52840dk_nrf52840.overlay
<samples/drivers/adc/adc_dt/boards/nrf52840dk_nrf52840.overlay>` for an example of
such setup.
Building and Running for Nordic nRF52840
========================================
The sample can be built and executed for the
:ref:`nrf52840dk_nrf52840` as follows:
.. zephyr-app-commands::
:zephyr-app: samples/drivers/adc/adc_sequence
:board: nrf52840dk/nrf52840
:goals: build flash
:compact:
To build for another board, change "nrf52840dk/nrf52840" above to that board's name
and provide a corresponding devicetree overlay.
Sample output
=============
You should get a similar output as below, repeated every second:
.. code-block:: console
ADC sequence reading [1]:
- ADC_0, channel 0, 5 sequence samples:
- - 36 = 65mV
- - 35 = 63mV
- - 36 = 65mV
- - 35 = 63mV
- - 36 = 65mV
- ADC_0, channel 1, 5 sequence samples:
- - 0 = 0mV
- - 0 = 0mV
- - 1 = 1mV
- - 0 = 0mV
- - 1 = 1mV
.. note:: If the ADC is not supported, the output will be an error message.

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/adc/adc.h>
/ {
aliases {
adc0 = &adc0;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 1)>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,input-positive = <0>; /* P10.0 */
};
channel@1 {
reg = <1>;
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 1)>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,input-positive = <1>; /* P10.1 */
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/adc/adc.h>
/ {
aliases {
adc0 = &adc0;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 1)>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,input-positive = <2>; /* P10.2 */
};
channel@1 {
reg = <1>;
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 1)>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,input-positive = <3>; /* P10.3 */
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/adc/mcux-lpadc.h>
/ {
aliases {
adc0 = &adc0;
};
};
&adc0 {
#address-cells = <1>;
#size-cells = <0>;
/*
* To use this sample:
* - Connect VREFN_TARGET to GND, and VREFP_TARGET to 3v3
* (Resistors J8 and J9, should be populated by default)
* LPADC0 CH0A and CH0B are set up in differential mode
* - Connect LPADC0 CH0A signal to voltage between 0~3.3V (P19 pin 4)
* - Connect LPADC0 CH0B signal to voltage between 0~3.3V (P19 pin 2)
* LPADC0 CH4A is set up in single ended mode
* - Connect LPADC0 CH4A signal to voltage between 0~3.3V (P17 pin 19)
* LPADC0 CH4B is set up in single ended mode
* - Connect LPADC0 CH4B signal to voltage between 0~3.3V (P18 pin 1)
*/
/*
* Channel 0 is used for differential mode, with 13 bit resolution
* CH0A (plus side) is routed to P19 pin 4
* CH0B (minus side) is routed to P19 pin 2
*/
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_EXTERNAL0";
zephyr,vref-mv = <3300>;
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <MCUX_LPADC_CH0A>;
zephyr,input-negative = <MCUX_LPADC_CH0B>;
};
/*
* Channel 1 is used in single ended mode, with 16 bit resolution
* CH4A is routed to P17 pin 19
*/
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_EXTERNAL0";
zephyr,vref-mv = <3300>;
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <MCUX_LPADC_CH4A>;
};
/*
* Channel 2 is used in single ended mode, with 12 bit resolution
* CH4B is routed to P18 pin 1
*/
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
adc0 = &adc0;
};
};
&adc0 {
#address-cells = <1>;
#size-cells = <0>;
channel@4 {
reg = <4>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <0>;
};
channel@5 {
reg = <5>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <0>;
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
adc0 = &adc0;
};
};
&adc0 {
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <0>;
};
channel@3 {
reg = <3>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <0>;
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
adc0 = &adc1;
};
};
&adc1 {
#address-cells = <1>;
#size-cells = <0>;
/*
* To use this sample connect
* J33.1 (ADC1 CH3) and J33.2 (ADC1 CH4) to voltages between 0 and 3.3V
*/
channel@3 {
reg = <3>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
channel@4 {
reg = <4>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/adc/mcux-lpadc.h>
/ {
aliases {
adc0 = &lpadc0;
};
};
&lpadc0 {
#address-cells = <1>;
#size-cells = <0>;
/*
* To use this sample:
* LPADC0 CH0A and CH0B are set up in differential mode (B-A)
* - Connect LPADC0 CH0A signal to voltage between 0~1.8V (J30 pin 1)
* - Connect LPADC0 CH0B signal to voltage between 0~1.8V (J30 pin 2)
* LPADC0 CH2A is set up in single ended mode
* - Connect LPADC0 CH2A signal to voltage between 0~1.8V (J30 pin 3)
*/
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_EXTERNAL0";
zephyr,vref-mv = <1800>;
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <MCUX_LPADC_CH0B>;
zephyr,input-negative = <MCUX_LPADC_CH0A>;
};
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_EXTERNAL0";
zephyr,vref-mv = <1800>;
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <MCUX_LPADC_CH2A>;
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/adc/adc.h>
/ {
aliases {
adc0 = &adc2;
};
};
&adc2 {
group-channel = "precision";
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@3 {
reg = <3>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
channel@4 {
reg = <4>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
adc0 = &adc;
};
};
&adc {
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_AIN1>; /* P0.03 */
};
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_VDD>;
zephyr,oversampling = <8>;
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
adc0 = &adc1;
};
};
&adc1 {
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
adc0 = &adc0;
};
};
&adc0 {
#address-cells = <1>;
#size-cells = <0>;
/* External ADC(+) */
channel@5 {
reg = <5>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_EXTERNAL0";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <5>;
};
/* Internal temperature sensor */
channel@f {
reg = <15>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_EXTERNAL0";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <15>;
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
adc0 = &afec0;
};
};
&afec0 {
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
channel@8 {
reg = <8>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
};

View file

@ -0,0 +1 @@
CONFIG_SEQUENCE_RESOLUTION=12

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
aliases {
adc0 = &afec0;
};
};
&afec0 {
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
channel@8 {
reg = <8>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
};
};

View file

@ -0,0 +1 @@
CONFIG_ADC=y

View file

@ -0,0 +1,21 @@
sample:
name: ADC driver sequence sample
tests:
sample.drivers.adc.adc_sequence:
tags:
- adc
depends_on: adc
platform_allow:
- cy8cproto_063_ble
- cy8cproto_062_4343w
- nrf52840dk/nrf52840
integration_platforms:
- nrf52840dk/nrf52840
harness: console
timeout: 10
harness_config:
type: multi_line
regex:
- "ADC sequence reading\\[\\d+\\]:"
- "- .+, channel \\d+, \\d+ sequence samples:"
- "- - \\d+ (= \\d+mV)|(\\(value in mV not available\\))"

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2024 Centro de Inovacao EDGE
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/adc.h>
#include <zephyr/kernel.h>
/* ADC node from the devicetree. */
#define ADC_NODE DT_ALIAS(adc0)
/* Data of ADC device specified in devicetree. */
static const struct device *adc = DEVICE_DT_GET(ADC_NODE);
/* Data array of ADC channels for the specified ADC. */
static const struct adc_channel_cfg channel_cfgs[] = {
DT_FOREACH_CHILD_SEP(ADC_NODE, ADC_CHANNEL_CFG_DT, (,))};
/* Get the number of channels defined on the DTS. */
#define CHANNEL_COUNT ARRAY_SIZE(channel_cfgs)
int main(void)
{
int err;
uint32_t count = 0;
uint16_t channel_reading[CONFIG_SEQUENCE_SAMPLES][CHANNEL_COUNT];
/* Options for the sequence sampling. */
const struct adc_sequence_options options = {
.extra_samplings = CONFIG_SEQUENCE_SAMPLES - 1,
.interval_us = 0,
};
/* Configure the sampling sequence to be made. */
struct adc_sequence sequence = {
.buffer = channel_reading,
/* buffer size in bytes, not number of samples */
.buffer_size = sizeof(channel_reading),
.resolution = CONFIG_SEQUENCE_RESOLUTION,
.options = &options,
};
if (!device_is_ready(adc)) {
printf("ADC controller device %s not ready\n", adc->name);
return 0;
}
/* Configure channels individually prior to sampling. */
for (size_t i = 0U; i < CHANNEL_COUNT; i++) {
sequence.channels |= BIT(channel_cfgs[i].channel_id);
err = adc_channel_setup(adc, &channel_cfgs[i]);
if (err < 0) {
printf("Could not setup channel #%d (%d)\n", i, err);
return 0;
}
}
while (1) {
printf("ADC sequence reading [%u]:\n", count++);
k_msleep(1000);
err = adc_read(adc, &sequence);
if (err < 0) {
printf("Could not read (%d)\n", err);
continue;
}
for (size_t channel_index = 0U; channel_index < CHANNEL_COUNT; channel_index++) {
int32_t val_mv;
printf("- %s, channel %" PRId32 ", %" PRId32 " sequence samples:\n",
adc->name, channel_cfgs[channel_index].channel_id,
CONFIG_SEQUENCE_SAMPLES);
for (size_t sample_index = 0U; sample_index < CONFIG_SEQUENCE_SAMPLES;
sample_index++) {
val_mv = channel_reading[sample_index][channel_index];
printf("- - %" PRId32, val_mv);
err = adc_raw_to_millivolts(channel_cfgs[channel_index].reference,
channel_cfgs[channel_index].gain,
CONFIG_SEQUENCE_RESOLUTION, &val_mv);
/* conversion to mV may not be supported, skip if not */
if ((err < 0) || channel_cfgs[channel_index].reference == 0) {
printf(" (value in mV not available)\n");
} else {
printf(" = %" PRId32 "mV\n", val_mv);
}
}
}
}
return 0;
}