blob: f1c62ed7624f88d12dd2763d5103fc492a1da2cd [file] [log] [blame]
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/dma.h>
#include <zephyr/drivers/counter.h>
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
/* 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 int16_t m_sample_buffer[BUFFER_SIZE];
#else
static __aligned(32) int16_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(DT_NODELABEL(test_counter), okay) && \
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(DT_NODELABEL(test_counter), okay) && \
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++) {
int16_t sample_value = m_sample_buffer[i];
TC_PRINT("0x%04x ", sample_value);
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);
}