Working DT ADC sample WITHOUT resistor divider
This commit is contained in:
parent
2c21f1f9cb
commit
dcb73c0a25
|
|
@ -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)
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
.. zephyr:code-sample:: adc_dt
|
||||||
|
:name: Analog-to-Digital Converter (ADC) with devicetree
|
||||||
|
:relevant-api: adc_interface
|
||||||
|
|
||||||
|
Read analog inputs from ADC channels.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
********
|
||||||
|
|
||||||
|
This sample demonstrates how to use the :ref:`ADC driver API <adc_api>`.
|
||||||
|
|
||||||
|
Depending on the target board, it reads ADC samples from one or more channels
|
||||||
|
and prints the readings on the console. If voltage of the used reference can
|
||||||
|
be obtained, 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.
|
||||||
|
|
||||||
|
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 ``nucleo_l073rz`` board can be easily adjusted for other boards.
|
||||||
|
|
||||||
|
Configuration of channels (settings like gain, reference, or acquisition time)
|
||||||
|
also needs to be specified in devicetree, in ADC controller child nodes. Also
|
||||||
|
the ADC resolution and oversampling setting (if used) need to be specified
|
||||||
|
there. 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 ST Nucleo L073RZ
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
The sample can be built and executed for the
|
||||||
|
:zephyr:board:`nucleo_l073rz` as follows:
|
||||||
|
|
||||||
|
.. zephyr-app-commands::
|
||||||
|
:zephyr-app: samples/drivers/adc/adc_dt
|
||||||
|
: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:
|
||||||
|
- ADC_0, channel 7: 36 = 65mV
|
||||||
|
|
||||||
|
.. note:: If the ADC is not supported, the output will be an error message.
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/ {
|
/ {
|
||||||
zephyr,user {
|
zephyr,user {
|
||||||
io-channels = <&adc1 1>, <&adc1 12>;
|
io-channels = <&adc1 1>;
|
||||||
|
io-channel-names = "multi_sense";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -16,19 +17,12 @@
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
|
|
||||||
channel@1 {
|
/* This defines channel 1 on adc1. The "1" in io-channels refers to this reg value. */
|
||||||
|
adc_channel_1: channel@1 {
|
||||||
reg = <1>;
|
reg = <1>;
|
||||||
zephyr,gain = "ADC_GAIN_1";
|
zephyr,gain = "ADC_GAIN_1";
|
||||||
zephyr,reference = "ADC_REF_INTERNAL";
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||||
zephyr,resolution = <12>;
|
zephyr,resolution = <12>;
|
||||||
};
|
};
|
||||||
|
|
||||||
channel@c {
|
|
||||||
reg = <0xc>;
|
|
||||||
zephyr,gain = "ADC_GAIN_1";
|
|
||||||
zephyr,reference = "ADC_REF_INTERNAL";
|
|
||||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
|
||||||
zephyr,resolution = <12>;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
CONFIG_ADC=y
|
||||||
|
CONFIG_LOG=y
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
sample:
|
||||||
|
name: ADC devicetree driver sample
|
||||||
|
tests:
|
||||||
|
sample.drivers.adc.adc_dt:
|
||||||
|
tags:
|
||||||
|
- adc
|
||||||
|
depends_on: adc
|
||||||
|
platform_allow:
|
||||||
|
- nucleo_l073rz
|
||||||
|
- disco_l475_iot1
|
||||||
|
- cc3220sf_launchxl
|
||||||
|
- cc3235sf_launchxl
|
||||||
|
- cy8cproto_063_ble
|
||||||
|
- stm32l496g_disco
|
||||||
|
- stm32h735g_disco
|
||||||
|
- nrf51dk/nrf51822
|
||||||
|
- nrf52840dk/nrf52840
|
||||||
|
- nrf54l15dk/nrf54l15/cpuapp
|
||||||
|
- nrf54h20dk/nrf54h20/cpuapp
|
||||||
|
- ophelia4ev/nrf54l15/cpuapp
|
||||||
|
- mec172xevb_assy6906
|
||||||
|
- gd32f350r_eval
|
||||||
|
- gd32f450i_eval
|
||||||
|
- gd32vf103v_eval
|
||||||
|
- gd32f403z_eval
|
||||||
|
- esp32_devkitc/esp32/procpu
|
||||||
|
- esp32s2_saola
|
||||||
|
- esp32c3_devkitm
|
||||||
|
- gd32l233r_eval
|
||||||
|
- lpcxpresso55s36
|
||||||
|
- mr_canhubk3
|
||||||
|
- longan_nano
|
||||||
|
- longan_nano/gd32vf103/lite
|
||||||
|
- rd_rw612_bga
|
||||||
|
- frdm_mcxn947/mcxn947/cpu0
|
||||||
|
- mcx_n9xx_evk/mcxn947/cpu0
|
||||||
|
- frdm_mcxc242
|
||||||
|
- ucans32k1sic
|
||||||
|
- xg24_rb4187c
|
||||||
|
- xg29_rb4412a
|
||||||
|
- raytac_an54l15q_db/nrf54l15/cpuapp
|
||||||
|
- frdm_mcxa166
|
||||||
|
- frdm_mcxa276
|
||||||
|
integration_platforms:
|
||||||
|
- nucleo_l073rz
|
||||||
|
- nrf52840dk/nrf52840
|
||||||
|
harness: console
|
||||||
|
timeout: 10
|
||||||
|
harness_config:
|
||||||
|
type: multi_line
|
||||||
|
regex:
|
||||||
|
- "ADC reading\\[\\d+\\]:"
|
||||||
|
- "- .+, channel \\d+: -?\\d+"
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/ {
|
||||||
|
zephyr,user {
|
||||||
|
io-channels = <&adc0 0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&adc0 {
|
||||||
|
status = "okay";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
channel@0 {
|
||||||
|
reg = <0>;
|
||||||
|
zephyr,gain = "ADC_GAIN_1_4";
|
||||||
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
|
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||||
|
zephyr,resolution = <12>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/ {
|
||||||
|
zephyr,user {
|
||||||
|
io-channels = <&adc0 0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&adc0 {
|
||||||
|
status = "okay";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
channel@0 {
|
||||||
|
reg = <0>;
|
||||||
|
zephyr,gain = "ADC_GAIN_1_4";
|
||||||
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
|
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||||
|
zephyr,resolution = <12>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/ {
|
||||||
|
zephyr,user {
|
||||||
|
io-channels = <&adc0 0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&adc0 {
|
||||||
|
status = "okay";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
channel@0 {
|
||||||
|
reg = <0>;
|
||||||
|
zephyr,gain = "ADC_GAIN_1_4";
|
||||||
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
|
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||||
|
zephyr,resolution = <12>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/ {
|
||||||
|
zephyr,user {
|
||||||
|
io-channels = <&adc0 0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&adc0 {
|
||||||
|
status = "okay";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
channel@0 {
|
||||||
|
reg = <0>;
|
||||||
|
zephyr,gain = "ADC_GAIN_1_4";
|
||||||
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
|
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||||
|
zephyr,resolution = <12>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -1,101 +1,58 @@
|
||||||
/*
|
#include <zephyr/kernel.h>
|
||||||
* Copyright (c) 2020 Libre Solar Technologies GmbH
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <zephyr/device.h>
|
#include <zephyr/device.h>
|
||||||
#include <zephyr/devicetree.h>
|
#include <zephyr/devicetree.h>
|
||||||
#include <zephyr/drivers/adc.h>
|
#include <zephyr/drivers/adc.h>
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/logging/log.h>
|
||||||
#include <zephyr/sys/printk.h>
|
|
||||||
#include <zephyr/sys/util.h>
|
|
||||||
|
|
||||||
#if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || \
|
LOG_MODULE_REGISTER(adc_dt_example, LOG_LEVEL_DBG);
|
||||||
!DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)
|
|
||||||
#error "No suitable devicetree overlay specified"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DT_SPEC_AND_COMMA(node_id, prop, idx) \
|
/* Get the ADC channel specification from the devicetree */
|
||||||
ADC_DT_SPEC_GET_BY_IDX(node_id, idx),
|
#define SENSE_NODE DT_PATH(zephyr_user)
|
||||||
|
static const struct adc_dt_spec sense_channel = ADC_DT_SPEC_GET_BY_NAME(SENSE_NODE, multi_sense);
|
||||||
/* Data of ADC io-channels specified in devicetree. */
|
|
||||||
static const struct adc_dt_spec adc_channels[] = {
|
|
||||||
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels,
|
|
||||||
DT_SPEC_AND_COMMA)
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
uint32_t count = 0;
|
|
||||||
uint16_t buf;
|
uint16_t buf;
|
||||||
struct adc_sequence sequence = {
|
struct adc_sequence sequence = {
|
||||||
.buffer = &buf,
|
.buffer = &buf,
|
||||||
/* buffer size in bytes, not number of samples */
|
|
||||||
.buffer_size = sizeof(buf),
|
.buffer_size = sizeof(buf),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Configure channels individually prior to sampling. */
|
if (!device_is_ready(sense_channel.dev)) {
|
||||||
for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) {
|
LOG_ERR("ADC controller device not ready");
|
||||||
if (!adc_is_ready_dt(&adc_channels[i])) {
|
|
||||||
printk("ADC controller device %s not ready\n", adc_channels[i].dev->name);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = adc_channel_setup_dt(&adc_channels[i]);
|
err = adc_channel_setup_dt(&sense_channel);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printk("Could not setup channel #%d (%d)\n", i, err);
|
LOG_ERR("Could not setup channel #%u (%d)", sense_channel.channel_id, err);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef CONFIG_COVERAGE
|
LOG_INF("ADC channel setup successful!");
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
#else
|
// 1. Explicitly initialize the sequence structure from the devicetree spec.
|
||||||
for (int k = 0; k < 10; k++) {
|
// This sets sequence.channels correctly.
|
||||||
#endif
|
(void)adc_sequence_init_dt(&sense_channel, &sequence);
|
||||||
printk("ADC reading[%u]:\n", count++);
|
|
||||||
for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) {
|
|
||||||
int32_t val_mv;
|
|
||||||
|
|
||||||
printk("- %s, channel %d: ",
|
// 2. Perform the read on the ADC device with the now-configured sequence.
|
||||||
adc_channels[i].dev->name,
|
err = adc_read(sense_channel.dev, &sequence);
|
||||||
adc_channels[i].channel_id);
|
|
||||||
|
|
||||||
(void)adc_sequence_init_dt(&adc_channels[i], &sequence);
|
|
||||||
|
|
||||||
err = adc_read_dt(&adc_channels[i], &sequence);
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printk("Could not read (%d)\n", err);
|
LOG_ERR("Could not read (%d)", err);
|
||||||
|
k_sleep(K_MSEC(1000));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
int32_t val_mv = buf;
|
||||||
* If using differential mode, the 16 bit value
|
err = adc_raw_to_millivolts_dt(&sense_channel, &val_mv);
|
||||||
* in the ADC sample buffer should be a signed 2's
|
|
||||||
* complement value.
|
|
||||||
*/
|
|
||||||
if (adc_channels[i].channel_cfg.differential) {
|
|
||||||
val_mv = (int32_t)((int16_t)buf);
|
|
||||||
} else {
|
|
||||||
val_mv = (int32_t)buf;
|
|
||||||
}
|
|
||||||
printk("%"PRId32, val_mv);
|
|
||||||
err = adc_raw_to_millivolts_dt(&adc_channels[i],
|
|
||||||
&val_mv);
|
|
||||||
/* conversion to mV may not be supported, skip if not */
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printk(" (value in mV not available)\n");
|
LOG_WRN("Could not convert to millivolts (%d)", err);
|
||||||
} else {
|
|
||||||
printk(" = %"PRId32" mV\n", val_mv);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_INF("ADC reading raw: %d -> %d mV", buf, val_mv);
|
||||||
|
|
||||||
k_sleep(K_MSEC(1000));
|
k_sleep(K_MSEC(1000));
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue