5. Code Overview

This section provides the code blocks needed to successfully execute this tutorial.

5.1. Header Files

In main.c file, add the following header files:

#include "hw_pdc.h"
#include "ad_gpadc.h"
#include "hw_wkup.h"
#include "hw_sys.h"
#include "peripheral_setup.h"
#include "platform_devices.h"

5.2. System Init Code

In main.c file, replace the system_init() routine with the following code:

/* Task priorities */
#define  mainGPADC_TASK_PRIORITY    ( OS_TASK_PRIORITY_NORMAL )

/* Enable/disable asynchronous SPI operations */
#define POT_ASYNC_EN                (1)


/* Retained symbols */
__RETAINED static OS_EVENT signal_pot;
__RETAINED static OS_EVENT signal_pot_async;

/* GPADC Task handle */
__RETAINED static OS_TASK prvGPADCTask_h;

uint32_t pdc_wkup_combo_id  __attribute__((unused));

/*
 * Task functions
 */
static void prvGPADCTask_POT(void *pvParameters);

static void system_init(void *pvParameters)
{
        OS_BASE_TYPE status;

        REG_SETF(GPREG, DEBUG_REG, SYS_CPU_FREEZE_EN, 0);

#if defined CONFIG_RETARGET
        extern void retarget_init(void);
#endif /* CONFIG_RETARGET */

        /*
         * Prepare clocks. Note: cm_cpu_clk_set() and cm_sys_clk_set() can be called only
         * from a task since they will suspend the task until the XTAL32M has settled and,
         * maybe, the PLL is locked.
         */
        cm_sys_clk_init(sysclk_XTAL32M);
        cm_apb_set_clock_divider(apb_div1);
        cm_ahb_set_clock_divider(ahb_div1);
        cm_lp_clk_init();

        /* Prepare the hardware to run this demo */
        prvSetupHardware();

#if defined CONFIG_RETARGET
        retarget_init();
#endif /* CONFIG_RETARGET */


        OS_EVENT_CREATE(signal_pot);
        OS_EVENT_CREATE(signal_pot_async);

        /*
         * Upon a wakeup cycle, wait for the XTAL32M crystal to settle.
         * BLE, USB and UART blocks require the XTAL32M to be up and
         * running to work properly.
         */
        pm_set_wakeup_mode(true);


        /* Set the desired sleep mode. */
        pm_sleep_mode_set(pm_mode_extended_sleep);

        /*
         * Set the desired wakeup mode.
         *
         * \warning When set is Ultra-Fast wakeup mode, sleep voltage should be 0.9V
         *          and not less than that.
         *
         **/
        pm_set_sys_wakeup_mode(pm_sys_wakeup_mode_fast);

        /* GPADC task  */
        status = OS_TASK_CREATE("GPADC",     /* The text name assigned to the task, for
                                             debug only; not used by the kernel. */
                        prvGPADCTask_POT,    /* The function that implements the task. */
                        NULL,               /* The parameter passed to the task. */
                        1024 * OS_STACK_WORD_SIZE,  /* Stack size allocated for the task
                                                    in bytes. */
                        mainGPADC_TASK_PRIORITY, /* The priority assigned to the task. */
                        prvGPADCTask_h );       /* The task handle. */
        OS_ASSERT(status == OS_TASK_CREATE_SUCCESS);

        /* The work of the SysInit task is done */
        OS_TASK_DELETE(xHandle);
}

5.3. Task Code for GPADC Measurements

In main.c file and after system_init(), add the following code used for performing GPADC measurements from an analog pin.

/*
 * Function for converting a raw ADC value to mV
 *
 * \param [in]  src     The GPADC instance
 * \param [in]  value   The raw ADC value
 *
 * \return The converted raw ADC value in millivolt
 *
 */
int convert_raw_to_mv(gpadc_device src, int value)
{
        ad_gpadc_controller_conf_t *cfg = (ad_gpadc_controller_conf_t *)src;

        const uint16 adc_src_max =  ad_gpadc_get_source_max(cfg->drv);
        uint32_t mv_src_max = (cfg->drv->input_attenuator ==
                                     HW_GPADC_INPUT_VOLTAGE_UP_TO_1V2) ? 1200 : 3600;
        int ret = 0;

        switch (cfg->drv->input_mode) {
        case HW_GPADC_INPUT_MODE_SINGLE_ENDED:
                if (cfg->drv->input == HW_GPADC_INPUT_SE_VBAT) {
                        mv_src_max = 5000;
                }
                ret =  (mv_src_max * value) / adc_src_max;
                break;
        case HW_GPADC_INPUT_MODE_DIFFERENTIAL:
                ret = ((int)mv_src_max * (value - (adc_src_max >> 1))) /
                                                         (adc_src_max >> 1);
                break;
        default:
                /* Invalid input mode */
                OS_ASSERT(0);
        }

        return ret;
}


#if POT_ASYNC_EN == 1
/*
 * Callback function for GPADC asynchronous operations:
 *
 * \param [in]  user_data  Data that can be passed and used within the function
 * \param [out] value      The raw ADC value
 *
 */
