tests/driver/adc: Add ADC accuracy test

These tests, that expects boards to be properly setup, allow one to
measure ADC value and compare to expected values.

Two tests are added. For one, a reference voltage is expected on the ADC
- and this value can be informed to the test via devicetree. The other
uses DAC to generate a value that is then read from ADC. This assumes
DAC is accurate. Naturally, one could make this a DAC test if ADC is
assumed to be accurate.

As the board setup involve connecting physical pins, they are behind
twister fixtures.

Signed-off-by: Yusuf Ahmed <muhammed.ahmed@intel.com>
Signed-off-by: Ederson de Souza <ederson.desouza@intel.com>
This commit is contained in:
Yusuf Ahmed 2023-09-07 14:46:00 -07:00 committed by David Leach
parent 6514b3b88d
commit f22d30b853
11 changed files with 292 additions and 0 deletions

View File

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

View File

@ -0,0 +1,22 @@
# Copyright (c) 2023 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
mainmenu "ADC accuracy test"
source "Kconfig.zephyr"
# Workaround to have commas on function arguments
ZEPHYR_USER := zephyr,user
config DAC_SOURCE_TEST
bool
default y if $(dt_node_has_prop,/$(ZEPHYR_USER),dac)
config REFERENCE_VOLTAGE_TEST
bool
default y if $(dt_node_has_prop,/$(ZEPHYR_USER),reference_mv)
config NUMBER_OF_PASSES
int "Number of passes"
default 5

View File

@ -0,0 +1,21 @@
ADC accuracy test
This test checks that ADC readings match an expected value. It is
done using two approaches:
- DAC source: a board DAC pin is set to a known value, which is then
read on an ADC one. If they match, the test passes.
- Reference voltage: an ADC channel is read and compared to an expected
value.
For the DAC source, it is expected that DAC and ADC are connected. This
can be indicated for twister runs by setting the fixture "dac_adc_loop".
The test then sets DAC to half its resolution and reads the ADC to see
if they match. Note that DAC and ADC are expected to generate/read
voltage on the same range.
In the reference voltage case, the ADC is expected to be connected to a
known voltage reference, whose value is informed, in millivolts, at
property "reference_mv" from "zephyr,user" node. The test reads the ADC
to see if they match.

View File

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

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Please connect J4.3 and J4.11 together to run this test.
* J4.3 will be the ADC input and J4.11 the DAC output
*/
/ {
zephyr,user {
io-channels = <&adc0 20>;
dac = <&dac0>;
dac-channel-id = <0>;
dac-resolution = <12>;
};
};
&adc0{
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
channel@14 {
reg = <20>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
zephyr,user {
io-channels = <&adc0 12>;
reference_mv = <1100>;
};
};
&adc0{
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
channel@12 {
reg = <12>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

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

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2023 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/dac.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/ztest.h>
#define DIV 2
#define DAC_DEVICE_NODE DT_PROP(DT_PATH(zephyr_user), dac)
extern const struct adc_dt_spec *get_adc_channel(void);
static const struct dac_channel_cfg dac_ch_cfg = {
.channel_id = DT_PROP(DT_PATH(zephyr_user), dac_channel_id),
.resolution = DT_PROP(DT_PATH(zephyr_user), dac_resolution),
.buffered = true
};
static const struct device *init_dac(void)
{
int ret;
const struct device *const dac_dev = DEVICE_DT_GET(DAC_DEVICE_NODE);
zassert_true(device_is_ready(dac_dev), "DAC device is not ready");
ret = dac_channel_setup(dac_dev, &dac_ch_cfg);
zassert_equal(ret, 0,
"Setting up of the first channel failed with code %d", ret);
return dac_dev;
}
static int test_dac_to_adc(void)
{
int ret, write_val;
int32_t sample_buffer = 0;
struct adc_sequence sequence = {
.buffer = &sample_buffer,
.buffer_size = sizeof(sample_buffer),
};
const struct device *dac_dev = init_dac();
const struct adc_dt_spec *adc_channel = get_adc_channel();
write_val = (1U << dac_ch_cfg.resolution) / DIV;
ret = dac_write_value(dac_dev, DT_PROP(DT_PATH(zephyr_user), dac_channel_id), write_val);
zassert_equal(ret, 0, "dac_write_value() failed with code %d", ret);
k_sleep(K_MSEC(10));
adc_sequence_init_dt(adc_channel, &sequence);
ret = adc_read_dt(adc_channel, &sequence);
zassert_equal(ret, 0, "adc_read_dt() failed with code %d", ret);
zassert_within(sample_buffer,
(1U << adc_channel->resolution) / DIV, 32,
"Value %d read from ADC does not match expected range.",
sample_buffer);
return TC_PASS;
}
ZTEST(adc_accuracy_test, test_dac_to_adc)
{
int i;
for (i = 0; i < CONFIG_NUMBER_OF_PASSES; i++) {
zassert_true(test_dac_to_adc() == TC_PASS);
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2023 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/adc.h>
#include <zephyr/ztest.h>
#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)
static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user));
#else
#error "Unsupported board."
#endif
const struct adc_dt_spec *get_adc_channel(void)
{
return &adc_channel;
}
static void *adc_setup(void)
{
int ret;
zassert_true(adc_is_ready_dt(&adc_channel), "ADC device is not ready");
ret = adc_channel_setup_dt(&adc_channel);
zassert_equal(ret, 0,
"Setting up of the ADC channel failed with code %d", ret);
return NULL;
}
ZTEST_SUITE(adc_accuracy_test, NULL, adc_setup, NULL, NULL, NULL);

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2023 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/adc.h>
#include <zephyr/ztest.h>
#define REF_V DT_PROP(DT_PATH(zephyr_user), reference_mv)
extern const struct adc_dt_spec *get_adc_channel(void);
static int test_ref_to_adc(void)
{
int ret;
int32_t sample_buffer = 0;
struct adc_sequence sequence = {
.buffer = &sample_buffer,
.buffer_size = sizeof(sample_buffer),
};
const struct adc_dt_spec *adc_channel = get_adc_channel();
adc_sequence_init_dt(adc_channel, &sequence);
ret = adc_read_dt(adc_channel, &sequence);
zassert_equal(ret, 0, "adc_read_dt() failed with code %d", ret);
ret = adc_raw_to_millivolts_dt(adc_channel, &sample_buffer);
zassert_equal(ret, 0, "adc_raw_to_millivolts_dt() failed with code %d",
ret);
zassert_within(sample_buffer, REF_V, 32,
"Value %d mV read from ADC does not match expected range (%d mV).",
sample_buffer, REF_V);
return TC_PASS;
}
ZTEST(adc_accuracy_test, test_ref_to_adc)
{
int i;
for (i = 0; i < CONFIG_NUMBER_OF_PASSES; i++) {
zassert_true(test_ref_to_adc() == TC_PASS);
}
}

View File

@ -0,0 +1,19 @@
common:
tags:
- adc
- drivers
depends_on:
- adc
tests:
drivers.adc.accuracy.dac_source:
depends_on:
- dac
harness_config:
fixture: dac_adc_loopback
platform_allow:
- frdm_k64f
drivers.adc.accuracy.ref_volt:
harness_config:
fixture: adc_ref_volt
platform_allow:
- frdm_kl25z