drivers: adc: Introduce reworked API

This commit replaces the API for ADC drivers with a reworked one.
As requested in the issue #3980, the adc_enable/adc_disable functions
are removed. Additionaly, some new features are introduced, like:
- asynchronous calls
- configuration of channels
- multi-channel sampling

Common parts of code that are supposed to appear in each implementation
of the driver (like locking, synchronization, triggering of consecutive
samplings) are provided in the "adc_context.h" file to keep consistency
with the SPI driver. Syscalls are no longer present in the API because
the functions starting read requests cannot use them, since they can be
provided with a callback that is executed in the ISR context, and there
is no point in supporting syscalls only for the channels configuration.

"adc_api" test is updated and extended with additional test cases,
with intention to show how the API is supposed to be used.
"adc_simple" test is removed as it does not seem to add much value.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2018-05-21 15:00:28 +02:00 committed by Anas Nashif
commit f1891e9473
15 changed files with 921 additions and 386 deletions

View file

@ -1,10 +1,8 @@
zephyr_library() zephyr_library()
zephyr_library_sources_ifdef(CONFIG_ADC_DW adc_dw.c) zephyr_library_sources_ifdef(CONFIG_ADC_DW adc_dw.c)
zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_ADC16 adc_mcux_adc16.c) zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_ADC16 adc_mcux_adc16.c)
zephyr_library_sources_ifdef(CONFIG_ADC_QMSI adc_qmsi.c) zephyr_library_sources_ifdef(CONFIG_ADC_QMSI adc_qmsi.c)
zephyr_library_sources_ifdef(CONFIG_ADC_QMSI_SS adc_qmsi_ss.c) zephyr_library_sources_ifdef(CONFIG_ADC_QMSI_SS adc_qmsi_ss.c)
zephyr_library_sources_ifdef(CONFIG_ADC_SAM_AFEC adc_sam_afec.c) zephyr_library_sources_ifdef(CONFIG_ADC_SAM_AFEC adc_sam_afec.c)
zephyr_library_sources_ifdef(CONFIG_ADC_TI_ADC108S102 adc_ti_adc108s102.c) zephyr_library_sources_ifdef(CONFIG_ADC_TI_ADC108S102 adc_ti_adc108s102.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE adc_handlers.c)

View file

@ -12,10 +12,22 @@
menuconfig ADC menuconfig ADC
bool "ADC drivers" bool "ADC drivers"
help help
Enable ADC (Analog to Digital Converter) driver configuration Enable ADC (Analog to Digital Converter) driver configuration.
if ADC if ADC
# By selecting or not this option particular ADC drivers indicate if it is
# required to explicitly specify analog inputs when configuring channels or
# just the channel identifier is sufficient.
config ADC_CONFIGURABLE_INPUTS
bool
config ADC_ASYNC
bool "Enable asynchronous call support"
select POLL
help
This option enables the asynchronous API calls.
config SYS_LOG_ADC_LEVEL config SYS_LOG_ADC_LEVEL
int "ADC drivers log level" int "ADC drivers log level"
depends on SYS_LOG depends on SYS_LOG

268
drivers/adc/adc_context.h Normal file
View file

