--- bsps/include/bsp/adc.h | 407 +++++++++++++++++++++++++++++++ bsps/shared/dev/adc/adc.c | 249 +++++++++++++++++++ spec/build/bsps/bspopts.yml | 2 + spec/build/bsps/obj.yml | 1 + spec/build/bsps/optmaxnumadc.yml | 16 ++ 5 files changed, 675 insertions(+) create mode 100644 bsps/include/bsp/adc.h create mode 100644 bsps/shared/dev/adc/adc.c create mode 100644 spec/build/bsps/optmaxnumadc.yml
diff --git a/bsps/include/bsp/adc.h b/bsps/include/bsp/adc.h new file mode 100644 index 0000000000..97e596317b --- /dev/null +++ b/bsps/include/bsp/adc.h @@ -0,0 +1,407 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup adc + * + * RTEMS ADC API. + * + * This API is created to improve portability for ADC-related operations. + * + * Drivers need to register each ADC controller to the manager using + * @ref rtems_adc_register(). Then, users can get an ADC object of a controller + * by specifying the index of that controller (starting from 0, the first + * registered ADC). + * + * Sometimes, ADCs are integrated into a GPIO pin (such as on-chip ADC). If so, + * additional effort to set the pin mode to analog might be required. + * + * The general process to use ADC API: + * - Get an ADC object using @ref rtems_adc_get() + * - Configure ADC object with rtems_adc_set_* functions + * - Call @ref rtems_adc_init() + * - The ADC object should be ready by now + */ + +/* + * Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBBSP_BSP_ADC_H +#define LIBBSP_BSP_ADC_H + +#include <bsp.h> +#include <rtems.h> +#include <bsp/gpio2.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern volatile int *adc_dummy; +/** + * @brief Macro to link BSP source file. + * + * There might be a case that a BSP GPIO source file is not linked because + * of no reference. Use this macro to create a dummy variable to link with + * GPIO API. This macro should be placed outside of any function. + */ +#define RTEMS_ADC_LINK() \ + static int ___dummy___ = 0; \ + volatile int *adc_dummy = &___dummy___; + +/** + * Configure the maximum number of ADC controllers used in + * a application. + * + * The macro CONFIGURE_ADC_MAXIMUM_CONTROLLERS is a build option. + * If it is not defined, it will default to BSP_ADC_NUM_CONTROLLERS. + * If BSP's number of controllers is not defined, it will default + * to 0. + */ +#ifndef CONFIGURE_ADC_MAXIMUM_CONTROLLERS + +#ifndef BSP_ADC_NUM_CONTROLLERS +#define CONFIGURE_ADC_MAXIMUM_CONTROLLERS 0 +#else +#define CONFIGURE_ADC_MAXIMUM_CONTROLLERS BSP_ADC_NUM_CONTROLLERS +#endif /* BSP_ADC_NUM_CONTROLLERS */ + +#endif /* CONFIGURE_ADC_MAXIMUM_CONTROLLERS */ + +typedef enum { + RTEMS_ADC_NOT_STARTED = 0, + RTEMS_ADC_NOT_READY, + RTEMS_ADC_READY +} rtems_adc_status; + +/** + * @brief Data alignment. + */ +typedef enum { + RTEMS_ADC_ALIGN_LEFT, + RTEMS_ADC_ALIGN_RIGHT +} rtems_adc_align; + +/** + * @brief Enumeration of reference voltages. + */ +typedef enum { + RTEMS_ADC_REF_DEFAULT = 0, + RTEMS_ADC_REF_INTERNAL, + RTEMS_ADC_REF_EXTERNAL, + RTEMS_ADC_REF_BSP_SPECIFIC = 100 +} rtems_adc_ref; + +#define RTEMS_ADC_NO_TIMEOUT 0xFFFFFFFFU + +typedef void (*rtems_adc_isr)(void *); +typedef double (*rtems_adc_tf) (void *params, uint32_t raw_value); +typedef struct rtems_adc_handlers rtems_adc_handlers; +typedef struct rtems_adc_config rtems_adc_config; +typedef struct rtems_adc rtems_adc; + +/** + * @brief Driver-specific handlers. + * Each driver need to create an object of this structure to supply to + * the API. + */ +struct rtems_adc_handlers { + void (*init) (rtems_adc *); + /** + * @brief Pointer to a function that reads raw ADC value. + * This function is blocking and has a timeout parameter. + */ + rtems_status_code (*read_raw) (rtems_adc *, uint32_t *, uint32_t); + /** + * @brief Pointer to a function that starts ADC conversion + * in non-blocking style. + */ + rtems_status_code (*start_read_raw_nb) (rtems_adc *); + /** + * @brief Pointer to a function that gets a raw ADC value when + * available after a start_read_raw_nb() call. + * If data is not available, the function should return a status + * to indicate that. + */ + rtems_adc_status (*read_raw_nb) (rtems_adc *, uint32_t *); + /** + * @brief Sets the channel of an ADC. + * + */ + rtems_status_code (*set_channel) (rtems_adc *, uint32_t); + /** + * @brief Pointer to a function that sets the resolution of an + * ADC controller associated with a pin. + * + * @note If a controller contains multiple pins, the resolution + * setting may affect all of them. + */ + rtems_status_code (*set_resolution) (rtems_adc *, unsigned int); + /** + * @brief Pointer to a function that sets the alignment of an + * ADC controller associated with a pin. + * + * @note If a controller contains multiple pins, the alignment + * setting may affect all of them. + */ + rtems_status_code (*set_alignment) (rtems_adc *, rtems_adc_align); + /** + * @brief This member is the pointer to a handler for configuring + * interrupt of a pin. + * + * This handler should register ISR and its arguments. + * + * @note Interrupt may occur when ADC conversion is completed. + * @note Enabling interrupt should be done in enable_interrupt() + * handler. + */ + rtems_status_code (*configure_interrupt) (rtems_adc *, rtems_adc_isr, void *); + /** + * @brief This member is the pointer to a handler for removing + * interrupt settings of a pin. + */ + rtems_status_code (*remove_interrupt) (rtems_adc *); + /** + * @brief This member is the pointer to a handler for enabling + * interrupt functionality of a pin. + */ + rtems_status_code (*enable_interrupt) (rtems_adc *); + /** + * @brief This member is the pointer to a handler for disabling + * interrupt of a pin. + */ + rtems_status_code (*disable_interrupt) (rtems_adc *); +}; + +/** + * @brief ADC configuration structure. + */ +struct rtems_adc_config { + uint32_t channel; + rtems_adc_align alignment; + uint32_t resolution; +}; + +/** + * @brief ADC core object. + */ +struct rtems_adc { + /** + * @brief The index of an ADC controller. + */ + const uint32_t id; + /** + * @brief Driver-specific handlers. + */ + const rtems_adc_handlers *const handlers; + /** + * @brief ADC channel configuration. + */ + rtems_adc_config config; + /** + * @brief A transfer function that will be assigned to this pin. + * If no transfer function assigned, it should remain NULL. + */ + rtems_adc_tf tf; + void *tf_params; +}; + +/** + * @brief Registers an ADC to the ADC manager. + */ +extern rtems_status_code rtems_adc_register( + rtems_status_code (*get_adc) (uint32_t, rtems_adc **), + rtems_status_code (*destroy_adc) (rtems_adc *) +); + +/** + * @brief Get the ADC object by ADC controller index. + */ +extern rtems_status_code rtems_adc_get( + uint32_t id, + rtems_adc **out +); + +/** + * @brief Frees an ADC object. + */ +extern rtems_status_code rtems_adc_destroy( + rtems_adc *adc +); + +/** + * @brief Perform initialization for an ADC controller/channel. + */ +extern void rtems_adc_init( + rtems_adc *base +); + +/** + * @brief Read raw ADC value with infinite timeout. + * + * @param base[in] Pointer to an ADC object. + * @param result[out] Pointer to result variable. + * + * @retval + */ +extern rtems_status_code rtems_adc_read_raw( + rtems_adc *base, + uint32_t *result +); + +/** + * @brief Read raw ADC value with specified timeout. + * + * @param base[in] Pointer to an ADC object. + * @param result[out] Pointer to result variable. + * @param timeout + * + * @retval + */ +extern rtems_status_code rtems_adc_read_raw_timeout( + rtems_adc *base, + uint32_t *result, + uint32_t timeout +); + +/** + * @brief Starts a non-blocking ADC conversion. + * + * This function must be called before + * rtems_adc_read_raw_nb() or rtems_adc_read_nb() + * + * @param base[in] Pointer to an ADC object. + * + * @retval + */ +extern rtems_status_code rtems_adc_start_read_nb( + rtems_adc *base +); + +/** + * @brief Reads raw ADC value non-blocking. + * User can query the status of the conversion by the returned status code. + * + * @param base[in] Pointer to an ADC object. + * @param result[out] Pointer to result variable. + * + * @retval @see rtems_adc_status + */ +extern rtems_adc_status rtems_adc_read_raw_nb( + rtems_adc *base, + uint32_t *result +); + +/** + * @brief Assigns a transfer function with parameters + * to a pin. + */ +extern rtems_status_code rtems_adc_assign_tf( + rtems_adc *base, + rtems_adc_tf tf, + void *params +); + +/** + * @brief Removes the assigned transfer function from + * a pin. + */ +extern rtems_status_code rtems_adc_remove_tf( + rtems_adc *base +); + +/** + * @brief Reads an ADC value with infinite timeout. + * + * If no transfer function assigned, this will + * return the raw value via result pointer. Else, + * it returns the calculated value using transfer + * function. + * + * @param base[in] Pointer to an ADC object. + * @param result[out] Pointer to result variable. + * + * @retval + */ +extern rtems_status_code rtems_adc_read( + rtems_adc *base, + double *result +); + +/** + * @brief Reads an ADC value with specified timeout. + * + * If no transfer function assigned, this will + * return the raw value via result pointer. Else, + * it returns the calculated value using transfer + * function. + * + * @param base[in] Pointer to an ADC object. + * @param result[out] Pointer to result variable. + * @param timeout + * + * @retval + */ +extern rtems_status_code rtems_adc_read_timeout( + rtems_adc *base, + double *result, + uint32_t timeout +); + +extern rtems_adc_status rtems_adc_read_nb( + rtems_adc *base, + double *result +); + +/** + * @brief Set the channel of an ADC object. + */ +extern rtems_status_code rtems_adc_set_channel( + rtems_adc *base, + uint32_t channel +); + +/** + * @brief Set the resolution of an ADC object. + */ +extern rtems_status_code rtems_adc_set_resolution( + rtems_adc *base, + unsigned int bits +); + +/** + * @brief Set the alignment of an ADC object. + */ +extern rtems_status_code rtems_adc_set_alignment( + rtems_adc *base, + rtems_adc_align align +); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBBSP_BSP_ADC_H */ diff --git a/bsps/shared/dev/adc/adc.c b/bsps/shared/dev/adc/adc.c new file mode 100644 index 0000000000..1b43c7a0b6 --- /dev/null +++ b/bsps/shared/dev/adc/adc.c @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup adc + */ + +/* + * Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <bsp/adc.h> +#include <stdbool.h> +#include <rtems/sysinit.h> + +/** + * @brief Private struct to store get() and destroy() handlers of drivers. + */ +struct rtems_adc_ctrl { + rtems_status_code (*get) (uint32_t, rtems_adc **); + rtems_status_code (*destroy) (rtems_adc *); +}; + +static struct rtems_adc_ctrl get_destroy_table[CONFIGURE_ADC_MAXIMUM_CONTROLLERS]; + +static uint32_t num_ctrl = 0; + +rtems_status_code rtems_adc_register( + rtems_status_code (*get_adc) (uint32_t, rtems_adc **), + rtems_status_code (*destroy_adc) (rtems_adc *) +) +{ + rtems_interrupt_level level; + + if (num_ctrl == CONFIGURE_GPIO_MAXIMUM_CONTROLLERS) + return RTEMS_TOO_MANY; + + rtems_interrupt_disable(level); + + get_destroy_table[num_ctrl] = (struct rtems_adc_ctrl) { + get_adc, destroy_adc + }; + ++num_ctrl; + + rtems_interrupt_enable(level); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_adc_get( + uint32_t id, + rtems_adc **out +) +{ + if (id >= num_ctrl) + return RTEMS_UNSATISFIED; + rtems_status_code sc = (*get_destroy_table[id].get)(id, out); + (*out)->tf = (*out)->tf_params = NULL; + return sc; +} + +rtems_status_code rtems_adc_destroy( + rtems_adc *adc +) +{ + return (*get_destroy_table[adc->id].destroy)(adc); +} + +void rtems_adc_init( + rtems_adc *base +) +{ + base->handlers->init(base); +} + +rtems_status_code rtems_adc_read_raw( + rtems_adc *base, + uint32_t *result +) +{ + return base->handlers->read_raw(base, result, RTEMS_ADC_NO_TIMEOUT); +} + +rtems_status_code rtems_adc_read_raw_timeout( + rtems_adc *base, + uint32_t *result, + uint32_t timeout +) +{ + return base->handlers->read_raw(base, result, timeout); +} + +rtems_adc_status rtems_adc_read_raw_nb( + rtems_adc *base, + uint32_t *result +) +{ + + return base->handlers->read_raw_nb(base, result); +} + +rtems_status_code rtems_adc_assign_tf( + rtems_adc *base, + rtems_adc_tf tf, + void *params +) +{ + base->tf = tf; + base->tf_params = params; + return RTEMS_SUCCESSFUL; +} +rtems_status_code rtems_adc_remove_tf( + rtems_adc *base +) +{ + + base->tf = NULL; + base->tf_params = NULL; + return RTEMS_SUCCESSFUL; +} +rtems_status_code rtems_adc_read( + rtems_adc *base, + double *result +) +{ + + uint32_t raw; + rtems_status_code sc = rtems_adc_read_raw(base, &raw); + if (sc == RTEMS_SUCCESSFUL) { + if (base->tf == NULL) + *result = (double) raw; + else + *result = base->tf(base->tf_params, raw); + } + return sc; +} +rtems_status_code rtems_adc_read_timeout( + rtems_adc *base, + double *result, + uint32_t timeout +) +{ + + uint32_t raw; + rtems_status_code sc = rtems_adc_read_raw_timeout(base, &raw, timeout); + if (sc == RTEMS_SUCCESSFUL) { + if (base->tf == NULL) + *result = (double) raw; + else + *result = base->tf(base->tf_params, raw); + } + return sc; +} + +rtems_status_code rtems_adc_start_read_nb( + rtems_adc *base +) +{ + + return base->handlers->start_read_raw_nb(base); +} +rtems_adc_status rtems_adc_read_nb( + rtems_adc *base, + double *result +) +{ + + uint32_t raw; + rtems_adc_status sc = rtems_adc_read_raw_nb(base, &raw); + if (sc == RTEMS_ADC_READY) { + if (base->tf == NULL) + *result = (double) raw; + else + *result = base->tf(base->tf_params, raw); + } + return sc; +} + +rtems_status_code rtems_adc_set_channel( + rtems_adc *base, + uint32_t channel +) +{ + rtems_status_code sc = base->handlers->set_channel(base, channel); + if (sc == RTEMS_SUCCESSFUL) + base->config.channel = channel; + return sc; +} + +rtems_status_code rtems_adc_set_resolution( + rtems_adc *base, + unsigned int bits +) +{ + + rtems_status_code sc = base->handlers->set_resolution(base, bits); + if (sc == RTEMS_SUCCESSFUL) + base->config.resolution = bits; + return sc; +} +rtems_status_code rtems_adc_set_alignment( + rtems_adc *base, + rtems_adc_align align +) +{ + + rtems_status_code sc = base->handlers->set_alignment(base, align); + if (sc == RTEMS_SUCCESSFUL) + base->config.alignment = align; + return sc; +} + +/** + * Force linking driver ADC source + */ +static void rtems_adc_start( + void +) +{ + *adc_dummy = 1; +} +RTEMS_SYSINIT_ITEM( + rtems_adc_start, + RTEMS_SYSINIT_BSP_START, + RTEMS_SYSINIT_ORDER_LAST +); + diff --git a/spec/build/bsps/bspopts.yml b/spec/build/bsps/bspopts.yml index d0e31e8ae1..b94991ad39 100644 --- a/spec/build/bsps/bspopts.yml +++ b/spec/build/bsps/bspopts.yml @@ -9,6 +9,8 @@ install-path: ${BSP_INCLUDEDIR} links: - role: build-dependency uid: optmaxnumgpio +- role: build-dependency + uid: optmaxnumadc - role: build-dependency uid: optbspoptflags - role: build-dependency diff --git a/spec/build/bsps/obj.yml b/spec/build/bsps/obj.yml index 4de7bf6d16..a476893c81 100644 --- a/spec/build/bsps/obj.yml +++ b/spec/build/bsps/obj.yml @@ -17,6 +17,7 @@ install: - bsps/include/bsp/fatal.h - bsps/include/bsp/fdt.h - bsps/include/bsp/gpio2.h + - bsps/include/bsp/adc.h - bsps/include/bsp/irq-default.h - bsps/include/bsp/irq-generic.h - bsps/include/bsp/irq-info.h diff --git a/spec/build/bsps/optmaxnumadc.yml b/spec/build/bsps/optmaxnumadc.yml new file mode 100644 index 0000000000..9cea688e52 --- /dev/null +++ b/spec/build/bsps/optmaxnumadc.yml @@ -0,0 +1,16 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +actions: +- get-integer: null +- define-condition: null +build-type: option +copyrights: +- Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) +default: 0 +default-by-variant: [] +description: | + The number of GPIO controllers used for this application. +enabled-by: true +format: '{}' +links: [] +name: CONFIGURE_ADC_MAXIMUM_CONTROLLERS +type: build -- 2.37.1 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel