5. Code Overview

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

5.1. Header Files

In main.c, add the following header files:

#include "ad_gpadc.h"
#include "hw_wkup.h"

#include <platform_devices.h>

5.2. System Init Code

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

/*
 * Macro for enabling asynchronous GPADC measurements.
 *
 * Valid values:
 * 0: GPADC conversions will follow a synchronous conversion scheme
 * 1: GPADC conversions will follow an asynchronous conversion scheme
 *
 */
#define POT_ASYNC_EN   (1)

/* OS signals used for synchronizing OS tasks */
OS_EVENT signal_bat;
OS_EVENT signal_pot;

/* GPADC Task priority */
#define mainGPADC_TASK_PRIORITY                 ( OS_TASK_PRIORITY_NORMAL )

/*
 * GPADC application tasks - Function prototype
 */
static void prvGPADC_Battery_Task( void *pvParameters );
static void prvGPADC_POT_Task( void *pvParameters );

int convert_adc_to_mv(gpadc_source src, uint16_t value);
int convert_mv_to_adc(gpadc_source src, uint16_t value) __attribute__((unused));


static void system_init( void *pvParameters )
{
        OS_TASK task_h = NULL;
        OS_TASK task_bat_h = NULL;
        OS_TASK task_pot_h = NULL;

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

        /* 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 XTAL16M has settled and, maybe, the PLL
         * is locked.
         */
        cm_sys_clk_init(sysclk_XTAL16M);
        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();

        /* init resources */
        resource_init();

#if defined CONFIG_RETARGET
        retarget_init();
#endif

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

        /* Initialize the OS event signals. */
        OS_EVENT_CREATE(signal_bat);
        OS_EVENT_CREATE(signal_pot);

        /* Start main task here */
        OS_TASK_CREATE( "Template",                     /* The text name assigned to the task, for
                                                           debug only; not used by the kernel. */
                        prvTemplateTask,                /* The function that implements the task. */
                        NULL,                           /* The parameter passed to the task. */
                        200 * OS_STACK_WORD_SIZE,       /* The number of bytes to allocate to the
                                                           stack of the task. */
                        mainTEMPLATE_TASK_PRIORITY,     /* The priority assigned to the task. */
                        task_h );                       /* The task handle */
        OS_ASSERT(task_h);

        /* Suspend task execution */
        OS_TASK_SUSPEND(task_h);

        /*
         * Task responsible for battery voltage measurements
         */
        OS_TASK_CREATE("GPADC_BATTERY",

                        prvGPADC_Battery_Task,
                        NULL,
                        200 * OS_STACK_WORD_SIZE,

                        mainGPADC_TASK_PRIORITY,
                        task_bat_h );
        OS_ASSERT(task_bat_h);

        /*
         * Task responsible for GPADC voltage measurements
         */
        OS_TASK_CREATE("GPADC_POT",

                        prvGPADC_POT_Task,
                        NULL,
                        200 * OS_STACK_WORD_SIZE,

                        mainGPADC_TASK_PRIORITY,
                        task_pot_h );
        OS_ASSERT(task_pot_h);


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

}

5.3. Wake-Up Timer Code

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

/*
 * Callback function to be called after an external event is generated,
 * that is, after K1 button on the Pro DevKit is pressed.
 */
void wkup_cb(void)
{
        /*
         * This function must be called by any user-specified
         * interrupt callback, to clear the interrupt flag.
         */
        hw_wkup_reset_interrupt();

        /*
         * Notify [prvGPADC_Battery_Task] that time for
         * execution has elapsed.
         */
        OS_EVENT_SIGNAL_FROM_ISR(signal_bat);
}

/*
 * Function which makes all the necessary initializations for the
 * wake-up controller
 */
static void init_wkup(void)
{
        /*
         * This function must be called first and is responsible
         * for the initialization of the hardware block.
         */
        hw_wkup_init(NULL);

        /*
         * Configure the pin(s) that can trigger the device to wake up while
         * in sleep mode. The last input parameter determines the triggering
         * edge of the pulse (event)
         */
        hw_wkup_configure_pin(HW_GPIO_PORT_1, HW_GPIO_PIN_6, true, HW_WKUP_PIN_STATE_LOW);


        /*
         * This function defines a delay between the moment at which
         * a trigger event is present and the moment at which the controller
         * takes this event into consideration. Setting debounce time to [0]
         * hardware debouncing mechanism is disabled. Maximum debounce time is 63 ms.
         */
        hw_wkup_set_debounce_time(10);

// Check if the chip is either DA14680 or 81
#if dg_configBLACK_ORCA_IC_REV == BLACK_ORCA_IC_REV_A

        /*
         * Set threshold for event counter. Interrupt is generated after
         * the event counter reaches the configured value. This function
         * is only supported in DA14680/1 chips.
         */
        hw_wkup_set_counter_threshold(1);
#endif

        /* Register interrupt handler */
        hw_wkup_register_interrupt(wkup_cb, 1);
}