@ -0,0 +1,268 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __ADC_CONTEXT_H__
#define __ADC_CONTEXT_H__
#include <adc.h>
#include <atomic.h>
#ifdef __cplusplus
extern "C" {
#endif
struct adc_context;
/*
* Each driver should provide implementations of the following two functions:
* - adc_context_start_sampling() that will be called when a sampling (of one
* or more channels, depending on the realized sequence) is to be started
* - adc_context_update_buffer_pointer() that will be called when the sample
* buffer pointer should be prepared for writing of next sampling results,
* the "repeat_sampling" parameter indicates if the results should be written
* in the same place as before (when true) or as consecutive ones (otherwise).
*/
static void adc_context_start_sampling(struct adc_context *ctx);
static void adc_context_update_buffer_pointer(struct adc_context *ctx,
bool repeat_sampling);
/*
* If a given driver uses some dedicated hardware timer to trigger consecutive
* samplings, it should implement also the following two functions. Otherwise,
* it should define the ADC_CONTEXT_USES_KERNEL_TIMER macro to enable parts of
* this module that utilize a standard kernel timer.
*/
static void adc_context_enable_timer(struct adc_context *ctx);
static void adc_context_disable_timer(struct adc_context *ctx);
struct adc_context {
atomic_t sampling_requested;
#ifdef ADC_CONTEXT_USES_KERNEL_TIMER
struct k_timer timer;
#endif /* ADC_CONTEXT_USES_KERNEL_TIMER */
struct k_sem lock;
struct k_sem sync;
int status;
#ifdef CONFIG_ADC_ASYNC
struct k_poll_signal *signal;
bool asynchronous;
#endif /* CONFIG_ADC_ASYNC */
const struct adc_sequence *sequence;
u16_t sampling_index;
};
#ifdef ADC_CONTEXT_USES_KERNEL_TIMER
#define ADC_CONTEXT_INIT_TIMER(_data, _ctx_name) \
._ctx_name.timer = _K_TIMER_INITIALIZER(_data._ctx_name.timer, \
adc_context_on_timer_expired, \
NULL)
#endif /* ADC_CONTEXT_USES_KERNEL_TIMER */
#define ADC_CONTEXT_INIT_LOCK(_data, _ctx_name) \
._ctx_name.lock = _K_SEM_INITIALIZER(_data._ctx_name.lock, 0, 1)
#define ADC_CONTEXT_INIT_SYNC(_data, _ctx_name) \
._ctx_name.sync = _K_SEM_INITIALIZER(_data._ctx_name.sync, 0, 1)
static inline void adc_context_request_next_sampling(struct adc_context *ctx)
{
if (atomic_inc(&ctx->sampling_requested) == 0) {
adc_context_start_sampling(ctx);
} else {
/*
* If a sampling was already requested and was not finished yet,
* do not start another one from here, this will be done from
* adc_context_on_sampling_done() after the current sampling is
* complete. Instead, note this fact, and inform the user about
* it after the sequence is done.
*/
ctx->status = -EIO;
}
}
#ifdef ADC_CONTEXT_USES_KERNEL_TIMER
static inline void adc_context_enable_timer(struct adc_context *ctx)
{
u32_t interval_us = ctx->sequence->options->interval_us;
u32_t interval_ms = ceiling_fraction(interval_us, 1000UL);
k_timer_start(&ctx->timer, 0, interval_ms);
}
static inline void adc_context_disable_timer(struct adc_context *ctx)
{
k_timer_stop(&ctx->timer);
}
static void adc_context_on_timer_expired(struct k_timer *timer_id)
{
struct adc_context *ctx =
CONTAINER_OF(timer_id, struct adc_context, timer);
adc_context_request_next_sampling(ctx);
}
#endif /* ADC_CONTEXT_USES_KERNEL_TIMER */
static inline void adc_context_lock(struct adc_context *ctx,
bool asynchronous,
struct k_poll_signal *signal)
{
k_sem_take(&ctx->lock, K_FOREVER);
#ifdef CONFIG_ADC_ASYNC
ctx->asynchronous = asynchronous;
ctx->signal = signal;
#endif /* CONFIG_ADC_ASYNC */
}
static inline void adc_context_release(struct adc_context *ctx, int status)
{
#ifdef CONFIG_ADC_ASYNC
if (ctx->asynchronous && (status == 0)) {
return;
}
#endif /* CONFIG_ADC_ASYNC */
k_sem_give(&ctx->lock);
}
static inline void adc_context_unlock_unconditionally(struct adc_context *ctx)
{
if (!k_sem_count_get(&ctx->lock)) {
k_sem_give(&ctx->lock);
}
}
static inline int adc_context_wait_for_completion(struct adc_context *ctx)
{
#ifdef CONFIG_ADC_ASYNC
if (ctx->asynchronous) {
return 0;
}
#endif /* CONFIG_ADC_ASYNC */
k_sem_take(&ctx->sync, K_FOREVER);
return ctx->status;
}
static inline void adc_context_complete(struct adc_context *ctx, int status)
{
#ifdef CONFIG_ADC_ASYNC
if (ctx->asynchronous) {
if (ctx->signal) {
k_poll_signal(ctx->signal, status);
}
k_sem_give(&ctx->lock);
return;
}
#endif /* CONFIG_ADC_ASYNC */
/*
* Override the status only when an error is signaled to this function.
* Please note that adc_context_request_next_sampling() might have set
* this field.
*/
if (status != 0) {
ctx->status = status;
}
k_sem_give(&ctx->sync);
}
static inline void adc_context_start_read(struct adc_context *ctx,
const struct adc_sequence *sequence)
{
ctx->sequence = sequence;
ctx->status = 0;
if (ctx->sequence->options) {
ctx->sampling_index = 0;
if (ctx->sequence->options->interval_us != 0) {
atomic_set(&ctx->sampling_requested, 0);
adc_context_enable_timer(ctx);
return;
}
}
adc_context_start_sampling(ctx);
}
/*
* This function should be called after a sampling (of one or more channels,
* depending on the realized sequence) is done. It calls the defined callback
* function if required and takes further actions accordingly.
*/
static inline void adc_context_on_sampling_done(struct adc_context *ctx,
struct device *dev)
{
if (ctx->sequence->options) {
adc_sequence_callback callback =
ctx->sequence->options->callback;
enum adc_action action;
bool finish = false;
bool repeat = false;
if (callback) {
action = callback(dev,
ctx->sequence,
ctx->sampling_index);
} else {
action = ADC_ACTION_CONTINUE;
}
switch (action) {
case ADC_ACTION_REPEAT:
repeat = true;
break;
case ADC_ACTION_FINISH:
finish = true;
break;
default: /* ADC_ACTION_CONTINUE */
if (ctx->sampling_index <
ctx->sequence->options->extra_samplings) {
++ctx->sampling_index;
} else {
finish = true;
}
}
if (!finish) {
adc_context_update_buffer_pointer(ctx, repeat);
/*
* Immediately start the next sampling if working with
* a zero interval or if the timer expired again while
* the current sampling was in progress.
*/
if (ctx->sequence->options->interval_us == 0) {
adc_context_start_sampling(ctx);
} else if (atomic_dec(&ctx->sampling_requested) > 1) {
adc_context_start_sampling(ctx);
}
return;
}
if (ctx->sequence->options->interval_us != 0) {
adc_context_disable_timer(ctx);
}
}
adc_context_complete(ctx, 0);
}
#ifdef __cplusplus
}
#endif
#endif /* __ADC_CONTEXT_H__ */

