3. BSP

3.1. Drivers

3.1.1. Introduction

The SDK provides Low Level Drivers (LLD) for each of the available hardware peripherals.

The LLDs expose simple APIs for accessing and using the device peripherals without detailed knowledge of the hardware implementation, such as bits and their position within hardware registers.

For each hardware peripheral a dedicated header file describes the API functions of the peripheral, lists capabilities and defines control structures which are needed to interact with its particular LLD.

All LLD API functions share some common characteristics:

  • They all start with hw_

  • They are declared inside sdk/bsp/peripherals/include/hw_*.h header files

  • They are documented using Doxygen style comments, which describe their input and output parameters, any data types they may use, etc.

The SmartSnippets™ DA1469x SDK Documentation (generated with Doxygen) provides a helpful html reference for the LLD API, as well as for all other APIs of the SmartSnippets™ DA1469x SDK. The main page (shown in Figure 2) is located at <sdk_root_directory>/doc/html/index.html and can also be accessed from within SmartSnippets™ Studio (API documentation section of the Welcome page).


Figure 2 SmartSnippets™ DA1469x SDK Doxygen Documentation main page


All drivers and adapters in the SmartSnippets™ DA1469x SDK are supplied in full source to aid debugging. However, modifying the drivers is not advised.


It is recommended to only use the LLD drivers to access the device peripherals as the LLDs are tested and verified. Direct access to hardware resources (e.g. registers or peripheral interfaces) might lead to conflicts with lower level FW functions accessing the same resources through LLDs and lead to system instabilities.

3.1.2. List of provided LLD APIs

Table 1 LLD overview




AES/HASH Engine.


Brown Out Detection.


Cache controller.




Clocks configuration.


Clock & power manager helper functions.


Crypto engine.


DMA engine.


General purpose ADC.


GPIO controller.


Haptic (LRA/ERM) controller.


Hard-Fault Handler.


I2C controller.


ISO7816 Uart controller.


LCD controller.


LED controller.


Memory controller.


Memory protection unit controller.


OTP Controller.


Power Domain Controller.


Power Domains Driver.


Power management unit.


Quad SPI controller.


Real Time Clock controller.


SDADC controller.


Sensor Node Controller.


SPI controller.




True Random Number Generator controller.


UART controller.


USB controller.


USB framework type definitions.


USB Charger controller.


Watchdog timer.


Wakeup timer.

3.2. Board Abstraction Layer

3.2.1. Introduction

In order to easily port basic SDK interfacing functions (e.g serial port, LED, buttons, etc) on different boards, the SDK defines a set of macros which can be used for configuring the IOs that are used by these functions. For every supported board, a board configuration header file need to be created (e.g my_board.h) and be referenced in the project configuration file using #define dg_configUSE_Board "my_board.h" macro.

The SDK includes board configuration files for all the supported development kits located in bsp/config/boards folder. The contents of the configuration file for the proDK development kit (bsp/config/boards/brd_prodk_da1469x.h) are shown below:

Code 1 proDK Board configuration file
/* Serial port configuration section */
#define SER1_UART       (HW_UART2)

#define SER1_TX_PORT    (HW_GPIO_PORT_0)
#define SER1_TX_PIN     (HW_GPIO_PIN_9)

#define SER1_RX_PORT    (HW_GPIO_PORT_0)
#define SER1_RX_PIN     (HW_GPIO_PIN_8)

#define SER1_RTS_PORT   (HW_GPIO_PORT_1)
#define SER1_RTS_PIN    (HW_GPIO_PIN_0)

#define SER1_CTS_PORT   (HW_GPIO_PORT_0)
#define SER1_CTS_PIN    (HW_GPIO_PIN_7)

/* LED configuration section */
#define LED1_PORT       (HW_GPIO_PORT_1)
#define LED1_PIN        (HW_GPIO_PIN_1)
#define LED1_FUNC       (HW_GPIO_FUNC_GPIO)

/* KEY configuration section */
#define KEY1_PORT       (HW_GPIO_PORT_0)
#define KEY1_PIN        (HW_GPIO_PIN_6)
#define KEY1_FUNC       (HW_GPIO_FUNC_GPIO)

3.3. Wake-up controller

3.3.1. Introduction

