diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index 787145630d3..bcc3fa35bc2 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -1,10 +1,8 @@ zephyr_library() -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_QMSI adc_qmsi.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_TI_ADC108S102 adc_ti_adc108s102.c) - -zephyr_library_sources_ifdef(CONFIG_USERSPACE adc_handlers.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_QMSI adc_qmsi.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_TI_ADC108S102 adc_ti_adc108s102.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 5a72c1dbeb2..37c4eddb58c 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -12,10 +12,22 @@ menuconfig ADC bool "ADC drivers" help - Enable ADC (Analog to Digital Converter) driver configuration + Enable ADC (Analog to Digital Converter) driver configuration. 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 int "ADC drivers log level" depends on SYS_LOG diff --git a/drivers/adc/adc_context.h b/drivers/adc/adc_context.h new file mode 100644 index 00000000000..bb775175925 --- /dev/null +++ b/drivers/adc/adc_context.h @@ -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 +#include + +#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__ */ diff --git a/drivers/adc/adc_handlers.c b/drivers/adc/adc_handlers.c deleted file mode 100644 index d88d955db8b..00000000000 --- a/drivers/adc/adc_handlers.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2017 Intel Corporation - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -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; -} diff --git a/include/adc.h b/include/adc.h index f4dee8e4011..4da12f82182 100644 --- a/include/adc.h +++ b/include/adc.h @@ -4,6 +4,7 @@ */ /* + * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2015 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 @@ -12,7 +13,6 @@ #ifndef __INCLUDE_ADC_H__ #define __INCLUDE_ADC_H__ -#include #include #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 - * - * This structure defines a sequence entry used - * to define a sample from a specific channel. + * @brief Structure for specifying the configuration of an ADC channel. */ -struct adc_seq_entry { - /** Clock ticks delay before sampling the ADC. */ - s32_t sampling_delay; +struct adc_channel_cfg { + /** Gain selection. */ + enum adc_gain gain; - /** Buffer pointer where the sample is written.*/ - u8_t *buffer; + /** Reference selection. */ + 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 - * used to execute a sequence of samplings. + * @param dev Pointer to the device structure for the driver + * 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 { - /* Pointer to a sequence entry array. */ - struct adc_seq_entry *entries; +typedef enum adc_action (*adc_sequence_callback)( + struct device *dev, + const struct adc_sequence *sequence, + u16_t sampling_index); - /* Number of entries in the sequence entry array. */ - u8_t num_entries; - u8_t stride[3]; +/** + * @brief Structure defining additional options for an ADC sampling sequence. + */ +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 * - * This structure holds all API function pointers. + * This is the mandatory API any ADC driver needs to expose. */ struct adc_driver_api { - /** Pointer to the enable routine. */ - void (*enable)(struct device *dev); - - /** Pointer to the disable routine. */ - void (*disable)(struct device *dev); - - /** Pointer to the read routine. */ - int (*read)(struct device *dev, struct adc_seq_table *seq_table); + adc_api_channel_setup channel_setup; + adc_api_read read; +#ifdef CONFIG_ADC_ASYNC + adc_api_read_async read_async; +#endif }; /** - * @brief Enable ADC hardware + * @brief Configure an ADC channel. * - * This routine enables the ADC hardware block for data sampling for the - * specified device. + * It is required to call this function and configure each channel before it is + * 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 void _impl_adc_enable(struct device *dev) +static inline int adc_channel_setup(struct device *dev, + const struct adc_channel_cfg *channel_cfg) { const struct adc_driver_api *api = dev->driver_api; - api->enable(dev); -} - -/** - * @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); + return api->channel_setup(dev, channel_cfg); } /** * @brief Set a read request. * - * This routine sends a read or sampling request to the ADC hardware block. - * A sequence table describes the read request. - * 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. + * @param sequence Structure specifying requested sequence of samplings. * - * @param dev Pointer to the device structure for the driver instance. - * @param seq_table Pointer to the structure representing the sequence table. - * - * @retval 0 On success - * @retval else Otherwise. + * @retval 0 On success. + * @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 + * of all requested samplings. + * @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 _impl_adc_read(struct device *dev, - struct adc_seq_table *seq_table) +static inline int adc_read(struct device *dev, + const struct adc_sequence *sequence) { 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 -#include - #endif /* __INCLUDE_ADC_H__ */ diff --git a/tests/drivers/adc/adc_api/CMakeLists.txt b/tests/drivers/adc/adc_api/CMakeLists.txt index 800ba5841af..d94f55d4153 100644 --- a/tests/drivers/adc/adc_api/CMakeLists.txt +++ b/tests/drivers/adc/adc_api/CMakeLists.txt @@ -1,4 +1,12 @@ 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) project(NONE) diff --git a/tests/drivers/adc/adc_api/README b/tests/drivers/adc/adc_api/README deleted file mode 100644 index 7bb74029993..00000000000 --- a/tests/drivers/adc/adc_api/README +++ /dev/null @@ -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. diff --git a/tests/drivers/adc/adc_api/prj.conf b/tests/drivers/adc/adc_api/prj.conf deleted file mode 100644 index c2869161895..00000000000 --- a/tests/drivers/adc/adc_api/prj.conf +++ /dev/null @@ -1,2 +0,0 @@ -CONFIG_ADC=y -CONFIG_ZTEST=y diff --git a/tests/drivers/adc/adc_api/prj_base.conf b/tests/drivers/adc/adc_api/prj_base.conf new file mode 100644 index 00000000000..17f655ec2ed --- /dev/null +++ b/tests/drivers/adc/adc_api/prj_base.conf @@ -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 diff --git a/tests/drivers/adc/adc_api/src/main.c b/tests/drivers/adc/adc_api/src/main.c index fca46436cc2..21f50d17295 100644 --- a/tests/drivers/adc/adc_api/src/main.c +++ b/tests/drivers/adc/adc_api/src/main.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2016 Intel Corporation * * 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 #include @@ -19,6 +24,10 @@ extern void test_adc_sample(void); void test_main(void) { 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); } diff --git a/tests/drivers/adc/adc_api/src/test_adc.c b/tests/drivers/adc/adc_api/src/test_adc.c index 84b75f07f1a..7d8f79bacb3 100644 --- a/tests/drivers/adc/adc_api/src/test_adc.c +++ b/tests/drivers/adc/adc_api/src/test_adc.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 @@ -8,19 +9,7 @@ * @addtogroup test_adc_basic_operations * @{ * @defgroup t_adc_basic_basic_operations test_adc_sample - * @brief TestPurpose: verify ADC works well with different resolutions - * 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. + * @brief TestPurpose: verify ADC driver handles different sampling scenarios * @} */ @@ -28,78 +17,325 @@ #include #include -#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 ADC_DEV_NAME CONFIG_ADC_1_NAME -#define ADC_CHANNEL 14 -#elif defined(CONFIG_BOARD_FRDM_KL25Z) -#define ADC_DEV_NAME CONFIG_ADC_0_NAME -#define ADC_CHANNEL 12 -#elif defined(CONFIG_BOARD_FRDM_KW41Z) -#define ADC_DEV_NAME CONFIG_ADC_0_NAME -#define ADC_CHANNEL 3 -#elif defined(CONFIG_BOARD_HEXIWEAR_K64) -#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 +#define BUFFER_SIZE 6 +static s16_t m_sample_buffer[BUFFER_SIZE]; + +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 - -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]) }; - -static struct adc_seq_table table = { - .entries = &entry, - .num_entries = 1, +#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) */ -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 ret; - struct device *adc_dev = device_get_binding(ADC_DEV_NAME); - if (!adc_dev) { - 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); + TC_PRINT("Samples read: "); 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"); +} - /* 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; } - -void test_adc_sample(void) +void test_adc_sample_one_channel(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); } diff --git a/tests/drivers/adc/adc_simple/CMakeLists.txt b/tests/drivers/adc/adc_simple/CMakeLists.txt deleted file mode 100644 index 800ba5841af..00000000000 --- a/tests/drivers/adc/adc_simple/CMakeLists.txt +++ /dev/null @@ -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}) diff --git a/tests/drivers/adc/adc_simple/prj.conf b/tests/drivers/adc/adc_simple/prj.conf deleted file mode 100644 index 44074b400a9..00000000000 --- a/tests/drivers/adc/adc_simple/prj.conf +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_ZTEST=y -CONFIG_ADC=y -CONFIG_SYS_LOG_ADC_LEVEL=4 diff --git a/tests/drivers/adc/adc_simple/src/main.c b/tests/drivers/adc/adc_simple/src/main.c deleted file mode 100644 index 7a50d5893ed..00000000000 --- a/tests/drivers/adc/adc_simple/src/main.c +++ /dev/null @@ -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 - -#include -#include -#include -#include - -/* 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); -} - diff --git a/tests/drivers/adc/adc_simple/testcase.yaml b/tests/drivers/adc/adc_simple/testcase.yaml deleted file mode 100644 index c9325da5ee3..00000000000 --- a/tests/drivers/adc/adc_simple/testcase.yaml +++ /dev/null @@ -1,4 +0,0 @@ -tests: - peripheral.adc: - depends_on: adc - tags: driver adc