void pot_gpadc_cb(void *user_data, int value)
{
        /* User can pass in and process data from here */
        int *pData = (int *)user_data;

        /* Read the raw ADC value */
        *pData = value;

        /* Signal the [prvGPADCTask_POT] task that time for resuming has elapsed. */
        OS_EVENT_SIGNAL_FROM_ISR(signal_pot_async);
}
#endif


/* Perform a GPADC read operation */
static void pot_gpadc_reader(gpadc_device dev)
{
        int POT_error_code, POT_raw_val, POT_mv_value;


        /* Open the GPADC device */
        ad_gpadc_handle_t pot_dev = ad_gpadc_open((ad_gpadc_controller_conf_t *)dev);

#if POT_ASYNC_EN == 0
       /*
        * Perform a synchronous GPADC read operation, that is, the task
        * is blocking waiting for the operation to finish.
        */
        POT_error_code = ad_gpadc_read(pot_dev, (uint16_t *)&POT_raw_val);

#else
       /*
        * Perform an asynchronous GPADC read operation, that is, the task does not
        * block waiting for the transaction to finish. Upon operation completion
        * callback function is triggered indicating the completion of the GPADC operation
        */
        POT_error_code = ad_gpadc_read_async(pot_dev, pot_gpadc_cb, (void *)&POT_raw_val);

        /*
         * In the meantime and while GPADC operations are performed in the background,
         * application task can proceed to other operations/calculation. It is essential
         * that the new operations do not involve GPADC operations on the already
         * occupied block!!!
         */

         /*
          * Make sure that the current GPADC operation has finished,
          * blocking here forever.
          */
         OS_EVENT_WAIT(signal_pot_async, OS_EVENT_FOREVER);
#endif

         /* Convert the raw ADC value to mV */
         POT_mv_value = convert_raw_to_mv(dev, POT_raw_val);

         /* Close the GPADC device */
         ad_gpadc_close(pot_dev, true);


         /* Print on the serial console the status of the GPADC operation */
         if (POT_error_code == 0) {
                 printf("\n\rPOT value (raw): %d\n\rPOT value (analog): %d mV\n\r",
                                                        POT_raw_val,POT_mv_value);
         } else {
                 printf("\n\rUnsuccessful GPADC write operation with error code: %d\n\r",
                                                                         POT_error_code);
         }
         fflush(stdout);
}

/**
 * @brief GPADC task
 */
static void prvGPADCTask_POT(void *pvParameters)
{

        printf("\n\r***GPADC Demonstration Example***\n\r\n");

        /*
         * GPADC adapter initialization should be done once at the beginning.
         * Alternatively, this function could be called during system
         * initialization in system_init().
         */
        ad_gpadc_init();


        for (;;) {
                /*
                 * Suspend task execution - As soon as WKUP callback function
                 * is triggered, the task resumes its execution.
                 */
                OS_EVENT_WAIT(signal_pot, OS_EVENT_FOREVER);

                /* Perform a GPADC read operation */
                pot_gpadc_reader(POT_DEVICE);
        }
}

5.4. Wake-Up Timer Code

In main.c file and after system_init() routine, add the following code used for handling external events via the wake-up controller:

/* WKUP KEY interrupt handler */
static void wkup_cb(void)
{

        /* Clear the WKUP interrupt flag!!! */
        hw_wkup_reset_interrupt();


        /*
         * Avoid using printf() within ISR context!!! It may crash your code.
         * Instead, use notifications to notify a task to perform an action!!
         */

        /*
         * Notify [prvGPADCTask_POT] task that time for performing GPADC operations
         * has elapsed.
         */
        OS_EVENT_SIGNAL_FROM_ISR(signal_pot);

}


/* Initialize the WKUP controller */
static void wkup_init(void)
{

        /* Initialize the WKUP controller */
        hw_wkup_init(NULL);

        /*
         * Set debounce time expressed in ms. Maximum allowable value is 63 ms.
         * A value set to 0 disables the debounce functionality.
         */
        hw_wkup_set_debounce_time(10);

        /*
         * Enable interrupts produced by the KEY block of the wakeup controller (debounce
         * circuitry) and register a callback function to hit following a KEY event.
         */
        hw_wkup_register_key_interrupt(wkup_cb, 1);


        /*
         * Set the polarity (rising/falling edge) that triggers the WKUP controller.
         *
         * \note The polarity is applied both to KEY and GPIO blocks of the controller
         *
         */
        hw_wkup_configure_pin(KEY1_PORT, KEY1_PIN, 1, HW_WKUP_PIN_STATE_LOW);


        /* Enable interrupts of WKUP controller */
        hw_wkup_enable_irq();
}

5.5. Hardware Initialization

In main.c file, replace the prvSetupHardware() routine with the following code used for configuring the pins utilized by the wake-up contoller.

/**
 * @brief Hardware Initialization
 */