View file

@ -1,69 +0,0 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <adc.h>
#include <syscall_handler.h>
#include <string.h>
Z_SYSCALL_HANDLER(adc_enable, dev)
{
Z_OOPS(Z_SYSCALL_DRIVER_ADC(dev, enable));
_impl_adc_enable((struct device *)dev);
return 0;
}
Z_SYSCALL_HANDLER(adc_disable, dev)
{
Z_OOPS(Z_SYSCALL_DRIVER_ADC(dev, disable));
_impl_adc_disable((struct device *)dev);
return 0;
}
Z_SYSCALL_HANDLER(adc_read, dev, seq_table_p)
{
struct adc_seq_entry *entry, *entries_copy;
struct adc_seq_table *seq_table = (struct adc_seq_table *)seq_table_p;
struct adc_seq_table seq_table_copy;
unsigned int entries_bounds;
int i = 0, ret;
Z_OOPS(Z_SYSCALL_DRIVER_ADC(dev, read));
Z_OOPS(Z_SYSCALL_MEMORY_READ(seq_table, sizeof(struct adc_seq_table)));
seq_table_copy = *seq_table;
if (Z_SYSCALL_VERIFY_MSG(
!__builtin_umul_overflow(seq_table_copy.num_entries,
sizeof(struct adc_seq_entry),
&entries_bounds),
"num_entries too large")) {
ret = -EINVAL;
goto out;
}
Z_OOPS(Z_SYSCALL_MEMORY_READ(seq_table_copy.entries, entries_bounds));
entries_copy = z_thread_malloc(entries_bounds);
if (!entries_copy) {
ret = -ENOMEM;
goto out;
}
memcpy(entries_copy, seq_table_copy.entries, entries_bounds);
seq_table_copy.entries = entries_copy;
for (entry = seq_table_copy.entries; i < seq_table_copy.num_entries;
i++, entry++) {
if (Z_SYSCALL_MEMORY_WRITE(entry->buffer,
entry->buffer_length)) {
k_free(entries_copy);
Z_OOPS(1);
}
}
ret = _impl_adc_read((struct device *)dev, &seq_table_copy);
k_free(entries_copy);
out:
return ret;
}

View file

