diff --git a/tests/drivers/adc/adc_dma/CMakeLists.txt b/tests/drivers/adc/adc_dma/CMakeLists.txt new file mode 100644 index 00000000000..1a6c349ded6 --- /dev/null +++ b/tests/drivers/adc/adc_dma/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(adc_dma) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/drivers/adc/adc_dma/boards/frdm_k64f.conf b/tests/drivers/adc/adc_dma/boards/frdm_k64f.conf new file mode 100644 index 00000000000..5692079a8ff --- /dev/null +++ b/tests/drivers/adc/adc_dma/boards/frdm_k64f.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2020, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA=y diff --git a/tests/drivers/adc/adc_dma/boards/frdm_k64f.overlay b/tests/drivers/adc/adc_dma/boards/frdm_k64f.overlay new file mode 100644 index 00000000000..c7ad87d0b57 --- /dev/null +++ b/tests/drivers/adc/adc_dma/boards/frdm_k64f.overlay @@ -0,0 +1,12 @@ +/* +* Copyright (c) 2020, NXP +* +* SPDX-License-Identifier: Apache-2.0 +*/ +&adc0 { + clk-source = <0>; + hw-trigger-src = <4>; + continuous-convert; + high-speed; + periodic-trigger; +}; diff --git a/tests/drivers/adc/adc_dma/boards/frdm_k82f.conf b/tests/drivers/adc/adc_dma/boards/frdm_k82f.conf new file mode 100644 index 00000000000..5692079a8ff --- /dev/null +++ b/tests/drivers/adc/adc_dma/boards/frdm_k82f.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2020, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_ADC_MCUX_ADC16_ENABLE_EDMA=y diff --git a/tests/drivers/adc/adc_dma/boards/frdm_k82f.overlay b/tests/drivers/adc/adc_dma/boards/frdm_k82f.overlay new file mode 100644 index 00000000000..c7ad87d0b57 --- /dev/null +++ b/tests/drivers/adc/adc_dma/boards/frdm_k82f.overlay @@ -0,0 +1,12 @@ +/* +* Copyright (c) 2020, NXP +* +* SPDX-License-Identifier: Apache-2.0 +*/ +&adc0 { + clk-source = <0>; + hw-trigger-src = <4>; + continuous-convert; + high-speed; + periodic-trigger; +}; diff --git a/tests/drivers/adc/adc_dma/prj.conf b/tests/drivers/adc/adc_dma/prj.conf new file mode 100644 index 00000000000..c44105b6a15 --- /dev/null +++ b/tests/drivers/adc/adc_dma/prj.conf @@ -0,0 +1,10 @@ +CONFIG_ZTEST=y + +CONFIG_ADC=y +CONFIG_ADC_ASYNC=y +CONFIG_ADC_LOG_LEVEL_INF=y +CONFIG_HEAP_MEM_POOL_SIZE=1024 +CONFIG_TEST_USERSPACE=y +CONFIG_DMA=y +CONFIG_COUNTER=y +CONFIG_ADC_MCUX_ADC16_HW_TRIGGER=y diff --git a/tests/drivers/adc/adc_dma/src/main.c b/tests/drivers/adc/adc_dma/src/main.c new file mode 100644 index 00000000000..942db23cc43 --- /dev/null +++ b/tests/drivers/adc/adc_dma/src/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include + +extern void test_adc_sample_one_channel(void); +extern void test_adc_sample_two_channels(void); +extern void test_adc_asynchronous_call(void); +extern void test_adc_sample_with_interval(void); +extern void test_adc_repeated_samplings(void); +extern void test_adc_invalid_request(void); +extern const struct device *get_adc_device(void); +extern const struct device *get_count_device(void); +extern struct k_poll_signal async_sig; + +void test_main(void) +{ + k_object_access_grant(get_adc_device(), k_current_get()); + k_object_access_grant(get_count_device(), k_current_get()); +#ifdef CONFIG_ADC_ASYNC + k_object_access_grant(&async_sig, k_current_get()); + k_poll_signal_init(&async_sig); + k_thread_system_pool_assign(k_current_get()); +#endif + + ztest_test_suite(adc_dma_test, + ztest_user_unit_test(test_adc_sample_one_channel), + ztest_user_unit_test(test_adc_sample_two_channels), + ztest_user_unit_test(test_adc_asynchronous_call), + ztest_unit_test(test_adc_sample_with_interval), + ztest_unit_test(test_adc_repeated_samplings), + ztest_user_unit_test(test_adc_invalid_request)); + ztest_run_test_suite(adc_dma_test); +} diff --git a/tests/drivers/adc/adc_dma/src/test_adc.c b/tests/drivers/adc/adc_dma/src/test_adc.c new file mode 100644 index 00000000000..f6f43af2988 --- /dev/null +++ b/tests/drivers/adc/adc_dma/src/test_adc.c @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2020, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include +#include +#include + +#if defined(CONFIG_BOARD_FRDM_K64F) + +#define ADC_DEVICE_NAME DT_LABEL(DT_INST(0, nxp_kinetis_adc16)) +#define ADC_RESOLUTION 12 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 26 + +#elif defined(CONFIG_BOARD_FRDM_K82F) + +#define ADC_DEVICE_NAME DT_LABEL(DT_INST(0, nxp_kinetis_adc16)) +#define ADC_RESOLUTION 12 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 26 + +#endif + +#define HW_TRIGGER_INTERVAL (2U) +/* for DMA HW trigger interval need large than HW trigger interval*/ +#define SAMPLE_INTERVAL_US (10000U) + +#define BUFFER_SIZE 24 +static ZTEST_BMEM int16_t m_sample_buffer[BUFFER_SIZE]; +static ZTEST_BMEM int16_t m_sample_buffer2[2][BUFFER_SIZE]; +static int current_buf_inx; + +static const struct adc_channel_cfg m_1st_channel_cfg = { + .gain = ADC_GAIN, + .reference = ADC_REFERENCE, + .acquisition_time = ADC_ACQUISITION_TIME, + .channel_id = ADC_1ST_CHANNEL_ID, +#if defined(CONFIG_ADC_CONFIGURABLE_INPUTS) + .input_positive = ADC_1ST_CHANNEL_INPUT, +#endif +}; +#if defined(ADC_2ND_CHANNEL_ID) +static const struct adc_channel_cfg m_2nd_channel_cfg = { + .gain = ADC_GAIN, + .reference = ADC_REFERENCE, + .acquisition_time = ADC_ACQUISITION_TIME, + .channel_id = ADC_2ND_CHANNEL_ID, +#if defined(CONFIG_ADC_CONFIGURABLE_INPUTS) + .input_positive = ADC_2ND_CHANNEL_INPUT, +#endif +}; +#endif /* defined(ADC_2ND_CHANNEL_ID) */ + +const struct device *get_adc_device(void) +{ + return device_get_binding(ADC_DEVICE_NAME); +} + +const struct device *get_count_device(void) +{ + char *dev_name = DT_LABEL(DT_NODELABEL(pit0)); + + return device_get_binding(dev_name); +} + +static void init_pit(void) +{ + char *dev_name = DT_LABEL(DT_NODELABEL(pit0)); + int err; + const struct device *dev; + struct counter_top_cfg top_cfg = { .callback = NULL, + .user_data = NULL, + .flags = 0 }; + + dev = device_get_binding(dev_name); + zassert_not_null(dev, "Unable to get counter device %s", dev_name); + + counter_start(dev); + top_cfg.ticks = counter_us_to_ticks(dev, HW_TRIGGER_INTERVAL); + err = counter_set_top_value(dev, &top_cfg); + zassert_equal(0, err, "%s: Counter failed to set top value (err: %d)", + dev_name, err); +} + +static void stop_pit(void) +{ + char *dev_name = DT_LABEL(DT_NODELABEL(pit0)); + const struct device *dev; + + dev = device_get_binding(dev_name); + zassert_not_null(dev, "Unable to get counter device %s", dev_name); + counter_stop(dev); +} + +static const struct device *init_adc(void) +{ + int ret; + const struct device *adc_dev = device_get_binding(ADC_DEVICE_NAME); + + zassert_not_null(adc_dev, "Cannot get ADC device"); + + ret = adc_channel_setup(adc_dev, &m_1st_channel_cfg); + zassert_equal(ret, 0, + "Setting up of the first channel failed with code %d", + ret); + +#if defined(ADC_2ND_CHANNEL_ID) + ret = adc_channel_setup(adc_dev, &m_2nd_channel_cfg); + zassert_equal(ret, 0, + "Setting up of the second channel failed with code %d", + ret); +#endif /* defined(ADC_2ND_CHANNEL_ID) */ + + (void)memset(m_sample_buffer, 0, sizeof(m_sample_buffer)); + + init_pit(); + + return adc_dev; +} + +static void check_samples(int expected_count) +{ + int i; + + TC_PRINT("Samples read: "); + for (i = 0; i < BUFFER_SIZE; i++) { + int16_t sample_value = m_sample_buffer[i]; + + TC_PRINT("0x%04x ", sample_value); + if (i && i % 10 == 0) { + TC_PRINT("\n"); + } + } + TC_PRINT("%d sampled\n", BUFFER_SIZE); +} + +static void check_samples2(int expected_count) +{ + int i; + + TC_PRINT("Samples read: "); + for (i = 0; i < BUFFER_SIZE; i++) { + int16_t sample_value = m_sample_buffer2[current_buf_inx][i]; + + TC_PRINT("0x%04x ", sample_value); + if (i && i % 10 == 0) { + TC_PRINT("\n"); + } + } + TC_PRINT("%d sampled\n", BUFFER_SIZE); +} + +/* + * test_adc_sample_one_channel + */ +static int test_task_one_channel(void) +{ + int ret; + const struct adc_sequence sequence = { + .channels = BIT(ADC_1ST_CHANNEL_ID), + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = ADC_RESOLUTION, + }; + + const struct device *adc_dev = init_adc(); + + if (!adc_dev) { + return TC_FAIL; + } + + ret = adc_read(adc_dev, &sequence); + zassert_equal(ret, 0, "adc_read() failed with code %d", ret); + + check_samples(1); + + return TC_PASS; +} + +void test_adc_sample_one_channel(void) +{ + zassert_true(test_task_one_channel() == TC_PASS, NULL); +} + +/* + * test_adc_sample_two_channels + */ +#if defined(ADC_2ND_CHANNEL_ID) +static int test_task_two_channels(void) +{ + int ret; + const struct adc_sequence sequence = { + .channels = BIT(ADC_1ST_CHANNEL_ID) | BIT(ADC_2ND_CHANNEL_ID), + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = ADC_RESOLUTION, + }; + + const struct device *adc_dev = init_adc(); + + if (!adc_dev) { + return TC_FAIL; + } + + ret = adc_read(adc_dev, &sequence); + zassert_equal(ret, 0, "adc_read() failed with code %d", ret); + + check_samples(2); + + return TC_PASS; +} +#endif /* defined(ADC_2ND_CHANNEL_ID) */ + +void test_adc_sample_two_channels(void) +{ +#if defined(ADC_2ND_CHANNEL_ID) + zassert_true(test_task_two_channels() == TC_PASS, NULL); +#else + ztest_test_skip(); +#endif /* defined(ADC_2ND_CHANNEL_ID) */ +} + +/* + * test_adc_asynchronous_call + */ +#if defined(CONFIG_ADC_ASYNC) +struct k_poll_signal async_sig; + +static int test_task_asynchronous_call(void) +{ + int ret; + const struct adc_sequence_options options = { + .extra_samplings = 4, + /* Start consecutive samplings as fast as possible. */ + .interval_us = HW_TRIGGER_INTERVAL, + }; + const struct adc_sequence sequence = { + .options = &options, + .channels = BIT(ADC_1ST_CHANNEL_ID), + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = ADC_RESOLUTION, + }; + struct k_poll_event async_evt = K_POLL_EVENT_INITIALIZER( + K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, &async_sig); + const struct device *adc_dev = init_adc(); + + if (!adc_dev) { + return TC_FAIL; + } + + ret = adc_read_async(adc_dev, &sequence, &async_sig); + zassert_equal(ret, 0, "adc_read_async() failed with code %d", ret); + + ret = k_poll(&async_evt, 1, K_MSEC(1000)); + zassert_equal(ret, 0, "k_poll failed with error %d", ret); + + check_samples(1 + options.extra_samplings); + + return TC_PASS; +} +#endif /* defined(CONFIG_ADC_ASYNC) */ + +void test_adc_asynchronous_call(void) +{ +#if defined(CONFIG_ADC_ASYNC) + zassert_true(test_task_asynchronous_call() == TC_PASS, NULL); +#else + ztest_test_skip(); +#endif /* defined(CONFIG_ADC_ASYNC) */ +} + +/* + * test_adc_sample_with_interval + */ +static enum adc_action +sample_with_interval_callback(const struct device *dev, + const struct adc_sequence *sequence, + uint16_t sampling_index) +{ + struct adc_sequence *seq = (struct adc_sequence *)sequence; + int _inx = current_buf_inx; + + memcpy(m_sample_buffer, m_sample_buffer2[_inx], + sizeof(m_sample_buffer)); + current_buf_inx = (current_buf_inx == 0) ? 1 : 0; + seq->buffer = m_sample_buffer2[current_buf_inx]; + return ADC_ACTION_CONTINUE; +} + +static int test_task_with_interval(void) +{ + int ret; + int count = 2; + int64_t time_stamp; + int64_t milliseconds_spent; + + const struct adc_sequence_options options = { + .interval_us = 500UL, /*make this double to sample time*/ + .callback = sample_with_interval_callback, + .extra_samplings = 1, + }; + const struct adc_sequence sequence = { + .options = &options, + .channels = BIT(ADC_1ST_CHANNEL_ID), + .buffer = m_sample_buffer2[0], + .buffer_size = sizeof(m_sample_buffer2[0]), + .resolution = ADC_RESOLUTION, + }; + + const struct device *adc_dev = init_adc(); + + if (!adc_dev) { + return TC_FAIL; + } + + current_buf_inx = 0; + + while (count--) { + time_stamp = k_uptime_get(); + ret = adc_read(adc_dev, &sequence); + milliseconds_spent = k_uptime_delta(&time_stamp); + printk("now spend = %lldms\n", milliseconds_spent); + zassert_equal(ret, 0, "adc_read() failed with code %d", ret); + } + check_samples2(1 + options.extra_samplings); + return TC_PASS; +} + +void test_adc_sample_with_interval(void) +{ + zassert_true(test_task_with_interval() == TC_PASS, NULL); +} + +/* + * test_adc_repeated_samplings + */ +static uint8_t m_samplings_done; +static enum adc_action +repeated_samplings_callback(const struct device *dev, + const struct adc_sequence *sequence, + uint16_t sampling_index) +{ + ++m_samplings_done; + TC_PRINT("%s: done %d\n", __func__, m_samplings_done); + if (m_samplings_done == 1U) { +#if defined(ADC_2ND_CHANNEL_ID) + check_samples(2); +#else + check_samples(1); +#endif /* defined(ADC_2ND_CHANNEL_ID) */ + + /* After first sampling continue normally. */ + return ADC_ACTION_CONTINUE; + } +#if defined(ADC_2ND_CHANNEL_ID) + check_samples(4); +#else + check_samples(2); +#endif /* defined(ADC_2ND_CHANNEL_ID) */ + + /* + * The second sampling is repeated 9 times (the samples are + * written in the same place), then the sequence is finished + * prematurely. + */ + if (m_samplings_done < 10) { + return ADC_ACTION_REPEAT; + } else { + return ADC_ACTION_FINISH; + } + +} + +static int test_task_repeated_samplings(void) +{ + int ret; + const struct adc_sequence_options options = { + .callback = repeated_samplings_callback, + /* + * This specifies that 3 samplings are planned. However, + * the callback function above is constructed in such way + * that the first sampling is done normally, the second one + * is repeated 9 times, and then the sequence is finished. + * Hence, the third sampling will not take place. + */ + .extra_samplings = 2, + /* Start consecutive samplings as fast as possible. */ + /* but the interval shall be larger than the HW trigger*/ + .interval_us = SAMPLE_INTERVAL_US, + }; + const struct adc_sequence sequence = { + .options = &options, +#if defined(ADC_2ND_CHANNEL_ID) + .channels = BIT(ADC_1ST_CHANNEL_ID) | BIT(ADC_2ND_CHANNEL_ID), +#else + .channels = BIT(ADC_1ST_CHANNEL_ID), +#endif /* defined(ADC_2ND_CHANNEL_ID) */ + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = ADC_RESOLUTION, + }; + + const struct device *adc_dev = init_adc(); + + if (!adc_dev) { + return TC_FAIL; + } + + ret = adc_read(adc_dev, &sequence); + zassert_equal(ret, 0, "adc_read() failed with code %d", ret); + + return TC_PASS; +} + +void test_adc_repeated_samplings(void) +{ + zassert_true(test_task_repeated_samplings() == TC_PASS, NULL); +} + +/* + * test_adc_invalid_request + */ +static int test_task_invalid_request(void) +{ + int ret; + struct adc_sequence sequence = { + .channels = BIT(ADC_1ST_CHANNEL_ID), + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = 0, /* intentionally invalid value */ + }; + + const struct device *adc_dev = init_adc(); + + if (!adc_dev) { + return TC_FAIL; + } + + ret = adc_read(adc_dev, &sequence); + zassert_not_equal(ret, 0, "adc_read() unexpectedly succeeded"); + +#if defined(CONFIG_ADC_ASYNC) + ret = adc_read_async(adc_dev, &sequence, &async_sig); + zassert_not_equal(ret, 0, "adc_read_async() unexpectedly succeeded"); +#endif + + /* + * Make the sequence parameters valid, now the request should succeed. + */ + sequence.resolution = ADC_RESOLUTION; + + ret = adc_read(adc_dev, &sequence); + zassert_equal(ret, 0, "adc_read() failed with code %d", ret); + + check_samples(1); + + return TC_PASS; +} + +void test_adc_invalid_request(void) +{ + zassert_true(test_task_invalid_request() == TC_PASS, NULL); +} diff --git a/tests/drivers/adc/adc_dma/testcase.yaml b/tests/drivers/adc/adc_dma/testcase.yaml new file mode 100644 index 00000000000..2b3b1ee5910 --- /dev/null +++ b/tests/drivers/adc/adc_dma/testcase.yaml @@ -0,0 +1,6 @@ +common: + tags: adc dma trigger +tests: + drivers.adc-dma: + depends_on: adc dma pit + platform_allow: frdm_k82f frdm_k64f