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