diff --git a/tests/drivers/adc/adc_accuracy_test/CMakeLists.txt b/tests/drivers/adc/adc_accuracy_test/CMakeLists.txt new file mode 100644 index 00000000000..8c376af3eef --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/CMakeLists.txt @@ -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) diff --git a/tests/drivers/adc/adc_accuracy_test/Kconfig b/tests/drivers/adc/adc_accuracy_test/Kconfig new file mode 100644 index 00000000000..9264c69f8bb --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/Kconfig @@ -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 diff --git a/tests/drivers/adc/adc_accuracy_test/README.txt b/tests/drivers/adc/adc_accuracy_test/README.txt new file mode 100644 index 00000000000..6c233052001 --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/README.txt @@ -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. diff --git a/tests/drivers/adc/adc_accuracy_test/boards/frdm_k64f.conf b/tests/drivers/adc/adc_accuracy_test/boards/frdm_k64f.conf new file mode 100644 index 00000000000..5ddebe8a96e --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/boards/frdm_k64f.conf @@ -0,0 +1 @@ +CONFIG_DAC=y diff --git a/tests/drivers/adc/adc_accuracy_test/boards/frdm_k64f.overlay b/tests/drivers/adc/adc_accuracy_test/boards/frdm_k64f.overlay new file mode 100644 index 00000000000..ce9acbd1677 --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/boards/frdm_k64f.overlay @@ -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 = ; + zephyr,resolution = <12>; + }; +}; diff --git a/tests/drivers/adc/adc_accuracy_test/boards/frdm_kl25z.overlay b/tests/drivers/adc/adc_accuracy_test/boards/frdm_kl25z.overlay new file mode 100644 index 00000000000..148ecda4535 --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/boards/frdm_kl25z.overlay @@ -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 = ; + zephyr,resolution = <12>; + }; +}; diff --git a/tests/drivers/adc/adc_accuracy_test/prj.conf b/tests/drivers/adc/adc_accuracy_test/prj.conf new file mode 100644 index 00000000000..c82d81afb56 --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ZTEST=y +CONFIG_ADC=y diff --git a/tests/drivers/adc/adc_accuracy_test/src/dac_source.c b/tests/drivers/adc/adc_accuracy_test/src/dac_source.c new file mode 100644 index 00000000000..34a857fb011 --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/src/dac_source.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#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); + } +} diff --git a/tests/drivers/adc/adc_accuracy_test/src/main.c b/tests/drivers/adc/adc_accuracy_test/src/main.c new file mode 100644 index 00000000000..04fa08f70fb --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/src/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#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); diff --git a/tests/drivers/adc/adc_accuracy_test/src/ref_volt.c b/tests/drivers/adc/adc_accuracy_test/src/ref_volt.c new file mode 100644 index 00000000000..1c4806b249e --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/src/ref_volt.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#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); + } +} diff --git a/tests/drivers/adc/adc_accuracy_test/testcase.yaml b/tests/drivers/adc/adc_accuracy_test/testcase.yaml new file mode 100644 index 00000000000..07bf2e50119 --- /dev/null +++ b/tests/drivers/adc/adc_accuracy_test/testcase.yaml @@ -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