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_spi.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 SPI transactions.
*
* Valid values:
* 0: SPI transactions will follow a synchronous scheme
* 1: SPI transactions will follow an asynchronous scheme
*
*/
#define SPI_ASYNC_EN (0)
/* OS signals used for synchronizing OS tasks*/
static OS_EVENT signal_echo;
static OS_EVENT signal_mcp_4822;
/* SPI Task priority */
#define mainSPI_TASK_PRIORITY ( OS_TASK_PRIORITY_NORMAL )
/*
* SPI application tasks - Function prototype
*/
static void prvSPITask_ECHO( void *pvParameters );
static void prvSPITask_MCP_4822( void *pvParameter );
static void system_init( void *pvParameters )
{
OS_TASK task_h = NULL;
/* Handler for SPI freeRTOS tasks */
OS_TASK echo_h = NULL;
OS_TASK mcp4822_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_echo);
OS_EVENT_CREATE(signal_mcp_4822);
/* 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);
/*
* Create an SPI task responsible for SPI loopback tests
*/
OS_TASK_CREATE( "SPI_ECHO",
prvSPITask_ECHO,
NULL,
200 * OS_STACK_WORD_SIZE,
mainSPI_TASK_PRIORITY,
echo_h );
OS_ASSERT(echo_h);
/*
* Create an SPI task responsible for controlling the
* externally connected MCP4822 module.
*/
OS_TASK_CREATE( "SPI_MCP_4822",
prvSPITask_MCP_4822,
NULL,
200 * OS_STACK_WORD_SIZE,
mainSPI_TASK_PRIORITY,
mcp4822_h );
OS_ASSERT(mcp4822_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 the [prvSPITask_ECHO] task that time for performing
* a loopback test has elapsed.
*/
OS_EVENT_SIGNAL_FROM_ISR(signal_echo);
}
/*
* 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.
/* SPI pin configurations */
static const gpio_config gpio_cfg[] = {
// The system is set to [Master], so it outputs the clock signal
HW_GPIO_PINCONFIG(HW_GPIO_PORT_3, HW_GPIO_PIN_0, OUTPUT, SPI_CLK, true),
// Pin for capturing data
HW_GPIO_PINCONFIG(HW_GPIO_PORT_3, HW_GPIO_PIN_1, INPUT, SPI_DI, true),
// Pin for outputting data
HW_GPIO_PINCONFIG(HW_GPIO_PORT_3, HW_GPIO_PIN_2, OUTPUT, SPI_DO, true),
/*
* CS pin used when performing the loopback tests. Since the system is set
* to [Master], it drives this line.
*/
HW_GPIO_PINCONFIG(HW_GPIO_PORT_3, HW_GPIO_PIN_3, OUTPUT, GPIO, true),
/*
* CS pin used when performing transactions with the MCP4822 module.
* Since the system is set to [Master], it drives this line.
*/
HW_GPIO_PINCONFIG(HW_GPIO_PORT_3, HW_GPIO_PIN_4, OUTPUT, GPIO, true),
HW_GPIO_PINCONFIG_END // This is critical for the correct termination of the structure
};
/**
* @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 the SPI pins */
hw_gpio_configure(gpio_cfg);
}
/**
* @brief Hardware Initialization
*/
static void prvSetupHardware( void )
{
/* Init hardware */
pm_system_init(periph_init);
init_wkup();
}
5.5. Task Code for MCP4822¶
Code snippet of the prvSPITask_MCP_4822
task responsible for interacting with the MCP4822 DAC module,
externally connected on SPI1 bus. In main.c
, add the following code (after system_init()
):
/*
* Data structure for the DAC4822 module
*/
static struct spi_mcp_4822 {
// DAC4822 consists of a 2-byte register
uint16_t data;
// Control bits mask
uint16_t mask;
// Device handle
spi_device spi_dev;
} spi_mcp_4822_t = { // Create an instance of the above structure
.data = 0x0000,
// Control bits occupy the 4 MSbits and are set to '1'
.mask = 0xF000,
.spi_dev = NULL,
};
#if SPI_ASYNC_EN == 1
/*
* Callback function called upon an SPI asynchronous transaction.
*
* \param[in] user_data User data that can be passed and used within the function
*/
void spi_mcp_4822_cb( void *user_data )
{
/*
* Just to show how parameters can be passed
* within callback functions!
*/
struct spi_mcp_4822 *spi_param = (struct spi_mcp_4822 *)user_data;
/*
* Increment the analog output value of the DAC module. When data reaches
* its maximum value, it wraps around starting from zero value.
*/
spi_param->data += 10;
/* Signal [prvSPITask_MCP_4822] that time for resuming has elapsed */
OS_EVENT_SIGNAL_FROM_ISR(signal_mcp_4822);
}
#endif
/* Task responsible for controlling the MCP4822 module */
static void prvSPITask_MCP_4822( void *pvParameters )
{
/*
* SPI adapter initialization should be done once at the beginning. Alternatively,
* this function could be called during system initialization in system_init().
*/
ad_spi_init();
for (;;) {
/* Suspend task's execution for 100 ms */
OS_DELAY(OS_MS_2_TICKS(100));
/*
* Set DAC's control bits using a mask.
*/
spi_mcp_4822_t.data |= spi_mcp_4822_t.mask;
/*
* Turn on LED D2 on ProDev Kit indicating the start of a process
*/
hw_gpio_set_active(HW_GPIO_PORT_1, HW_GPIO_PIN_5);
/*
* Open the device that will access the SPI bus
*/
spi_mcp_4822_t.spi_dev = ad_spi_open(MCP_4822);
#if SPI_ASYNC_EN == 0
/*
* Perform a synchronous SPI write operation, that is, the task is blocking
* waiting for the transaction to finish. Upon transaction completion,
* the blocked task unblocks and resumes its operation.
*/
ad_spi_write(spi_mcp_4822_t.spi_dev, (uint8_t *)&spi_mcp_4822_t.data,
sizeof(uint16_t));
/*
* Increment the analog output value of the DAC module. When data reaches
* its maximum value, it wraps around starting from zero value.
*/
spi_mcp_4822_t.data += 10;
#else
/*
* Perform an asynchronous SPI write operation, that is, the task does not
* block waiting for the transaction to finish. Upon transaction completion
* callback function is triggered indicating the completion of the SPI operation
*/
ad_spi_async_transact(spi_mcp_4822_t.spi_dev,
SPI_CSA, // Activate CS signal
/* Send some data... */
SPI_SND((uint8_t *)&spi_mcp_4822_t.data, sizeof(uint16_t)),
/* Declare callback function and data to be passed inside it. */
SPI_CB1(spi_mcp_4822_cb, &spi_mcp_4822_t),
SPI_CSD, // Deactivate CS signal
SPI_END
);
/*
* In the meantime and while SPI transactions are performed in the background,
* application task can proceed to other operations/calculation.
* It is essential that, the new operations do not involve SPI transactions
* on the already occupied bus!!!
*/
/*
* Make sure that the current SPI operation has finished,
* blocking here forever.
*/
OS_EVENT_WAIT(signal_mcp_4822, OS_EVENT_FOREVER);
#endif
/* Close the already opened device */
ad_spi_close(spi_mcp_4822_t.spi_dev);
/*
* 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);
} // end of for()
} // end of task
5.6. Task Code for Loopback Test¶
Code snippet of the prvSPITask_ECHO
task responsible for performing loopback tests. In main.c
,
add the following code (after prvSPITask_MCP_4822()
):
/* Number of transmitted/received data */
#define BUFFER_SIZE 5
/*
* Create an SPI task responsible for SPI loopback tests
*/
static void prvSPITask_ECHO( void *pvParamters )
{
/* Buffer for storing the transmitted data */
uint8_t wbuf[BUFFER_SIZE];
/* Buffer for storing the received data */
uint8_t rbuf[BUFFER_SIZE];
/* Configure the transfer scheme used during the SPI duplex transaction */
spi_transfer_data spi_transfer_cfg = {
.wbuf = (void *)wbuf,
.rbuf = (void *)rbuf,
.length = BUFFER_SIZE,
};
/* Initialize transmitted data - All data are set to 0x55 */
memset(wbuf, 0x55, BUFFER_SIZE);
/*
* Initialization should be done once at the beginning. Alternatively,
* this function could be called during system initialization in [system_init]
* function.
*/
ad_spi_init();
spi_device spi_dev;
for (;;) {
/*
* Suspend task execution - As soon as WKUP callback function
* is triggered the task resumes its execution.
*/
OS_EVENT_WAIT(signal_echo, OS_EVENT_FOREVER);
/* Open the device that will access the SPI bus */
spi_dev = ad_spi_open(ECHO_LOOP);
printf("\n\rSPI loopback operation...\n\r");
/* Perform a duplex SPI transaction */
ad_spi_complex_transact(spi_dev, &spi_transfer_cfg, 1);
/* Close the already opened device */
ad_spi_close(spi_dev);
/*
* Check whether the read data matches written data. If there is a match
* [strncmp] returns [0].
*/
if (!strncmp((char *)spi_transfer_cfg.rbuf, (char *)spi_transfer_cfg.wbuf,
BUFFER_SIZE)) {
printf("\n\rRead data matches written data!\n\r\n\r");
} else {
printf("\n\rRead data does not match written data!\n\r\n\r");
}
/* Print out the received data */
for (int i = 0; i < 5; i++) {
printf("\n\rRBUF[%d] = 0x%X\n\r", i,
*( ((uint8_t *)spi_transfer_cfg.rbuf) + i) );
}
fflush(stdout);
} // end of for()
} // end of task
5.7. Macro Definitions¶
In config/custom_config_qspi.h
, add the following macro definitions:
/*
* Enable the preferred devices connected on SPI bus, declared in [platform_devices.h]
*/
#define SPI_ECHO
#define SPI_MCP_4822
/*
* Macros for enabling SPI operations using Adapters
*/
#define dg_configUSE_HW_SPI (1)
#define dg_configSPI_ADAPTER (1)
5.8. SPI Bus Configuration Macros¶
In the newly created platform_devices.h
, add the following device configuration between SPI_BUS(SPI1)
and SPI_BUS_END
:
#ifdef SPI_ECHO
SPI_SLAVE_DEVICE(SPI1, ECHO_LOOP, HW_GPIO_PORT_3, HW_GPIO_PIN_3, HW_SPI_WORD_8BIT,
HW_SPI_POL_LOW, HW_SPI_PHA_MODE_0, HW_SPI_FREQ_DIV_2, -1);
#endif
#ifdef SPI_MCP_4822
SPI_SLAVE_DEVICE(SPI1, MCP_4822, HW_GPIO_PORT_3, HW_GPIO_PIN_4, HW_SPI_WORD_16BIT,
HW_SPI_POL_LOW, HW_SPI_PHA_MODE_0, HW_SPI_FREQ_DIV_2, -1);
#endif
Note
By default, the SDK comes with a few predefined device configurations in the platform_devices.h
header file. Therefore, the developer should check whether an entry matches with a device connected to the
controller.