static void prvSetupHardware(void)
{
        /*
         * The IRQ produced by the KEY sub block of the wakeup controller
         * (debounced IO IRQ) is multiplexed with other trigger sources
         * (VBUS IRQ, SYS2CMAC IRQ, JTAG present) in a single PDC peripheral
         * trigger ID (HW_PDC_PERIPH_TRIG_ID_COMBO).
         */
#if !defined(CONFIG_USE_BLE) && (!dg_configENABLE_DEBUGGER) && (!dg_configUSE_SYS_CHARGER)

        pdc_wkup_combo_id = hw_pdc_add_entry(HW_PDC_LUT_ENTRY_VAL(
                                                         HW_PDC_TRIG_SELECT_PERIPHERAL,
                                                         HW_PDC_PERIPH_TRIG_ID_COMBO,
                                                         HW_PDC_MASTER_CM33, 0));
        OS_ASSERT(pdc_wkup_combo_id != HW_PDC_INVALID_LUT_INDEX);

        /*  */
        hw_pdc_set_pending(pdc_wkup_combo_id);
        hw_pdc_acknowledge(pdc_wkup_combo_id);
#endif

        wkup_init();

        /* Init hardware */
        pm_system_init(periph_init);


        /* Enable the COM power domain before handling any GPIO pin */
        hw_sys_pd_com_enable();

        ad_gpadc_io_config(((ad_gpadc_controller_conf_t *)POT_DEVICE)->id,
                ((ad_gpadc_controller_conf_t *)POT_DEVICE)->io, AD_IO_CONF_OFF);

        /* Configure the KEY1 push button on Pro DevKit */
        HW_GPIO_SET_PIN_FUNCTION(KEY1);
        HW_GPIO_PAD_LATCH_ENABLE(KEY1);

        /* Lock the mode of the target GPIO pin */
        HW_GPIO_PAD_LATCH_DISABLE(KEY1);

        /* Disable the COM power domain after handling the GPIO pins */
        hw_sys_pd_com_disable();
}

5.6. Macro Definitions

In config/custom_config_xxx.h file, add the following macro definitions to enable the GPADC related API:

/*******************************************************
 * Peripheral specific config
 */
#define dg_configUSE_HW_GPADC                   (1)
#define dg_configGPADC_ADAPTER                  (1)

In platform_devices.c file, add IO bus as well as driver configurations for the GPADC device(s) used. These settings will be applied when an application task attempts to interact with the target analog device.

#include <ad_gpadc.h>

#include "peripheral_setup.h"
#include "platform_devices.h"

/*
 * PLATFORM PERIPHERALS GPIO CONFIGURATION
 *****************************************************************************************
 */

#if dg_configGPADC_ADAPTER || dg_configUSE_HW_GPADC


/* GPADC IO */
const ad_gpadc_io_conf_t bus_GPADC = {
        .input0 = {
                .port = HW_GPIO_PORT_0,
                .pin  = HW_GPIO_PIN_25,
                .on   = {HW_GPIO_MODE_INPUT, HW_GPIO_FUNC_ADC,  true},
                .off  = {HW_GPIO_MODE_INPUT, HW_GPIO_FUNC_GPIO, true}
        },
        .input1 = {
                .port = HW_GPIO_PORT_NONE,
                .pin  = HW_GPIO_PIN_NONE,
                .on   = {HW_GPIO_MODE_INPUT, HW_GPIO_FUNC_GPIO, true},
                .off  = {HW_GPIO_MODE_INPUT, HW_GPIO_FUNC_GPIO, true}
        },
        .voltage_level = HW_GPIO_POWER_V33
};

/* GPADC driver configurations */
const ad_gpadc_driver_conf_t drv_GPADC = {
        .clock                  = HW_GPADC_CLOCK_INTERNAL, /* high-speed clock */
        .input_mode             = HW_GPADC_INPUT_MODE_SINGLE_ENDED,
        /* Analog pins are mapped on specific pins on DA1469x */
        .input                  = HW_GPADC_INPUT_SE_P0_25,
        .temp_sensor            = HW_GPADC_NO_TEMP_SENSOR,
        .sample_time            = 5,
        .continous              = false,
        .chopping               = true, /* Useful for DC and slowly changing signals */
        .oversampling           = HW_GPADC_OVERSAMPLING_1_SAMPLE,
        .input_attenuator       = HW_GPADC_INPUT_VOLTAGE_UP_TO_3V6,
};


/*  External device/module configurations */
const ad_gpadc_controller_conf_t dev_GPADC = {
        .id  = HW_GPADC_1, /* GPADC controller instance */
        .io  = &bus_GPADC,
        .drv = &drv_GPADC
};


gpadc_device POT_DEVICE = &dev_GPADC;

#endif /* dg_configGPADC_ADAPTER || dg_configUSE_HW_GPADC */

In platform_devices.h file, add the following definitions:

#include <ad_gpadc.h>
#include "peripheral_setup.h"


#ifdef __cplusplus
extern "C" {
#endif


#if dg_configGPADC_ADAPTER || dg_configUSE_HW_GPADC


/**
 * \brief GPADC device handle
 */
typedef const void* gpadc_device;


#endif /* dg_configGPADC_ADAPTER || dg_configUSE_HW_GPADC */



/* List of devices */
extern gpadc_device POT_DEVICE;


#ifdef __cplusplus
}
#endif