5.4. Hardware Initialization

In main.c, replace both periph_init() and prvSetupHardware() with the following code responsible for configuring pins after a power-up/wake-up cycle. Please note that every time the system enters sleep, it loses all its pin configurations.

/**
 * @brief Initialize the peripherals domain after power-up.
 *
 */
static void periph_init(void)
{
#       if dg_configBLACK_ORCA_MB_REV == BLACK_ORCA_MB_REV_D
#               define UART_TX_PORT    HW_GPIO_PORT_1
#               define UART_TX_PIN     HW_GPIO_PIN_3
#               define UART_RX_PORT    HW_GPIO_PORT_2
#               define UART_RX_PIN     HW_GPIO_PIN_3
#       else
#               error "Unknown value for dg_configBLACK_ORCA_MB_REV!"
#       endif


        hw_gpio_set_pin_function(UART_TX_PORT, UART_TX_PIN, HW_GPIO_MODE_OUTPUT,
                        HW_GPIO_FUNC_UART_TX);
        hw_gpio_set_pin_function(UART_RX_PORT, UART_RX_PIN, HW_GPIO_MODE_INPUT,
                        HW_GPIO_FUNC_UART_RX);

        /* LED D2 on ProDev Kit for debugging purposes */
        hw_gpio_set_pin_function(HW_GPIO_PORT_1, HW_GPIO_PIN_5,
                                        HW_GPIO_MODE_OUTPUT, HW_GPIO_FUNC_GPIO);

        /* Configure P1.2 pin for ADC measurements */
        hw_gpio_set_pin_function(HW_GPIO_PORT_1, HW_GPIO_PIN_2,
                                           HW_GPIO_MODE_INPUT, HW_GPIO_FUNC_ADC);
}

/**
 * @brief Hardware Initialization
 */
static void prvSetupHardware( void )
{
        /* Init hardware */
        pm_system_init(periph_init);
        init_wkup();
}

5.5. Raw ADC to mV Conversion Code

In main.c, after system_init(), add the following code responsible for converting a raw ADC value to mV:

/* 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_adc_to_mv(gpadc_source src, uint16_t value)
{
        gpadc_source_config *cfg = (gpadc_source_config *)src;
        const uint16 adc_src_max =  ad_gpadc_get_source_max(src);
        uint32_t mv_src_max = (cfg->hw_init.input_attenuator == HW_GPADC_INPUT_VOLTAGE_UP_TO_1V2) ?
                                                                                        1200 : 3600;

        int ret = 0;

        switch (cfg->hw_init.input_mode) {
        case HW_GPADC_INPUT_MODE_SINGLE_ENDED:
                if (cfg->hw_init.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;
}

5.6. mV to Raw ADC Value Conversion Code

In main.c, after system_init(), add the following code responsible for converting mV to raw ADC values:

/*
 * Function for converting millivolt to raw ADC value
 *
 * \param [in]  src    The handler returned when calling the ad_battery_open() API
 * \param [in]  value  The ADC value expressed in mVolt e.g. 3050 (3.05V)
 *
 * \return    In single-ended mode: the converted raw ADC value.
 *            In differential mode: -1 (not supported)
 *
 */
int convert_mv_to_adc(gpadc_source src, uint16_t value)
{
        gpadc_source_config *cfg = (gpadc_source_config *)src;
        const uint16_t adc_src_max = ad_gpadc_get_source_max(src);
        uint32_t mv_src_max = (cfg->hw_init.input_attenuator == HW_GPADC_INPUT_VOLTAGE_UP_TO_1V2) ?
                                                                                        1200 : 3600;

        int ret = 0;

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

        return ret;
}

Note

This function can be used when it comes to charger configurations where raw ADC values must be defined using appropriate macros, for instance dg_configCHARGING_THRESHOLD and dg_configPRECHARGING_THRESHOLD.

5.7. Task Code for BAT Measurements

Code snippet of prvGPADC_Battery_Task task responsible for performing battery measurements. In main.c add the following code (after system_init()):

