[PATCH v6 09/10] bsps: Add ADC API

Duc Doan dtbpkmte at gmail.com
Sun Aug 7 10:58:21 UTC 2022


---
 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



More information about the devel mailing list