Skip to content

Commit bb2c00e

Browse files
nordic-baminordic-piks
authored andcommitted
tests: drivers: adc: Implement ADC latency test
Measure ADC sampling call latency. Signed-off-by: Bartosz Miller <[email protected]>
1 parent afc305d commit bb2c00e

File tree

10 files changed

+359
-0
lines changed

10 files changed

+359
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,7 @@
861861
/tests/drivers/timer/grtc_timer_lfrc/ @nrfconnect/ncs-low-level-test
862862
/tests/drivers/uart/ @nrfconnect/ncs-low-level-test
863863
/tests/drivers/watchdog/ @nrfconnect/ncs-low-level-test
864+
/tests/drivers/adc/ @nrfconnect/ncs-low-level-test
864865
/tests/lib/at_cmd_parser/ @nrfconnect/ncs-modem
865866
/tests/lib/at_cmd_custom/ @nrfconnect/ncs-modem
866867
/tests/lib/at_parser/ @nrfconnect/ncs-modem

scripts/ci/tags.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,6 +1748,7 @@ ci_tests_zephyr_drivers_adc:
17481748
files:
17491749
- nrf/tests/zephyr/drivers/adc/
17501750
- zephyr/tests/drivers/adc/
1751+
- nrf/tests/drivers/adc/
17511752