@ -4,6 +4,7 @@
*/ */
/* /*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Intel Corporation * Copyright (c) 2015 Intel Corporation
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
@ -12,7 +13,6 @@
#ifndef __INCLUDE_ADC_H__ #ifndef __INCLUDE_ADC_H__
#define __INCLUDE_ADC_H__ #define __INCLUDE_ADC_H__
#include <zephyr/types.h>
#include <device.h> #include <device.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -26,122 +26,346 @@ extern "C" {
* @{ * @{
*/ */
/** @brief ADC channel gain factors. */
enum adc_gain {
ADC_GAIN_1_6, /**< x 1/6. */
ADC_GAIN_1_5, /**< x 1/5. */
ADC_GAIN_1_4, /**< x 1/4. */
ADC_GAIN_1_3, /**< x 1/3. */
ADC_GAIN_1_2, /**< x 1/2. */
ADC_GAIN_2_3, /**< x 2/3. */
ADC_GAIN_1, /**< x 1. */
ADC_GAIN_2, /**< x 2. */
ADC_GAIN_3, /**< x 3. */
ADC_GAIN_4, /**< x 4. */
ADC_GAIN_8, /**< x 8. */
ADC_GAIN_16, /**< x 16. */
ADC_GAIN_32, /**< x 32. */
ADC_GAIN_64, /**< x 64. */
};
/** @brief ADC references. */
enum adc_reference {
ADC_REF_VDD_1, /**< VDD. */
ADC_REF_VDD_1_2, /**< VDD/2. */
ADC_REF_VDD_1_3, /**< VDD/3. */
ADC_REF_VDD_1_4, /**< VDD/4. */
ADC_REF_INTERNAL, /**< Internal. */
ADC_REF_EXTERNAL0, /**< External, input 0. */
ADC_REF_EXTERNAL1, /**< External, input 1. */
};
/** Acquisition time is expressed in microseconds. */
#define ADC_ACQ_TIME_MICROSECONDS (1u)
/** Acquisition time is expressed in nanoseconds. */
#define ADC_ACQ_TIME_NANOSECONDS (2u)
/** Acquisition time is expressed in ADC ticks. */
#define ADC_ACQ_TIME_TICKS (3u)
/** Macro for composing the acquisition time value in given units. */
#define ADC_ACQ_TIME(unit, value) (((unit) << 14) | ((value) & BIT_MASK(14)))
/** Value indicating that the default acquisition time should be used. */
#define ADC_ACQ_TIME_DEFAULT 0
#define ADC_ACQ_TIME_UNIT(time) (((time) >> 14) & BIT_MASK(2))
#define ADC_ACQ_TIME_VALUE(time) ((time) & BIT_MASK(14))
/** /**
* @brief ADC driver Sequence entry * @brief Structure for specifying the configuration of an ADC channel.
*
* This structure defines a sequence entry used
* to define a sample from a specific channel.
*/ */
struct adc_seq_entry { struct adc_channel_cfg {
/** Clock ticks delay before sampling the ADC. */ /** Gain selection. */
s32_t sampling_delay; enum adc_gain gain;
/** Buffer pointer where the sample is written.*/ /** Reference selection. */
u8_t *buffer; enum adc_reference reference;
/** Length of the sampling buffer.*/ /**
u32_t buffer_length; * Acquisition time.
* Use the ADC_ACQ_TIME macro to compose the value for this field or
* pass ADC_ACQ_TIME_DEFAULT to use the default setting for a given
* hardware (e.g. when the hardware does not allow to configure the
* acquisition time).
* Particular drivers do not necessarily support all the possible units.
* Value range is 0-16383 for a given unit.
*/
u16_t acquisition_time;
/** Channel ID that should be sampled from the ADC */ /**
u8_t channel_id; * Channel identifier.
* This value primarily identifies the channel within the ADC API - when
* a read request is done, the corresponding bit in the "channels" field
* of the "adc_sequence" structure must be set to include this channel
* in the sampling.
* For hardware that does not allow selection of analog inputs for given
* channels, but rather have dedicated ones, this value also selects the
* physical ADC input to be used in the sampling. Otherwise, when it is
* needed to explicitly select an analog input for the channel, or two
* inputs when the channel is a differential one, the selection is done
* in "input_positive" and "input_negative" fields.
* Particular drivers indicate which one of the above two cases they
* support by selecting or not a special hidden Kconfig option named
* ADC_CONFIGURABLE_INPUTS. If this option is not selected, the macro
* CONFIG_ADC_CONFIGURABLE_INPUTS is not defined and consequently the
* mentioned two fields are not present in this structure.
* While this API allows identifiers from range 0-31, particular drivers
* may support only a limited number of channel identifiers (dependent
* on the underlying hardware capabilities or configured via a dedicated
* Kconfig option).
*/
u8_t channel_id : 5;
u8_t stride[3]; /** Channel type: single-ended or differential. */
u8_t differential : 1;
#ifdef CONFIG_ADC_CONFIGURABLE_INPUTS
/**
* Positive ADC input.
* This is a driver dependent value that identifies an ADC input to be
* associated with the channel.
*/
u8_t input_positive;
/**
* Negative ADC input (used only for differential channels).
* This is a driver dependent value that identifies an ADC input to be
* associated with the channel.
*/
u8_t input_negative;
#endif /* CONFIG_ADC_CONFIGURABLE_INPUTS */
};
/* Forward declaration of the adc_sequence structure. */
struct adc_sequence;
/**
* @brief Action to be performed after a sampling is done.
*/
enum adc_action {
/** The sequence should be continued normally. */
ADC_ACTION_CONTINUE = 0,
/**
* The sampling should be repeated. New samples or sample should be
* read from the ADC and written in the same place as the recent ones.
*/
ADC_ACTION_REPEAT,
/** The sequence should be finished immediately. */
ADC_ACTION_FINISH,
}; };
/** /**
* @brief ADC driver Sequence table * @brief Type definition of the optional callback function to be called after
* a requested sampling is done.
* *
* This structure defines a list of sequence entries * @param dev Pointer to the device structure for the driver
* used to execute a sequence of samplings. * instance.
* @param sequence Pointer to the sequence structure that triggered the
* sampling.
* @param sampling_index Index (0-65535) of the sampling done.
*
* @returns Action to be performed by the driver. See @ref adc_action.
*/ */
struct adc_seq_table { typedef enum adc_action (*adc_sequence_callback)(
/* Pointer to a sequence entry array. */ struct device *dev,
struct adc_seq_entry *entries; const struct adc_sequence *sequence,
u16_t sampling_index);
/* Number of entries in the sequence entry array. */ /**
u8_t num_entries; * @brief Structure defining additional options for an ADC sampling sequence.
u8_t stride[3]; */
struct adc_sequence_options {
/**
* Interval between consecutive samplings (in microseconds), 0 means
* sample as fast as possible, without involving any timer.
* The accuracy of this interval is dependent on the implementation of
* a given driver. The default routine that handles the intervals uses
* a kernel timer for this purpose, thus, it has the accuracy of the
* kernel's system clock. Particular drivers may use some dedicated
* hardware timers and achieve a better precision.
*/
u32_t interval_us;
/**
* Callback function to be called after each sampling is done.
* Optional - set to NULL if it is not needed.
*/
adc_sequence_callback callback;
/**
* Number of extra samplings to perform (the total number of samplings
* is 1 + extra_samplings).
*/
u16_t extra_samplings;
}; };
/**
* @brief Structure defining an ADC sampling sequence.
*/
struct adc_sequence {
/**
* Pointer to a structure defining additional options for the sequence.
* If NULL, the sequence consists of a single sampling.
*/
const struct adc_sequence_options *options;
/**
* Bit-mask indicating the channels to be included in each sampling
* of this sequence.
* All selected channels must be configured with adc_channel_setup()
* before they are used in a sequence.
*/
u32_t channels;
/**
* Pointer to a buffer where the samples are to be written. Samples
* from subsequent samplings are written sequentially in the buffer.
* The number of samples written for each sampling is determined by
* the number of channels selected in the "channels" field.
* The buffer must be of an appropriate size, taking into account
* the number of selected channels and the ADC resolution used,
* as well as the number of samplings contained in the sequence.
*/
void *buffer;
/**
* Specifies the actual size of the buffer pointed by the "buffer"
* field (in bytes). The driver must ensure that samples are not
* written beyond the limit and it must return an error if the buffer
* turns out to be not large enough to hold all the requested samples.
*/
size_t buffer_size;
/**
* ADC resolution.
* For single-ended channels the sample values are from range:
* 0 .. 2^resolution - 1,
* for differential ones:
* - 2^(resolution-1) .. 2^(resolution-1) - 1.
*/
u8_t resolution;
/**
* Oversampling setting.
* Each sample is averaged from 2^oversampling conversion results.
* This feature may be unsupported by a given ADC hardware, or in
* a specific mode (e.g. when sampling multiple channels).
*/
u8_t oversampling;
};
/**
* @brief Type definition of ADC API function for configuring a channel.
* See adc_channel_setup() for argument descriptions.
*/
typedef int (*adc_api_channel_setup)(struct device *dev,
const struct adc_channel_cfg *channel_cfg);
/**
* @brief Type definition of ADC API function for setting a read request.
* See adc_read() for argument descriptions.
*/
typedef int (*adc_api_read)(struct device *dev,
const struct adc_sequence *sequence);
#ifdef CONFIG_ADC_ASYNC
/**
* @brief Type definition of ADC API function for setting an asynchronous
* read request.
* See adc_read_async() for argument descriptions.
*/
typedef int (*adc_api_read_async)(struct device *dev,
const struct adc_sequence *sequence,
struct k_poll_signal *async);
#endif
/** /**
* @brief ADC driver API * @brief ADC driver API
* *
* This structure holds all API function pointers. * This is the mandatory API any ADC driver needs to expose.
*/ */
struct adc_driver_api { struct adc_driver_api {
/** Pointer to the enable routine. */ adc_api_channel_setup channel_setup;
void (*enable)(struct device *dev); adc_api_read read;
#ifdef CONFIG_ADC_ASYNC
/** Pointer to the disable routine. */ adc_api_read_async read_async;
void (*disable)(struct device *dev); #endif
/** Pointer to the read routine. */
int (*read)(struct device *dev, struct adc_seq_table *seq_table);
}; };
/** /**
* @brief Enable ADC hardware * @brief Configure an ADC channel.
* *
* This routine enables the ADC hardware block for data sampling for the * It is required to call this function and configure each channel before it is
* specified device. * selected for a read request.
* *
* @param dev Pointer to the device structure for the driver instance. * @param dev Pointer to the device structure for the driver instance.
* @param channel_cfg Channel configuration.
* *
* @return N/A * @retval 0 On success.
* @retval -EINVAL If a parameter with an invalid value has been provided.
*/ */
__syscall void adc_enable(struct device *dev); static inline int adc_channel_setup(struct device *dev,
const struct adc_channel_cfg *channel_cfg)
static inline void _impl_adc_enable(struct device *dev)
{ {
const struct adc_driver_api *api = dev->driver_api; const struct adc_driver_api *api = dev->driver_api;
api->enable(dev); return api->channel_setup(dev, channel_cfg);
}
/**
* @brief Disable ADC hardware
*
* This routine disables the ADC hardware block for data sampling for the
* specified device.
*
* @param dev Pointer to the device structure for the driver instance.
*
* @return N/A
*/
__syscall void adc_disable(struct device *dev);
static inline void _impl_adc_disable(struct device *dev)
{
const struct adc_driver_api *api = dev->driver_api;
api->disable(dev);
} }
/** /**
* @brief Set a read request. * @brief Set a read request.
* *
* This routine sends a read or sampling request to the ADC hardware block. * @param dev Pointer to the device structure for the driver instance.
* A sequence table describes the read request. * @param sequence Structure specifying requested sequence of samplings.
* The routine returns once the ADC completes the read sequence.
* The sample data can be retrieved from the memory buffers in
* the sequence table structure.
* *
* @param dev Pointer to the device structure for the driver instance. * @retval 0 On success.
* @param seq_table Pointer to the structure representing the sequence table. * @retval -EINVAL If a parameter with an invalid value has been provided.
* * @retval -ENOMEM If the provided buffer is to small to hold the results
* @retval 0 On success * of all requested samplings.
* @retval else Otherwise. * @retval -ENOTSUP If the requested mode of operation is not supported.
* @retval -EIO If another sampling was triggered while the previous one
* was still in progress. This may occur only when samplings
* are done with intervals, and it indicates that the selected
* interval was too small. All requested samples are written
* in the buffer, but at least some of them were taken with
* an extra delay compared to what was scheduled.
*/ */
__syscall int adc_read(struct device *dev, struct adc_seq_table *seq_table); static inline int adc_read(struct device *dev,
const struct adc_sequence *sequence)
static inline int _impl_adc_read(struct device *dev,
struct adc_seq_table *seq_table)
{ {
const struct adc_driver_api *api = dev->driver_api; const struct adc_driver_api *api = dev->driver_api;
return api->read(dev, seq_table); return api->read(dev, sequence);
} }
#ifdef CONFIG_ADC_ASYNC
/**
* @brief Set an asynchronous read request.
*
* @param dev Pointer to the device structure for the driver instance.
* @param sequence Structure specifying requested sequence of samplings.
* @param async Pointer to a valid and ready to be signaled struct
* k_poll_signal. (Note: if NULL this function will not notify
* the end of the transaction, and whether it went successfully
* or not).
*
* @returns The same
* 0 on success, negative error code otherwise. The returned values
* are the
*
*/
static inline int adc_read_async(struct device *dev,
const struct adc_sequence *sequence,
struct k_poll_signal *async)
{
const struct adc_driver_api *api = dev->driver_api;
return api->read_async(dev, sequence, async);
}
#endif /* CONFIG_ADC_ASYNC */
/** /**
* @} * @}
*/ */
@ -150,6 +374,4 @@ static inline int _impl_adc_read(struct device *dev,
} }
#endif #endif
#include <syscalls/adc.h>
#endif /* __INCLUDE_ADC_H__ */ #endif /* __INCLUDE_ADC_H__ */

