/* * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #if CONFIG_ADC_32_BITS_DATA typedef int32_t adc_data_size_t; #define INVALID_ADC_VALUE INT_MIN #else typedef int16_t adc_data_size_t; #endif /* Invalid value that is not supposed to be written by the driver. It is used * to mark the sample buffer entries as empty. If needed, it can be overridden * for a particular board by providing a specific definition above. */ #if !defined(INVALID_ADC_VALUE) #define INVALID_ADC_VALUE SHRT_MIN #endif #if CONFIG_NOCACHE_MEMORY #define __NOCACHE __attribute__((__section__(".nocache"))) #else /* CONFIG_NOCACHE_MEMORY */ #define __NOCACHE #endif /* CONFIG_NOCACHE_MEMORY */ #define BUFFER_SIZE 6 #ifdef CONFIG_TEST_USERSPACE static ZTEST_BMEM adc_data_size_t m_sample_buffer[BUFFER_SIZE]; #else static __aligned(32) adc_data_size_t m_sample_buffer[BUFFER_SIZE] __NOCACHE; #endif #define DT_SPEC_AND_COMMA(node_id, prop, idx) ADC_DT_SPEC_GET_BY_IDX(node_id, idx), #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels) /* 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) }; static const int adc_channels_count = ARRAY_SIZE(adc_channels); #else #error "Unsupported board." #endif const struct device *get_adc_device(void) { if (!adc_is_ready_dt(&adc_channels[0])) { printk("ADC device is not ready\n"); return NULL; } return adc_channels[0].dev; } #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(test_counter)) && \ defined(CONFIG_COUNTER) static void init_counter(void) { int err; const struct device *const dev = DEVICE_DT_GET(DT_NODELABEL(test_counter)); struct counter_top_cfg top_cfg = { .callback = NULL, .user_data = NULL, .flags = 0 }; zassert_true(device_is_ready(dev), "Counter device is not ready"); counter_start(dev); top_cfg.ticks = counter_us_to_ticks(dev, CONFIG_ADC_API_SAMPLE_INTERVAL_US); err = counter_set_top_value(dev, &top_cfg); zassert_equal(0, err, "%s: Counter failed to set top value (err: %d)", dev->name, err); } #endif static void init_adc(void) { int i, ret; zassert_true(adc_is_ready_dt(&adc_channels[0]), "ADC device is not ready"); for (i = 0; i < adc_channels_count; i++) { ret = adc_channel_setup_dt(&adc_channels[i]); zassert_equal(ret, 0, "Setting up of channel %d failed with code %d", i, ret); } for (i = 0; i < BUFFER_SIZE; ++i) { m_sample_buffer[i] = INVALID_ADC_VALUE; } #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(test_counter)) && \ defined(CONFIG_COUNTER) init_counter(); #endif } static void check_samples(int expected_count) { int i; TC_PRINT("Samples read: "); for (i = 0; i < BUFFER_SIZE; i++) { adc_data_size_t sample_value = m_sample_buffer[i]; #if CONFIG_ADC_32_BITS_DATA TC_PRINT("0x%08x ", sample_value); #else TC_PRINT("0x%04hx ", sample_value); #endif if (i < expected_count) { zassert_not_equal(INVALID_ADC_VALUE, sample_value, "[%u] should be filled", i); } else { zassert_equal(INVALID_ADC_VALUE, sample_value, "[%u] should be empty", i); } } TC_PRINT("\n"); } /* * test_adc_sample_one_channel */ static int test_task_one_channel(void) { int ret; struct adc_sequence sequence = { .buffer = m_sample_buffer, .buffer_size = sizeof(m_sample_buffer), }; init_adc(); (void)adc_sequence_init_dt(&adc_channels[0], &sequence); ret = adc_read_dt(&adc_channels[0], &sequence); zassert_equal(ret, 0, "adc_read() failed with code %d", ret); check_samples(1); return TC_PASS; } ZTEST_USER(adc_basic, test_adc_sample_one_channel) { zassert_true(test_task_one_channel() == TC_PASS); } /* * test_adc_sample_multiple_channels */ static int test_task_multiple_channels(void) { int ret; struct adc_sequence sequence = { .buffer = m_sample_buffer, .buffer_size = sizeof(m_sample_buffer), }; init_adc(); (void)adc_sequence_init_dt(&adc_channels[0], &sequence); for (int i = 1; i < adc_channels_count; i++) { sequence.channels |= BIT(adc_channels[i].channel_id); } ret = adc_read_dt(&adc_channels[0], &sequence); if (ret == -ENOTSUP) { ztest_test_skip(); } zassert_equal(ret, 0, "adc_read() failed with code %d", ret); check_samples(adc_channels_count); return TC_PASS; } ZTEST_USER(adc_basic, test_adc_sample_two_channels) { if (adc_channels_count > 1) { zassert_true(test_task_multiple_channels() == TC_PASS); } else { ztest_test_skip(); } } /* * 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 = CONFIG_ADC_API_SAMPLE_INTERVAL_US, }; struct adc_sequence sequence = { .options = &options, .buffer = m_sample_buffer, .buffer_size = sizeof(m_sample_buffer), }; struct k_poll_event async_evt = K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, &async_sig); init_adc(); (void)adc_sequence_init_dt(&adc_channels[0], &sequence); ret = adc_read_async(adc_channels[0].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) */ ZTEST_USER(adc_basic, test_adc_asynchronous_call) { #if defined(CONFIG_ADC_ASYNC) zassert_true(test_task_asynchronous_call() == TC_PASS); #else ztest_test_skip(); #endif /* defined(CONFIG_ADC_ASYNC) */ } /* * test_adc_sample_with_interval */ static uint32_t my_sequence_identifier = 0x12345678; static void *user_data = &my_sequence_identifier; static enum adc_action sample_with_interval_callback(const struct device *dev, const struct adc_sequence *sequence, uint16_t sampling_index) { if (sequence->options->user_data != &my_sequence_identifier) { user_data = sequence->options->user_data; return ADC_ACTION_FINISH; } TC_PRINT("%s: sampling %d\n", __func__, sampling_index); return ADC_ACTION_CONTINUE; } static int test_task_with_interval(void) { int ret; const struct adc_sequence_options options = { .interval_us = 100 * 1000UL, .callback = sample_with_interval_callback, .user_data = user_data, .extra_samplings = 4, }; struct adc_sequence sequence = { .options = &options, .buffer = m_sample_buffer, .buffer_size = sizeof(m_sample_buffer), }; init_adc(); (void)adc_sequence_init_dt(&adc_channels[0], &sequence); ret = adc_read_dt(&adc_channels[0], &sequence); if (ret == -ENOTSUP) { ztest_test_skip(); } zassert_equal(ret, 0, "adc_read() failed with code %d", ret); zassert_equal(user_data, sequence.options->user_data, "Invalid user data: %p, expected: %p", user_data, sequence.options->user_data); check_samples(1 + options.extra_samplings); return TC_PASS; } ZTEST(adc_basic, test_adc_sample_with_interval) { zassert_true(test_task_with_interval() == TC_PASS); } /* * 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) { check_samples(MIN(adc_channels_count, 2)); /* After first sampling continue normally. */ return ADC_ACTION_CONTINUE; } else { check_samples(2 * MIN(adc_channels_count, 2)); /* * 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. */ .interval_us = CONFIG_ADC_API_SAMPLE_INTERVAL_US, }; struct adc_sequence sequence = { .options = &options, .buffer = m_sample_buffer, .buffer_size = sizeof(m_sample_buffer), }; init_adc(); (void)adc_sequence_init_dt(&adc_channels[0], &sequence); if (adc_channels_count > 1) { sequence.channels |= BIT(adc_channels[1].channel_id); } ret = adc_read_dt(&adc_channels[0], &sequence); if (ret == -ENOTSUP) { ztest_test_skip(); } zassert_equal(ret, 0, "adc_read() failed with code %d", ret); return TC_PASS; } ZTEST(adc_basic, test_adc_repeated_samplings) { zassert_true(test_task_repeated_samplings() == TC_PASS); } /* * test_adc_invalid_request */ static int test_task_invalid_request(void) { int ret; struct adc_sequence sequence = { .channels = BIT(adc_channels[0].channel_id), .buffer = m_sample_buffer, .buffer_size = sizeof(m_sample_buffer), .resolution = 0, /* intentionally invalid value */ }; init_adc(); ret = adc_read_dt(&adc_channels[0], &sequence); zassert_not_equal(ret, 0, "adc_read() unexpectedly succeeded"); #if defined(CONFIG_ADC_ASYNC) ret = adc_read_async(adc_channels[0].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_channels[0].resolution; ret = adc_read_dt(&adc_channels[0], &sequence); zassert_equal(ret, 0, "adc_read() failed with code %d", ret); check_samples(1); return TC_PASS; } ZTEST_USER(adc_basic, test_adc_invalid_request) { zassert_true(test_task_invalid_request() == TC_PASS); }