17521753
ci_samples_zephyr_drivers_adc:
17531754
files:
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(adc_latency)
7+
8+
FILE(GLOB app_sources src/*.c)
9+
target_sources(app PRIVATE ${app_sources})
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
/ {
8+
aliases {
9+
tst-timer = &timer0;
10+
};
11+
12+
zephyr,user {
13+
test-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
14+
io-channels = <&adc 0>;
15+
io-channel-names = "A2";
16+
};
17+
};
18+
19+
dut_adc: &adc {
20+
#address-cells = <1>;
21+
#size-cells = <0>;
22+
23+
channel@0 {
24+
reg = <0>;
25+
zephyr,gain = "ADC_GAIN_1";
26+
zephyr,reference = "ADC_REF_INTERNAL";
27+
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
28+
zephyr,input-positive = <NRF_SAADC_AIN1>;
29+
};
30+
};
31+
32+
&gpio1 {
33+
status = "okay";
34+
};
35+
36+
&timer0 {
37+
status = "okay";
38+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
/*
8+
* Required loopbacks:
9+
* P0.0.5 (AIN1) <-> P0.04
10+
*/
11+
12+
/ {
13+
aliases {
14+
tst-timer = &timer0;
15+
};
16+
17+
zephyr,user {
18+
test-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
19+
io-channels = <&adc 0>;
20+
io-channel-names = "A2";
21+
};
22+
};
23+
24+
dut_adc: &adc {
25+
#address-cells = <1>;
26+
#size-cells = <0>;
27+
28+
channel@0 {
29+
reg = <0>;
30+
zephyr,gain = "ADC_GAIN_1";
31+
zephyr,reference = "ADC_REF_INTERNAL";
32+
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
33+
zephyr,input-positive = <NRF_SAADC_AIN1>;
34+
};
35+
};
36+
37+
&gpio0 {
38+
status = "okay";
39+
};
40+
41+
&timer0 {
42+
status = "okay";
43+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
/*
8+
* Required loopbacks:
9+
* P1.02 (AIN2) <-> P1.03
10+
*/
11+
12+
/ {
13+
aliases {
14+
tst-timer = &timer130;
15+
};
16+
17+
zephyr,user {
18+
test-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
19+
io-channels = <&adc 2>;
20+
io-channel-names = "A2";
21+
};
22+
};
23+
24+
dut_adc: &adc {
25+
#address-cells = <1>;
26+
#size-cells = <0>;
27+
28+
channel@0 {
29+
reg = <0>;
30+
zephyr,gain = "ADC_GAIN_1";
31+
zephyr,reference = "ADC_REF_INTERNAL";
32+
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
33+
zephyr,input-positive = <NRF_SAADC_AIN2>;
34+
};
35+
};
36+
37+
&gpio1 {
38+
status = "okay";
39+
};
40+
41+
&timer130 {
42+
status = "okay";
43+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
/*
8+
* Required loopbacks:
9+
* P1.06 (AIN2) <-> VDDIO (Zephyr test loopback set)
10+
*/
11+
12+
/ {
13+
aliases {
14+
tst-timer = &timer20;
15+
};
16+
17+
zephyr,user {
18+
test-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>;
19+
io-channels = <&adc 0>;
20+
io-channel-names = "A2";
21+
};
22+
};
23+
24+
dut_adc: &adc {
25+
#address-cells = <1>;
26+
#size-cells = <0>;
27+
28+
channel@0 {
29+
reg = <0>;
30+
zephyr,gain = "ADC_GAIN_1";
31+
zephyr,reference = "ADC_REF_INTERNAL";
32+
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
33+
zephyr,input-positive = <NRF_SAADC_AIN1>;
34+
};
35+
};
36+
37+
&gpio1 {
38+
status = "okay";
39+
};
40+
41+
&timer20 {
42+
status = "okay";
43+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CONFIG_ADC=y
2+
CONFIG_GPIO=y
3+
CONFIG_COUNTER=y
4+
CONFIG_ZTEST=y
5+
CONFIG_PM_DEVICE_RUNTIME=n
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* Copyright (c) 2025, Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/drivers/adc.h>
9+
#include <zephyr/drivers/gpio.h>
10+
#include <zephyr/drivers/counter.h>
11+
#include <zephyr/ztest.h>
12+
13+
#define ADC_NODE DT_NODELABEL(dut_adc)
14+
#define DT_SPEC_AND_COMMA(node_id, prop, idx) ADC_DT_SPEC_GET_BY_IDX(node_id, idx),
15+
#define MEASUREMENT_REPEATS 10
16+
#define TEST_TIMER_COUNT_TIME_LIMIT_MS 500
17+
#define ADC_MINIMAL_READING_FOR_HIGH_LEVEL 4000
18+
#define ADC_BUFFER_MAX_SIZE 500
19+
#define MAX_TOLERANCE 3.0
20+
21+
static int16_t adc_sample_buffer[ADC_BUFFER_MAX_SIZE];
22+
23+
static const struct device *adc = DEVICE_DT_GET(ADC_NODE);
24+
static const struct gpio_dt_spec test_gpio = GPIO_DT_SPEC_GET(DT_PATH(zephyr_user), test_gpios);
25+
static struct adc_channel_cfg channel_cfgs[] = {
26+
DT_FOREACH_CHILD_SEP(ADC_NODE, ADC_CHANNEL_CFG_DT, (,))};
27+
static const struct adc_dt_spec adc_channels[] = {
28+
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, DT_SPEC_AND_COMMA)};
29+
const struct device *const tst_timer_dev = DEVICE_DT_GET(DT_ALIAS(tst_timer));
30+
31+
void configure_test_timer(const struct device *timer_dev, uint32_t count_time_ms)
32+
{
33+
struct counter_alarm_cfg counter_cfg;
34+
35+
counter_cfg.flags = 0;
36+
counter_cfg.ticks = counter_us_to_ticks(timer_dev, (uint64_t)count_time_ms * 1000);
37+
counter_cfg.user_data = &counter_cfg;
38+
}
39+
40+
void *test_setup(void)
41+
{
42+
zassert_true(adc_is_ready_dt(&adc_channels[0]), "ADC channel 0 is not ready\n");
43+
zassert_true(gpio_is_ready_dt(&test_gpio), "Test GPIO is not ready\n");
44+
zassert_ok(gpio_pin_configure_dt(&test_gpio, GPIO_OUTPUT),
45+
"Failed to configure test pin\n");
46+
gpio_pin_set_dt(&test_gpio, 1);
47+
48+
return NULL;
49+
}
50+
51+
static uint32_t calculate_theoretical_sampling_time_us(uint32_t sampling_interval_us,
52+
uint16_t extra_samples)
53+
{
54+
return sampling_interval_us * (1 + extra_samples);
55+
}
56+
57+
/*
58+
* Note that interval_us >> (tACQ + tconv) must be fulfilled
59+
* to achieve constant sampling rate
60+
* tconv is ~2us
61+
*/
62+
static void test_adc_latency(uint32_t acquisition_time_us, uint32_t sampling_interval_us,
63+
uint16_t extra_samples)
64+
{
65+
int ret;
66+
uint32_t tst_timer_value;
67+
uint64_t timer_value_us[MEASUREMENT_REPEATS];
68+
uint64_t average_timer_value_us = 0;
69+
uint64_t theoretical_sampling_time_us;
70+
uint64_t maximal_allowed_sampling_time_us;
71+
#if !defined(CONFIG_SOC_NRF52840)
72+
uint16_t adc_buffer_length = 1 + extra_samples;
73+
#endif
74+
const struct adc_sequence_options options = {
75+
.interval_us = sampling_interval_us,
76+
.extra_samplings = extra_samples,
77+
};
78+
struct adc_sequence sequence = {
79+
.options = &options,
80+
.buffer = adc_sample_buffer,
81+
.buffer_size = sizeof(adc_sample_buffer),
82+
.channels = 1,
83+
.resolution = 12,
84+
};
85+
channel_cfgs[0].acquisition_time =
86+
ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, acquisition_time_us);
87+
88+
TC_PRINT("ADC latency test for %uus acquisition time, %uus sampling interval, %u extra "
89+
"samples\n",
90+
acquisition_time_us, sampling_interval_us, extra_samples);
91+
92+
ret = adc_channel_setup(adc, &channel_cfgs[0]);
93+
zassert_ok(ret, "ADC channel 0 setup failed\n: %d", ret);
94+
95+
configure_test_timer(tst_timer_dev, TEST_TIMER_COUNT_TIME_LIMIT_MS);
96+
97+
theoretical_sampling_time_us =
98+
calculate_theoretical_sampling_time_us(sampling_interval_us, extra_samples);
99+
maximal_allowed_sampling_time_us = MAX_TOLERANCE * theoretical_sampling_time_us;
100+
101+
for (uint32_t repeat_counter = 0; repeat_counter < MEASUREMENT_REPEATS; repeat_counter++) {
102+
counter_reset(tst_timer_dev);
103+
counter_start(tst_timer_dev);
104+
ret = adc_read(adc, &sequence);
105+
counter_get_value(tst_timer_dev, &tst_timer_value);
106+
counter_stop(tst_timer_dev);
107+
zassert_ok(ret, "ADC read failed: %d\n", ret);
108+
109+
timer_value_us[repeat_counter] =
110+
counter_ticks_to_us(tst_timer_dev, tst_timer_value);
111+
average_timer_value_us += timer_value_us[repeat_counter] / MEASUREMENT_REPEATS;
112+
113+
#if !defined(CONFIG_SOC_NRF52840)
114+
/*
115+
* There is no available loopback option
116+
* to verify the sample data on nrf52840
117+
*/
118+
for (int i = 0; i < adc_buffer_length; i++) {
119+
zassert_true(adc_sample_buffer[i] > ADC_MINIMAL_READING_FOR_HIGH_LEVEL,
120+
"Sample %u is below the minimal ADC reading for high level\n",
121+
i);
122+
}
123+
#endif
124+
}
125+
126+
TC_PRINT("Calculated ADC sampling time for %uus acquisition time, %uus sampling interval, "
127+
"%u "
128+
"extra samples [us]: %llu\n",
129+
acquisition_time_us, sampling_interval_us, extra_samples,
130+
theoretical_sampling_time_us);
131+
TC_PRINT("Measured ADC sampling time for %uus acquisition time, %uus sampling interval, %u "
132+
"extra samples [us]: %llu\n",
133+
acquisition_time_us, sampling_interval_us, extra_samples, average_timer_value_us);
134+
TC_PRINT("Measured - calculated ADC sampling time for %uus acquisition time, %uus sampling "
135+
"interval, %u "
136+
"extra samples [us]: %lld\n",
137+
acquisition_time_us, sampling_interval_us, extra_samples,
138+
average_timer_value_us - theoretical_sampling_time_us);
139+
140+
zassert_true(average_timer_value_us < maximal_allowed_sampling_time_us,
141+
"Measured sampling time is over the specified limit\n");
142+
}
143+
144+
ZTEST(adc_latency, test_adc_read_call_latency)
145+
{
146+
#if defined(CONFIG_SOC_NRF54H20) || defined(CONFIG_SOC_NRF54L15)
147+
test_adc_latency(5, 40, 9);
148+
test_adc_latency(5, 40, 99);
149+
test_adc_latency(5, 40, 499);
150+
test_adc_latency(20, 100, 9);
151+
test_adc_latency(20, 100, 99);
152+
test_adc_latency(20, 100, 499);
153+
#endif
154+
test_adc_latency(40, 200, 9);
155+
test_adc_latency(40, 200, 99);
156+
test_adc_latency(40, 200, 499);
157+
}
158+
159+
ZTEST_SUITE(adc_latency, NULL, test_setup, NULL, NULL, NULL);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
common:
2+
tags:
3+
- drivers
4+
- ci_tests_zephyr_drivers_adc
5+
platform_allow:
6+
- nrf54h20dk/nrf54h20/cpuapp
7+
- nrf54l15dk/nrf54l15/cpuapp
8+
- nrf5340dk/nrf5340/cpuapp
9+
- nrf52840dk/nrf52840
10+
integration_platforms:
11+
- nrf54h20dk/nrf54h20/cpuapp
12+
- nrf54l15dk/nrf54l15/cpuapp
13+
- nrf5340dk/nrf5340/cpuapp
14+
- nrf52840dk/nrf52840
15+
16+
tests:
17+
drivers.adc.adc_latency: {}

0 commit comments

Comments
 (0)