View file

@ -1,4 +1,12 @@
cmake_minimum_required(VERSION 3.8.2) cmake_minimum_required(VERSION 3.8.2)
macro(set_conf_file)
if(EXISTS ${APPLICATION_SOURCE_DIR}/boards/${BOARD}.conf)
set(CONF_FILE "prj_base.conf ${APPLICATION_SOURCE_DIR}/boards/${BOARD}.conf")
else()
set(CONF_FILE "prj_base.conf")
endif()
endmacro()
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(NONE) project(NONE)

View file

@ -1,15 +0,0 @@
This is the test app which test ADC on Quark SE processor.
Default ADC mode is interrupt mode.
The case tests ADC working in different resolutions.
The analog input pin and channel number mapping
for Quark Se Devboard.
A0 Channel 10
A1 Channel 11
A2 Channel 12
A3 Channel 13
A4 Channel 14
This test uses channel 10 to sample the voltage of VCC3.3.
So connect A0 to VCC3.3 and start the APP.

View file

@ -1,2 +0,0 @@
CONFIG_ADC=y
CONFIG_ZTEST=y

View file

@ -0,0 +1,8 @@
CONFIG_ZTEST=y
CONFIG_ADC=y
CONFIG_ADC_ASYNC=y
CONFIG_ADC_0=y
CONFIG_SYS_LOG=y
CONFIG_SYS_LOG_ADC_LEVEL=1

