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.