samples: drivers: adc: add ADC example

This sample shows how to use the ADC API. Depending on the support of
sequential reads in the driver, 1 or 2 channels are read and printed.

A devicetree overlay is necessary to specify the used ADC channel(s).

Signed-off-by: Martin Jäger <martin@libre.solar>
This commit is contained in:
Martin Jäger 2020-05-10 10:38:18 +02:00 committed by Anas Nashif
parent 3c7b2771b1
commit 0520ec814b
6 changed files with 216 additions and 0 deletions

View file

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

View file

@ -0,0 +1,61 @@
.. _adc-sample:
Analog-to-Digital Converter (ADC)
#################################
Overview
********
This sample demonstrates how to use the ADC driver API.
Depending on the MCU type, it reads the ADC samples of one or two ADC channels
and prints the readings to the console. If supported by the driver, the raw
readings are converted to millivolts.
The pins of the ADC channels are board-specific. Please refer to the board
or MCU datasheet for further details.
.. note::
This sample does not work on Nordic platforms where there is a distinction
between channel and analog input that requires additional configuration. See
:zephyr_file:`samples/boards/nrf/battery` for an example of using the ADC
infrastructure on Nordic hardware.
Building and Running
********************
The ADC peripheral and pinmux is configured in the board's ``.dts`` file. Make
sure that the ADC is enabled (``status = "okay";``).
In addition to that, this sample requires an ADC channel specified in the
``io-channels`` property of the ``zephyr,user`` node. This is usually done with
a devicetree overlay. The example overlay in the ``boards`` subdirectory for
the :ref:`nucleo_l073rz_board` can be easily adjusted for other boards.
Building and Running for ST Nucleo L073RZ
=========================================
The sample can be built and executed for the
:ref:`nucleo_l073rz_board` as follows:
.. zephyr-app-commands::
:zephyr-app: samples/drivers/adc
:board: nucleo_l073rz
:goals: build flash
:compact:
To build for another board, change "nucleo_l073rz" 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 reading(s): 42 (raw)
.. note:: If the ADC is not supported, the output will be an error message.

View file

@ -0,0 +1,12 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2020 Libre Solar Technologies GmbH
*/
/ {
zephyr,user {
/* adjust channel number according to pinmux in board.dts */
io-channels = <&adc1 1>;
};
};

View file

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

View file

@ -0,0 +1,12 @@
sample:
name: ADC driver sample
tests:
sample.drivers.adc:
tags: ADC
depends_on: adc
platform_allow: nucleo_l073rz
harness: console
harness_config:
type: single_line
regex:
- "ADC reading(s): (.*)"

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2020 Libre Solar Technologies GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <sys/printk.h>
#include <drivers/adc.h>
#if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || \
!DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)
#error "No suitable devicetree overlay specified"
#endif
#define ADC_NUM_CHANNELS DT_PROP_LEN(DT_PATH(zephyr_user), io_channels)
#if ADC_NUM_CHANNELS > 2
#error "Currently only 1 or 2 channels supported in this sample"
#endif
#if ADC_NUM_CHANNELS == 2 && !DT_SAME_NODE( \
DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), io_channels, 0), \
DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), io_channels, 1))
#error "Channels have to use the same ADC."
#endif
#define ADC_NODE DT_PHANDLE(DT_PATH(zephyr_user), io_channels)
/* Common settings supported by most ADCs */
#define ADC_RESOLUTION 12
#define ADC_GAIN ADC_GAIN_1
#define ADC_REFERENCE ADC_REF_INTERNAL
#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT
/* Get the numbers of up to two channels */
static uint8_t channel_ids[ADC_NUM_CHANNELS] = {
DT_IO_CHANNELS_INPUT_BY_IDX(DT_PATH(zephyr_user), 0),
#if ADC_NUM_CHANNELS == 2
DT_IO_CHANNELS_INPUT_BY_IDX(DT_PATH(zephyr_user), 1)
#endif
};
static int16_t sample_buffer[ADC_NUM_CHANNELS];
struct adc_channel_cfg channel_cfg = {
.gain = ADC_GAIN,
.reference = ADC_REFERENCE,
.acquisition_time = ADC_ACQUISITION_TIME,
/* channel ID will be overwritten below */
.channel_id = 0,
.differential = 0
};
struct adc_sequence sequence = {
/* individual channels will be added below */
.channels = 0,
.buffer = sample_buffer,
/* buffer size in bytes, not number of samples */
.buffer_size = sizeof(sample_buffer),
.resolution = ADC_RESOLUTION,
};
void main(void)
{
int err;
const struct device *dev_adc = DEVICE_DT_GET(ADC_NODE);
if (!device_is_ready(dev_adc)) {
printk("ADC device not found\n");
return;
}
/*
* Configure channels individually prior to sampling
*/
for (uint8_t i = 0; i < ADC_NUM_CHANNELS; i++) {
channel_cfg.channel_id = channel_ids[i];
#ifdef CONFIG_ADC_NRFX_SAADC
channel_cfg.input_positive = SAADC_CH_PSELP_PSELP_AnalogInput0
+ channel_ids[i];
#endif
adc_channel_setup(dev_adc, &channel_cfg);
sequence.channels |= BIT(channel_ids[i]);
}
int32_t adc_vref = adc_ref_internal(dev_adc);
while (1) {
/*
* Read sequence of channels (fails if not supported by MCU)
*/
err = adc_read(dev_adc, &sequence);
if (err != 0) {
printk("ADC reading failed with error %d.\n", err);
return;
}
printk("ADC reading(s):");
for (uint8_t i = 0; i < ADC_NUM_CHANNELS; i++) {
int32_t raw_value = sample_buffer[i];
printk(" %d", raw_value);
if (adc_vref > 0) {
/*
* Convert raw reading to millivolts if driver
* supports reading of ADC reference voltage
*/
int32_t mv_value = raw_value;
adc_raw_to_millivolts(adc_vref, ADC_GAIN,
ADC_RESOLUTION, &mv_value);
printk(" = %d mV ", mv_value);
}
}
printk("\n");
k_sleep(K_MSEC(1000));
}
}