View file

@ -1,4 +1,5 @@
/* /*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2016 Intel Corporation * Copyright (c) 2016 Intel Corporation
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
@ -11,7 +12,11 @@
* @} * @}
*/ */
extern void test_adc_sample(void); 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);
#include <zephyr.h> #include <zephyr.h>
#include <ztest.h> #include <ztest.h>
@ -19,6 +24,10 @@ extern void test_adc_sample(void);
void test_main(void) void test_main(void)
{ {
ztest_test_suite(adc_basic_test, ztest_test_suite(adc_basic_test,
ztest_unit_test(test_adc_sample)); ztest_unit_test(test_adc_sample_one_channel),
ztest_unit_test(test_adc_sample_two_channels),
ztest_unit_test(test_adc_asynchronous_call),
ztest_unit_test(test_adc_sample_with_interval),
ztest_unit_test(test_adc_repeated_samplings));
ztest_run_test_suite(adc_basic_test); ztest_run_test_suite(adc_basic_test);
} }

View file

@ -1,4 +1,5 @@
/* /*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2016 Intel Corporation * Copyright (c) 2016 Intel Corporation
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
@ -8,19 +9,7 @@
* @addtogroup test_adc_basic_operations * @addtogroup test_adc_basic_operations
* @{ * @{
* @defgroup t_adc_basic_basic_operations test_adc_sample * @defgroup t_adc_basic_basic_operations test_adc_sample
* @brief TestPurpose: verify ADC works well with different resolutions * @brief TestPurpose: verify ADC driver handles different sampling scenarios
* and sample mode
* @details
* - Test Steps
* -# Connect A0 to VCC3.3.
* -# Prepare ADC sequence table.
* -# Bind ADC device.
* -# Enable ADC device.
* -# Call adc_read() to fetch ADC sample.
* -# Dump the sample results.
* - Expected Results
* -# ADC will return the sample result for VCC3.3. Different resolutions
* will all return almost the biggest value in each sample width.
* @} * @}
*/ */
@ -28,78 +17,325 @@
#include <zephyr.h> #include <zephyr.h>
#include <ztest.h> #include <ztest.h>
#define BUFFER_SIZE 5 /* These settings need to be defined separately for particular boards. */
#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME
#define ADC_RESOLUTION 10
#define ADC_GAIN ADC_GAIN_1_2
#define ADC_REFERENCE ADC_REF_INTERNAL
#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT
#define ADC_1ST_CHANNEL_ID 0
/*#define ADC_2ND_CHANNEL_ID 2*/
#if defined(CONFIG_BOARD_FRDM_K64F) #define BUFFER_SIZE 6
#define ADC_DEV_NAME CONFIG_ADC_1_NAME static s16_t m_sample_buffer[BUFFER_SIZE];
#define ADC_CHANNEL 14
#elif defined(CONFIG_BOARD_FRDM_KL25Z) static const struct adc_channel_cfg m_1st_channel_cfg = {
#define ADC_DEV_NAME CONFIG_ADC_0_NAME .gain = ADC_GAIN,
#define ADC_CHANNEL 12 .reference = ADC_REFERENCE,
#elif defined(CONFIG_BOARD_FRDM_KW41Z) .acquisition_time = ADC_ACQUISITION_TIME,
#define ADC_DEV_NAME CONFIG_ADC_0_NAME .channel_id = ADC_1ST_CHANNEL_ID,
#define ADC_CHANNEL 3 #if defined(CONFIG_ADC_CONFIGURABLE_INPUTS)
#elif defined(CONFIG_BOARD_HEXIWEAR_K64) .input_positive = ADC_1ST_CHANNEL_INPUT,
#define ADC_DEV_NAME CONFIG_ADC_0_NAME
#define ADC_CHANNEL 16
#elif defined(CONFIG_BOARD_HEXIWEAR_KW40Z)
#define ADC_DEV_NAME CONFIG_ADC_0_NAME
#define ADC_CHANNEL 1
#else
#define ADC_DEV_NAME CONFIG_ADC_0_NAME
#define ADC_CHANNEL 10
#endif #endif
static u16_t seq_buffer[BUFFER_SIZE];
static struct adc_seq_entry entry = {
.sampling_delay = 30,
.channel_id = ADC_CHANNEL,
.buffer = (void *)seq_buffer,
.buffer_length = BUFFER_SIZE * sizeof(seq_buffer[0])
}; };
#if defined(ADC_2ND_CHANNEL_ID)
static struct adc_seq_table table = { static const struct adc_channel_cfg m_2nd_channel_cfg = {
.entries = &entry, .gain = ADC_GAIN,
.num_entries = 1, .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) */
static int test_task(void) static struct device *init_adc(void)
{
int ret;
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) */
memset(m_sample_buffer, 0, sizeof(m_sample_buffer));
return adc_dev;
}
static void check_samples(int expected_count)
{ {
int i; int i;
int ret;
struct device *adc_dev = device_get_binding(ADC_DEV_NAME);
if (!adc_dev) { TC_PRINT("Samples read: ");
TC_PRINT("Cannot get ADC device\n");
return TC_FAIL;
}
/* 1. Verify adc_enable() */
adc_enable(adc_dev);
k_sleep(500);
/* 2. Verify adc_read() */
ret = adc_read(adc_dev, &table);
if (ret != 0) {
TC_PRINT("Failed to fetch sample data from ADC controller\n");
return TC_FAIL;
}
TC_PRINT("Channel %d ADC Sample: ", ADC_CHANNEL);
for (i = 0; i < BUFFER_SIZE; i++) { for (i = 0; i < BUFFER_SIZE; i++) {
TC_PRINT("%d ", seq_buffer[i]); s16_t sample_value = m_sample_buffer[i];
TC_PRINT("0x%04x ", sample_value);
if (i < expected_count) {
zassert_not_equal(0, sample_value,
"[%u] should be non-zero", i);
} else {
zassert_equal(0, sample_value,
"[%u] should be zero", i);
}
} }
TC_PRINT("\n"); TC_PRINT("\n");
}
/* 3. Verify adc_disable() */ /*******************************************************************************
adc_disable(adc_dev); * 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,
};
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; return TC_PASS;
} }
void test_adc_sample_one_channel(void)
void test_adc_sample(void)
{ {
zassert_true(test_task() == TC_PASS, NULL); 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,
};
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)
static int test_task_asynchronous_call(void)
{
int ret;
const struct adc_sequence_options options = {
.extra_samplings = 4,
};
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_signal async_sig = K_POLL_SIGNAL_INITIALIZER(async_sig);
struct k_poll_event async_evt =
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
K_POLL_MODE_NOTIFY_ONLY,
&async_sig);
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, "async signal not received as expected");
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(
struct device *dev,
const struct adc_sequence *sequence,
u16_t sampling_index)
{
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,
.extra_samplings = 4,
};
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 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 + 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 u8_t m_samplings_done = 0;
static enum adc_action repeated_samplings_callback(
struct device *dev,
const struct adc_sequence *sequence,
u16_t sampling_index)
{
++m_samplings_done;
TC_PRINT("%s: done %d\n", __func__, m_samplings_done);
if (m_samplings_done == 1) {
#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;
} else {
#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,
};
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,
};
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);
} }

View file

@ -1,6 +0,0 @@
cmake_minimum_required(VERSION 3.8.2)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(NONE)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -1,3 +0,0 @@
CONFIG_ZTEST=y
CONFIG_ADC=y
CONFIG_SYS_LOG_ADC_LEVEL=4

View file

@ -1,127 +0,0 @@
/*
* Copyright (c) 2015 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <device.h>
#include <misc/byteorder.h>
#include <adc.h>
#include <misc/printk.h>
/* in millisecond */
#define SLEEPTIME 2000
#if defined(CONFIG_BOARD_FRDM_K64F)
#define ADC_DEVICE_NAME CONFIG_ADC_1_NAME
#define CHANNEL 14
#elif defined(CONFIG_BOARD_FRDM_KL25Z)
#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME
#define CHANNEL 12
#elif defined(CONFIG_BOARD_FRDM_KW41Z)
#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME
#define CHANNEL 3
#elif defined(CONFIG_BOARD_HEXIWEAR_K64)
#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME
#define CHANNEL 16
#elif defined(CONFIG_BOARD_HEXIWEAR_KW40Z)
#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME
#define CHANNEL 1
#else
#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME
#define CHANNEL 10
#endif
/*
* The analog input pin and channel number mapping
* for Arduino 101 board.
* A0 Channel 10
* A1 Channel 11
* A2 Channel 12
* A3 Channel 13
* A4 Channel 14
*/
#define BUFFER_SIZE 10
static u32_t seq_buffer[2][BUFFER_SIZE];
static struct adc_seq_entry sample = {
.sampling_delay = 30,
.channel_id = CHANNEL,
.buffer_length = BUFFER_SIZE * sizeof(seq_buffer[0][0])
};
static struct adc_seq_table table = {
.entries = &sample,
.num_entries = 1,
};
static void _print_sample_in_hex(const u32_t *buf, u32_t length)
{
const u32_t *top;
printk("Buffer content:\n");
for (top = buf + length; buf < top; buf++)
printk("0x%x ", *buf);
printk("\n");
}
static long _abs(long x)
{
return x < 0 ? -x : x;
}
static void test_adc(void)
{
int result = TC_FAIL;
struct device *adc;
unsigned int loops = 10;
unsigned int bufi0 = ~0, bufi;
adc = device_get_binding(ADC_DEVICE_NAME);
zassert_not_null(adc, "Cannot get adc controller");
adc_enable(adc);
while (loops--) {
bufi = loops & 0x1;
/* .buffer should be void * ... */
sample.buffer = (void *) seq_buffer[bufi];
result = adc_read(adc, &table);
zassert_equal(result, 0, "Sampling could not proceed, "
"an error occurred");
printk("loop %u: sampling done to buffer #%u\n", loops, bufi);
_print_sample_in_hex(seq_buffer[bufi], BUFFER_SIZE);
if (bufi0 != ~0) {
unsigned int cnt;
long delta;
for (cnt = 0; cnt < BUFFER_SIZE; cnt++) {
delta = _abs((long)seq_buffer[bufi][cnt]
- seq_buffer[bufi0][cnt]);
printk("loop %u delta %u = %ld\n",
loops, cnt, delta);
}
}
k_sleep(SLEEPTIME);
bufi0 = bufi;
}
adc_disable(adc);
}
void test_main(void)
{
ztest_test_suite(_adc_test,
ztest_unit_test(test_adc));
ztest_run_test_suite(_adc_test);
}

View file

@ -1,4 +0,0 @@
tests:
peripheral.adc:
depends_on: adc
tags: driver adc