/* Task responsible for battery voltage measurements */
static void prvGPADC_Battery_Task( void *pvParameters )
{
        battery_source bat_hdr;

        uint16_t bat_raw_adc;  // raw ADC value
        uint16_t bat_value_mv; // converted value to mVolt

        /*
         * Buffer to hold the debugging message
         */
        char dbg_message[40];
        memset(dbg_message, 0x20, sizeof(dbg_message));


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


        for (;;) {

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


                /* Open the battery interface */
                bat_hdr = ad_battery_open();

               /*
                * Read the raw ADC value of the battery. Based on the oversampling used
                * the returned value can vary from 10 to 16 bits.
                */
                bat_raw_adc = ad_battery_read(bat_hdr);


                /*
                 * Convert the previously measured raw ADC value to millivolt (mV)
                 */
                bat_value_mv = ad_battery_raw_to_mvolt(bat_hdr, (uint32_t) bat_raw_adc);


                /*
                 * Prepare the message to be sent out on the serial console
                 */
                sprintf(dbg_message, "\n\rBattery voltage is: %d  mV\n\r", bat_value_mv);

                printf("%s", dbg_message);
                fflush(stdout);

                /* Close the already opened battery interface */
                ad_battery_close(bat_hdr);

        }
}

5.8. Task Code for GPADC Measurements

Code snippet of prvGPADC_POT_Task task responsible for performing voltage measurements from an analog GPIO pin. In main.c, add the following code (after system_init()):

#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 potentiometer_task_cb(void *user_data, int value)
{
        /* User can pass in and process data from here */
        uint16_t *user_ptr = (uint16_t *) user_data;

        /* Read the raw ADC value */
        *user_ptr = (uint16_t) value;

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


/* Task responsible for GPADC voltage measurements */
static void prvGPADC_POT_Task( void *pvParameters )
{
        gpadc_source pot_hdr;

        uint16_t pot_raw_adc;  // raw ADC value
        uint16_t pot_value_mv; // converted value to mV

        /*
         * Buffer for holding the debugging message
         */
        char dbg_message[40];
        memset(dbg_message, 0x20, sizeof(dbg_message));

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


        for (;;) {

                /* Suspend task's execution for 1000 ms */
                OS_DELAY(OS_MS_2_TICKS(1000));

                /*
                 * Turn on the LED D2 on Pro DevKit indicating the beginning of a process.
                 */
                hw_gpio_set_active(HW_GPIO_PORT_1, HW_GPIO_PIN_5);


                /*
                 * Open the GPADC interface used (POT_LEVEL).
                 */
                 pot_hdr = ad_gpadc_open(POT_LEVEL);


#if POT_ASYNC_EN == 0
                /*
                 * Perform a synchronous GPADC operation.
                 */
                ad_gpadc_read(pot_hdr, &pot_raw_adc);
#else
                /*
                 * Perform an asynchronous GPADC operation.
                 */
                ad_gpadc_read_async(pot_hdr, potentiometer_task_cb, (void *) &pot_raw_adc);

                /*
                 * Wait until the current GPADC operation is finished
                 */
                OS_EVENT_WAIT(signal_pot, OS_EVENT_FOREVER);
#endif
                /*
                 * Convert the previously measured raw ADC value to millivolt (mV)
                 */
                pot_value_mv = (uint16_t) convert_adc_to_mv(pot_hdr, pot_raw_adc);

                /*
                 * Prepare the message to be sent out on the serial console.
                 */
                sprintf(dbg_message, "\n\rPotentiometer voltage is: %d mV\n\r", pot_value_mv);

                printf("%s", dbg_message);
                fflush((stdout));

                /* Close the already opened device */
                ad_gpadc_close(pot_hdr);

                /*
                 * Turn off LED D2 on ProDev kit indicating the end of a process.
                 */
                hw_gpio_set_inactive(HW_GPIO_PORT_1, HW_GPIO_PIN_5);

        }
}

5.9. Macro Definitions

In /config/custom_config_qspi.h, add the following macro definitions:

/* Enable the GPADC instance used for POT measurements */
#define GPADC_POT

/*
 * Macros for enabling GPADC + Battery measurement operations using Adapters
 */
#define dg_configUSE_HW_GPADC                   (1)
#define dg_configGPADC_ADAPTER                  (1)
#define dg_configBATTERY_ADAPTER                (1)


/*
 * This macro should be already written in the file so just make sure
 * it is set to [1].
 */
#define dg_configUSE_HW_TEMPSENS                (1)

5.10. GPADC HW Configuration Macros

In the newly created platform_devices.h, add the following macro definition between #if dg_configGPADC_ADAPTER and #endif

#ifdef GPADC_POT
GPADC_SOURCE(POT_LEVEL, HW_GPADC_CLOCK_INTERNAL, HW_GPADC_INPUT_MODE_SINGLE_ENDED,
     HW_GPADC_INPUT_SE_P12, 5, true, HW_GPADC_OVERSAMPLING_2_SAMPLES, HW_GPADC_INPUT_VOLTAGE_UP_TO_3V6)
#endif

Note

By default, the SDK comes with a few predefined device settings in platform_devices.h. Therefore, the developer should check whether an entry matches with a device connected to the controller.