The Wake-Up Controller HW block belongs to Always On power domain (PD_AON) and can be programmed to wake up the DA14690 from the Extended/Deep Sleep (clocked) and the Hibernation (clockless) mode. It consists of two parallel circuits that produce two different event sources the KEY_WAKEUP and the GPIO_WAKEUP. Except for waking up the system wake-up controller can also be used while the system is active to provide information about a change in the voltage level of a GPIO input pin.


    Is used to monitor GPIOs levels and triggers the interrupt KEY_WKUP_GPIO_IRQ when a level is changed. A debounce counter can be programmed to debounce the GPIO changed level before the interrupt is triggered. If wkup_enable_irq is set, both M33 and PDC are able to catch the interrupt. KEY_WKUP_GPIO_IRQ is kept asserted until acknowledged by SW, so M33 is capable to receive it even if it was triggered during sleep. This circuit starts RC32K oscillator and can be used to wake up the system from Hibernation if PDC is programmed accordingly.


    M33 should be programmed to accept and acknowledge this interrupt in order for wake-up controller to be able to trigger it again.


    This circuit can wake up M33 from any sleep mode (clocked, clockless mode) if PDC is programmed accordingly.


    M33 and PDC are not aware of which GPIO pin triggered the KEY_WKUP_GPIO_IRQ.


    Is used to monitor the GPIO positive or negative edges. In contrast to KEY_WAKEUP circuit, the GPIO pin which triggers the wake-up controller is stored to WKUP_STATUS_P0_REG or WKUP_STATUS_P1_REG depending on the GPIO port of the pin. This circuit produces two signals, the interrupts GPIO_P0_IRQ and GPIO_P1_IRQ towards M33 and a bus signal towards PDC which delivers the contents of the WKUP_STATUS_X_REG. M33 can access WKUP_STATUS_X_REG and is able to identify the GPIO pin and execute the corresponding ISR routine. PDC can identify as well the triggering source through the signal and execute the corresponding PDC entry.


    M33 should clear the WKUP_STATUS_X_REG in order to acknowledge the interrupt otherwise the interrupt will remain asserted.


    This circuit can wake up the system from any clocked sleep (Deep or Extended sleep) if PDC is programmed accordingly.

Both circuits can be configured to trigger either M33 or PDC or both. If there is no PDC LUT entry then PDC will not take any action. The same applies in M33, if the interrupts are not enabled in NVIC then M33 will not be notified.

3.3.2. Low level driver

Low level wake-up controller handling is implemented in the hw_wkup Low Level Driver (LLD):

  • <sdk-root>/sdk/bsp/peripherals/include/hw_wkup.h

  • <sdk-root>/sdk/bsp/peripherals/src/hw_wkup_da_1469x.c

