diff --git a/samples/drivers/adc/CMakeLists.txt b/samples/drivers/adc/CMakeLists.txt new file mode 100644 index 00000000000..1e36f6ad972 --- /dev/null +++ b/samples/drivers/adc/CMakeLists.txt @@ -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) diff --git a/samples/drivers/adc/README.rst b/samples/drivers/adc/README.rst new file mode 100644 index 00000000000..2c5a0094fc3 --- /dev/null +++ b/samples/drivers/adc/README.rst @@ -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. diff --git a/samples/drivers/adc/boards/nucleo_l073rz.overlay b/samples/drivers/adc/boards/nucleo_l073rz.overlay new file mode 100644 index 00000000000..e65bb53403e --- /dev/null +++ b/samples/drivers/adc/boards/nucleo_l073rz.overlay @@ -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>; + }; +}; diff --git a/samples/drivers/adc/prj.conf b/samples/drivers/adc/prj.conf new file mode 100644 index 00000000000..488a81dca52 --- /dev/null +++ b/samples/drivers/adc/prj.conf @@ -0,0 +1 @@ +CONFIG_ADC=y diff --git a/samples/drivers/adc/sample.yaml b/samples/drivers/adc/sample.yaml new file mode 100644 index 00000000000..ab10b8b4dad --- /dev/null +++ b/samples/drivers/adc/sample.yaml @@ -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): (.*)" diff --git a/samples/drivers/adc/src/main.c b/samples/drivers/adc/src/main.c new file mode 100644 index 00000000000..35585c6fe51 --- /dev/null +++ b/samples/drivers/adc/src/main.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2020 Libre Solar Technologies GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#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)); + } +}