Because wake-up controller API is very extensive we will focus on the description of very basic functions.

  • Initialize the wake-up controller. See:

    • hw_wkup_init() to initialize the GPIO ports (necessary step for both circuits)

    • hw_wkup_configure() to configure the GPIO we are interesting in ((necessary step for both circuits)

    • hw_wkup_set_debounce_time() to set a debounce timer for the KEY_WAKEUP path.

  • Configure GPIO ports. See:

    • hw_wkup_configure_pin() to configure GPIO pin and level for KEY_WAKEUP path.

    • hw_wkup_gpio_configure_pin() to configure GPIO pin and edge trigger for GPIO_WAKEUP path.

  • Register interrupts. See:

    • hw_wkup_register_key_interrupt() to register an ISR routine for the KEY_WAKEUP path.

    • hw_wkup_register_gpio_p0_interrupt() to register an ISR routine for the GPIO port 0 for the GPIO_WAKEUP path.

    • hw_wkup_register_gpio_p1_interrupt() to register an ISR routine for the GPIO port 1 for the GPIO_WAKEUP path.

  • Read/clear WKUP_STATUS_X_REG valid only for GPIO_WAKEUP path. See:

    • hw_wkup_get_status()

    • hw_wkup_clear_status()

  • Clear/reset KEY_WAKEUP path interrupt. See:

    • hw_wkup_reset_interrupt() If no interrupt routine is configured use this to clear the wkup_ctrl interrupt.

3.3.3. Using wake-up controller in an application

This section describes how to setup wake-up controller to fire an interrupt targeting M33.

First lets configure wake-up controller for KEY_WAKEUP path.

Code 2 Setup wake up controller for debounced input
#include "hw_wkup.h"

// user defined KEY_WKUP_GPIO_IRQ handler
void wkup_handler(void);

// initialize wake-up ctrl

// set debounce time to 10 ms

// set user defined KEY_WKUP_GPIO_IRQ handler
hw_wkup_register_key_interrupt(wkup_handler, 1);

// enable KEY_WKUP_GPIO_IRQ to controller

// configure wake-up controller to fire KEY_WKUP_GPIO_IRQ when P0_6 goes low
hw_wkup_configure_pin( HW_GPIO_PORT_0,
                       1,                    // enable monitoring for given pin
                       HW_WKUP_PIN_STATE_LOW // fire IRQ when pin goes low

A button press will assert the KEY_WKUP_GPIO_IRQ which is routed both to PDC HW block and M33 NVIC. The configuration applied so far is effective while M33 is active: as long as M33 is active and KEY_WKUP_GPIO_IRQ is enabled in NVIC, M33 will fire the corresponding ISR. However a button press will not have any effect if M33 is in sleep mode since we have not programmed a PDC LUT entry to wake up M33 from that trigger source.

The following snippet describes how we can configure wake-up controller to fire GPIO_Px_IRQ to M33.

Code 3 Setup wake up controller for GPIO edge trigger
#include "hw_wkup.h"

// user defined GPIO_P0_IRQ handler
void wkup_gpio_handler(void) {

     /* just do something*/

     //clear the status of GPIO 0-1
     hw_wkup_clear_status(HW_GPIO_PORT_0, 0x1);


// set user defined GPIO_P0_IRQ handler
hw_wkup_register_gpio_p0_interrupt(wkup_gpio_handler, 1);

// configure wake-up controller to fire GPIO_P0_IRQ when P0_6 goes low
hw_wkup_gpio_configure_pin( HW_GPIO_PORT_0,
                         1,                    // enable monitoring for given pin
                         HW_WKUP_PIN_STATE_LOW // fire IRQ when detect negative edge

A button press will assert the GPIO_P0_IRQ which is routed both to PDC HW block and M33 NVIC. The configuration applied so far is effective while M33 is active: as long as M33 is active and GPIO_P0_IRQ is enabled in NVIC, M33 will fire the corresponding ISR. However a button press will not have any effect if M33 is in sleep mode since we have not programmed a PDC LUT entry to wake up M33 from that trigger source.

3.4. PDC

Power Domain Controller (PDC) is the HW block which:

  • coordinates the state of the system’s power domains (see section 6.2 of [Ref_01]).

  • provides master to master signaling services.

  • manages the 32 MHz crystal oscillator.

PDC HW is described in section 8 of the DA1469x datasheet [Ref_01].

PDC registers are described in section 42.25 of [Ref_01]. They belong to the Always On power domain (PD_AON) i.e. their values are retained until the next Power-On or HW reset.

3.4.1. Power domains overview

Apart from the Always On power domain, DA1469x’s power domains can be divided into the following categories:

  • Power domains which contain masters

  • Power domains which do not contain masters Master power domains

The DA1469x SoC contains three processing units which function as masters in the AMBA bus matrix (see section 13 of [Ref_01]). Masters are assigned to distinct power domains. The following table shows the masters and their corresponding power domains:

Table 2 Master power domains


Power Domain


ARM Cortex-M33


System CPU - the main application processor. PD_SYS also contains:

  • QSPI controllers,

  • LCD controller, OTP, ROM, USB, DMA, Crypto blocks


  • the AHB multilayer bus

Sensor Node Controller


Minimal controller for sensor manipulation. PD_COM also contains:

  • all serial interfaces and SD ADC

  • the GPIO multiplexing.

ARM Cortex-M0+


CMAC CPU - the BLE controller Non-master power domains

The following table contains the DA1469x power domains which do not contain masters:

Table 3 Non-master power domains

Power Domain



Timer Power Domain. It contains:

  • MAC timer, TIMER, TIMER2,

  • XTAL32M digital state machine


Peripherals Power Domain. It contains:

  • Audio blocks

  • Motor Controller

  • General purpose ADC

  • WLEDs and LRA

  • On-chip temperature sensors


Memory Power Domain. It contains:

  • Memory controller

  • DCDC digital FSM

  • RAM cells

SW can configure how PDC manages PD_TMR and PD_PER.

SW cannot configure how PDC manages PD_MEM state. PDC HW is solely responsible for that. PDC HW will automatically:

  • turn PD_MEM on when any master power domain is turned on and

  • turn PD_MEM off when all master power domains have been turned off.

3.4.2. HW overview

At each instant a master power domain can be on or off depending on the processing requirements of the system (e.g. BLE stack, clock calibration) and the application. Master power domains are turned on by PDC when a pre-configured event is triggered to wake up the respective master. Several events can be used as PDC trigger sources, for example:

  • The state of a GPIO changes.

  • A timer expires.

The PDC Look Up Table (LUT) contains the mapping between the trigger sources and the masters to be woken up. Each LUT entry describes a specific trigger source and target master pair. The same trigger source can be used in different entries i.e. the same trigger can wake up multiple masters. Similarly the same master can be targeted by different entries i.e. a master can be woken up by multiple trigger sources.

See also

For more detailed information about PDC LUT please refer to section 8.2 of [Ref_01].


Setting up a trigger in the PDC LUT and configuring the trigger source to generate the trigger event are two separate concerns. SW must do both. E.g. in order to wake up M33 from an RTC alarm the SW must both:

  • Setup RTC to fire an interrupt when the alarm is activated.

  • Add a PDC LUT entry to wakeup M33 from the RTC alarm.

Apart from the trigger source and the master to be woken up, each PDC LUT entry contains additional instructions about the course of action to be taken when a master power domain is enabled:

  • Turn on additional power domains (non-master: PD_TMR, PD_PER or master: PD_COM) which are required by the master to do its work. E.g. if M33 is the master to be woken up and it needs to drive some GPIO then we also need to have PD_COM enabled.

  • Enable XTAL32M. Once enabled XTAL32M will be kept enabled until all masters go to sleep.

When a PDC LUT entry is triggered, it is marked as pending in PDC_PENDING_REG and the SW running in the respective master has to acknowledge this through PDC_ACKNOWLEDGE_REG. PDC HW also internally tracks which LUT entries have been activated for each master.

A PDC LUT can be triggered by SW through PDC_SET_PENDING_REG.

The activated PDC LUT entries for a master are effective until the master finishes processing (i.e. until it has no work to do and it could enter low power mode). A master signals PDC about having finished processing as follows:

  • M33 (and CMAC) executes the WFI instruction while SCR.DEEPSLEEP = 1.

  • SNC executes the SLP instruction.

PDC will turn off the power domain of the master that finished processing and it will “release” all activated entries for that master. It will let each power domain requested by those entries to go down provided that no other activated LUT entry (for some other master) is still requesting that power domain.

When the last active master goes to sleep PDC will turn off PD_MEM and then activate the “goto sleep” HW FSM (section 6.3 of [Ref_01]) to put the whole device in sleep mode. Given the whole device is in sleep mode, when a PDC trigger fires:

  • the wake-up HW FSM will be activated

  • PDC will turn PD_MEM on

  • PDC will perform the actions requested by the triggered PDC LUT entries.

3.4.3. SW overview

M33 is responsible for setting up the PDC.

M33 is the only master allowed to modify PDC configuration.

M33 does not use the PDC IRQ. The SDK keeps this interrupt disabled. M33 acknowledges all pending PDC LUT entries (which target M33) before attempting to enter sleep (in function goto_deepsleep()).

The system sleep entry and exit procedure is depicted in the following diagram.


Figure 3 System sleep entry and exit

3.4.4. Low level driver

Low level PDC handling is implemented in the hw_pdc Low Level Driver (LLD):

  • <sdk-root>/sdk/bsp/peripherals/include/hw_pdc.h

  • <sdk-root>/sdk/bsp/peripherals/src/hw_pdc.c

The PDC LLD provides preprocessor macros to construct valid PDC LUT entries. See:

  • HW_PDC_LUT_ENTRY_VAL() generic macro as well as

  • HW_PDC_TRIGGER_FROM_<x>() per trigger source type macros

The PDC LLD provides APIs to:

  • Read/modify a LUT entry at a given index. See:

    • hw_pdc_read_entry()

    • hw_pdc_write_entry()

  • Add/remove a LUT entry taking into account that the LUT may already be populated with some entries. See:

    • hw_pdc_add_entry()

    • hw_pdc_remove_entry()

  • Query if PDC LUT entries are pending. See:

    • hw_pdc_get_pending()

    • hw_pdc_get_pending_cm33()

    • hw_pdc_get_pending_cmac()

    • hw_pdc_get_pending_snc()

    • hw_pdc_is_pending()

  • Trigger a PDC LUT entry by SW. See:

    • hw_pdc_set_pending()

  • Acknowledge a pending PDC LUT entry. See:

    • hw_pdc_acknowledge()

PDC LLD does not handle system level concerns as:

  • Concurrent access from different FreeRTOS tasks.

  • Keeping a master power domain alive while the PDC LUT is being modified. If all entries which keep a master alive get invalidated, its power domain will be immediately turned off terminating its execution.

3.4.5. PDC LLD integration into the system

PDC LUT configuration is applied both:

  • Statically during startup for setting up entries required by any FreeRTOS BLE application.

  • Dynamically according to the application’s needs. This method is employed by the Sensor Node Controller adapter.

PDC LLD is used by the following M33 software modules:

  • System startup code

  • Clock manager

  • Power manager

  • SNC adapter System startup

During startup the PDC lookup table is statically populated with the entries required by all FreeRTOS BLE applications (see configure_pdc() in sdk/bsp/startup/DA1469x/system_da1469x.c):

  1. Wake up system CPU from TIMER2 (used by FreeRTOS port as the low power timer).

  2. Wake up system CPU when signaled from CMAC controller through the mailbox mechanism. Notice that the CMAC2SYS IRQ is multiplexed together with VBUS IRQ, debounced IO IRQ and JTAG present signal in the same “COMBO” PDC trigger (identified as HW_PDC_PERIPH_TRIG_ID_COMBO in the LLD) so we actually setup a common PDC trigger for all those events.

  3. Wake up CMAC from MAC timer or from system CPU signaling (both are encoded in a single entry).

Currently all the above PDC LUT entries get the 32MHz crystal started. Switching the system clock to the 32 MHz crystal oscillator is clock manager’s responsibility.

All the above entries are also SW-triggered in order to prepare for sleep. We let PDC keep M33 alive and stop forcing PD_SYS to be always on through PMU_CTRL_REG.SYS_SLEEP. Clock manager

Upon wake-up clock manager starts XTAL32M by SW-triggering the RTOS timer PDC entry. Power manager

When power manager needs to wake-up CMAC it SW-triggers the corresponding PDC entry (created during startup) to accomplish that.

Power manager checks if the COMBO trigger is pending in order to detect if M33 has been woken-up by a debugger. SNC adapter

SNC adapter’s responsibility is to set up

  1. the PDC entries that trigger SNC execution and

  2. SNC micro-code (ucode) to be executed when each PDC trigger is fired.

For more details regarding M33 – SNC communication refer to Section Section 5.8.2.

3.4.6. Using PDC in an application

This section describes how to setup a PDC trigger targeting M33. The example will show how to wake up M33 from a debounced button press.

First we need to setup the peripheral (wake-up controller) to fire the corresponding interrupt (debounced IO IRQ - KEY_WKUP_GPIO_IRQ) which will serve as the PDC trigger source. Please refer to Using wake-up controller in an application for more details on this.

In order to actually wake up M33 we need to instruct PDC what to do upon detecting that the trigger has fired. The following code snippet shows how this is accomplished.

Code 4 Setup PDC entry to wake up M33 from debounced input
uint32_t idx = HW_PDC_INVALID_LUT_INDEX;

idx = hw_pdc_add_entry(HW_PDC_LUT_ENTRY_VAL(

Notice that the “debounced IO IRQ” is multiplexed with other trigger sources (VBUS IRQ, CMAC2SYS IRQ, JTAG present) in a single PDC peripheral trigger ID (HW_PDC_PERIPH_TRIG_ID_COMBO).


In the typical FreeRTOS/BLE application you do not actually need to create a PDC entry for a debounced input IRQ because such an entry shall already exist. A COMBO triggered PDC entry always gets created during system start up because M33 needs to wake up from CMAC2SYS IRQ which is fired as part of CMAC to M33 communication through the mailbox mechanism.

The constructed PDC entry will wake up M33 (possibly after the “wake up” HW FSM is first triggered if all masters had previously gone to sleep and the whole device had entered sleep mode) and M33 SW will see KEY_WKUP_GPIO_IRQ pending in NVIC as soon as M33 starts to execute.

The constructed PDC entry also requests that the 32 MHz crystal oscillator should be started. When this entry gets activated, PDC will start the crystal provided that it has not already been started because some other entry (requiring the XTAL32M) was activated since the last device wake up (including the entry that triggered the device wake up).

The above code snippet also triggers by SW the PDC LUT entry and then it acknowledges the pending entry. The former action guarantees that PD_SYS stays on in case the application invalidates all other activated PDC entries which are keeping M33 alive. Acknowledging the newly created (and activated) PDC entry is recommended although not strictly needed since M33 will automatically acknowledge all pending PDC entries before its next attempt to enter sleep mode.

3.5. RTC

Real Time Clock (RTC) is a HW block which provides complete clock and calendar information, generates one-time or recurring alarms and detects when a particular event occurs. For more details please refer to [Ref_01].

The SmartSnippets™ DA1469x SDK provides an extended API with features such as the ability to configure the RTC time and calendar registers, reading the corresponding values, setting alarms, enabling interrupts and setting up the event controller for configuring and enabling the RTC/Motor to PDC event.

See also

For more information about the RTC LLD API please refer to the SmartSnippets™ DA1469x SDK Documentation.

3.5.1. Application use

From an application point of view RTC can be used to track the time and date, utilize the alarm functionality or just the event detection. RTC can also be used as a trigger source to SNC ucodes through the RTC-to-PDC event, or as a trigger source to Step Motor Controller through the Motor-to-PDC event.

3.5.2. System use and API limitations

At system level the SmartSnippets™ DA1469x SDK utilizes RTC in all BLE applications and/or when RCX is set as the system’s low power clock in order to trigger the corresponding SNC ucodes, which are dedicated for RF and RCX calibration respectively (see ADC Service and Low Power Clocks).

This partial reservation of RTC for system use leads to some limitations in the exposed API as RTC must be configured accordingly, preserve this configuration and be always enabled. More specifically, it is recommended not to invoke the following functions for the sake of system’s best performance when operating on a BLE application or with RCX as low power clock:

  • hw_rtc_config_RTC_to_PDC_evt()

  • hw_rtc_time_start()

  • void hw_rtc_time_stop()

  • hw_rtc_start()

  • hw_rtc_stop()

  • hw_rtc_pdc_event_enable()

  • hw_rtc_pdc_event_disable()

  • hw_rtc_set_pdc_event_period()

  • hw_rtc_pdc_event_clear()

  • hw_rtc_reset()

  • hw_rtc_reset_set()

  • hw_rtc_reset_clear()

3.6. System Clocks

Table 4 System Clocks




Crystal oscillator for the low power clock (32.786KHz). Crystal Constraint should be up to +/-250ppm.


Crystal oscillator for the system clock 32MHz. Crystal Constraint should be up to +/-40ppm or +/-20ppm untrimmed


XTAL32K replacement (15KHz typical) with precision of < 500 ppm.


RC oscillator (32KHz) for the low power clock. Frequency is temperature dependent.


RC oscillator (31.5MHz typical) for clocking the HW-FSM at power up.


PLL (69MHz) which will increase the system clock to 96MHz.

3.6.1. Low Power Clocks

During sleep one of the two low power clocks, RCX or XTAL32K, is used since they have precision of < 500 ppm. XTAL32K uses an external crystal oscillator, generates 32.768 kHz and it is tolerant to voltage and temperature changes. The enhanced RC oscillator (RCX) is a simple RC oscillator and generates a typical frequency of 15 kHz. The RCX oscillator can be used to replace the 32.768 kHz crystal, since it has a precision of < 500 ppm, while its output frequency is quite stable over temperature. However, RCX is sensitive to voltage and temperature changes and requires recalibration. In the case of using the RCX as a sleep clock, Sensor Node Controller checks periodically if temperature or battery voltage has changed and performs RCX frequency calibration if needed. The procedure of the RCX calibration is depicted in the next diagram (Figure 4).


Figure 4 RCX calibration procedure