3. Analyzing the Key Points of a Sample Code

This section covers the fundamental code structure found in every demonstration application included in Dialog’s SDK. All applications are implemented as FreeRTOS tasks that are created under the system_init() function.

3.1. Clock and Power Manager

The clock manager is part of the clock and power manager (CPM) and it:

  • Controls the system level clocks. The divider of each hardware resource is responsible to control the clock settings for the resource. In this context, the CPM controls the system clock which is illustrated with green line (sys_clk) in Fig. 13, the AHB and APB clocks which are the blue lines at the top of the figure, and the low power clock which is the black line (lp_clk).
  • Handles requests to switch to another clock configuration (cm_sys_clk_set()). A request may be denied if this switch affects a hardware resource that is active (for example, a hardware timer).
  • Offers the application tasks the ability to switch to another clock configuration during runtime, if possible. The low power clock cannot be changed during runtime.
'Import clock_tree'

Fig. 13 The Clock Tree Diagram for the DA1468x Family of Devices

SmartBond™ DA1468x SDK abstracts the complexity of the clock tree from the applications by offering a unified API for clock control. The details of the API are highlighted in Table 1.


Table 1 Dialog’s API for Clock Control
Function name Description
bool cm_sys_clk_set(sys_clk_t type) Sets the system clock. The available options are: RC16, XTAL16M (or XTAL32M), PLL48, and PLL96. The low power clock cannot be set as the system clock.
bool cm_cpu_clk_set(cpu_clk_t clk) Sets the system clock and the AHB divisor such that the requested clock frequency is achieved.
void cm_apb_set_clock_divider(apb_div_t div) Sets the clock divisor for the APB clock. The actual frequency depends on the system clock used.
bool cm_ahb_set_clock_divider(ahb_div_t div) Sets the clock divisor for the AHB clock. The actual frequency depends on the system clock used.
_get_ and _fromISR Variants of the above _set_ functions.
void cm_lp_clk_init(void) Initializes the Low Power clock.
bool cm_lp_clk_is_avail(void) Checks if the Low Power clock is available.
void cm_clk_init_low_level(void) Executes clock initialization after power-up.
void cm_sys_clk_init(sys_clk_t type) Executes clock initialization after the OS has started.

3.2. The Key Points of the Main Function

The main() function is the basic routine of every source code and is responsible for the following actions:

'SW FSM of main() Function'

Fig. 14 SW FSM of main() Function

  1. The cm_clk_init_low_level function must be called before any other configuration related to system clocks. It is the lowest level function. More specifically, it switches to the internal 16MHz RC oscillator (RC16M), restarts the external 16 MHz crystal (XTAL16M) and may wait for it to settle. It also sets the DIVN clock divider which provides the 16 MHz clock source to various hardware blocks of the device, regardless of the system clock source. It then sets up the low power clock, according to the value of the dg_configUSE_LP_CLK macro in the config/custom_config_qspi.h header file. This function must be called only once, before the freeRTOS scheduler is started.

Note

One case where the system does not wait for the settling of the XTAL16M, is when both the external 32kHz crystal is selected as low power clock (LP_CLK_32768) and it has explicitly defined not to wait for the settling of the crystal, that is, pm_set_wakeup_mode(false). The hardware blocks that require the XTAL16M to properly work are the BLE, USB and the UART peripheral.


  1. The next step is to create the task named system_init that is responsible for initializing the system (see the The Key Points of the System Initialization Function section). In this FreeRTOS task all the application tasks should be declared as well.
  2. After creating all the required FreeRTOS tasks, it’s time to start running the scheduler which is responsible for controlling and executing all the previous defined tasks, according to their priorities.

3.3. The Key Points of the System Initialization Function

  1. All the system clocks should be initialised according to the target application needs. In specific, the system clock, low power clock source, clock divider for the AMBA peripheral bus clock (APB) and AMBA high speed bus (AHB) are set.

Code Snippet:

/*
 * Prepare clocks. Note: cm_cpu_clk_set() and cm_sys_clk_set() can only be called from a
 * task since they will suspend the task until the XTAL16M has settled and the PLL maybe
 * locked.
 */

 cm_sys_clk_init(sysclk_XTAL16M); // Set the system clock
 cm_apb_set_clock_divider(apb_div1); // Set divider for the APB bus
 cm_ahb_set_clock_divider(ahb_div1); // Set divider for the AHB bus
 cm_lp_clk_init(); // Set the clock source of the low power clock

Note

The function which sets the low power clock does not have any input parameters. The desired clock source must be defined using the macro dg_configUSE_LP_CLK, preferably set in the /config/custom_config_qspi.h header file. The valid values this macro can accept is: LP_CLK_RCX (internal RC oscillator), LP_CLK_32768 (external 32 kHz crystal) or LP_LCK_32000 (external clock pulses). By default all code examples found in Dialog’s SDK use the external crystal 32 kHz as the LP clock, that is, #define dg_configUSE_LP_CLK LP_CLK_32768. Table 2 contains the system clock source enumerated type values.


Table 2 System Clock Types
Enumeration name Value Description
sysclk_RC16 0 Sets the internal RC oscillator of 16 MHz as the source clock.
sysclk_XTAL16M 1 Sets the external crystal of 16 MHz as the source clock.
sysclk_XTAL32M 2 Sets the external crystal of 32 MHz as the source clock.
sysclk_PLL48 3 Sets the PLL block at 48 MHz as the source clock.
sysclk_PLL96 6 Sets the PLL block at 96 MHz as the source clock.
sysclk_LP 255 Sets the low power clock as the source clock.

Note

If the external 32kHz crystal is selected as low power clock, the system is prevented from entering sleep for approximately 8 seconds after a HW reset (cold boot). This delay ensures the external crystal is settled before using it as a low power clock for the system. After that time delay, the device can enter a sleep mode. The low power clock (LP) is used during the device sleep. Also note that sysclk_LP cannot be used as system clock source.


  1. The next step is the initialization of the device’s peripherals used in the project, for example, the pins multiplexing for the UART interface. The state configurations are not retained during sleep and have to be restored at wake up. All device configurations should be placed in prvSetupHardware function. In this function pm_system_init routine is called as well. This function performs the actual device’s pin initialization after a power-up and a wake-up cycle.

  1. The next step is the selection of the sleep mode. Please note that the recommended sleep mode for all applications is the extended sleep mode.

Code Snippet:

/* Set the desired sleep mode */

pm_set_wakeup_mode(true); // If the input parameter is set to "true" then the device waits until XTAL16M is settled.
pm_set_sleep_mode(pm_mode_extended_sleep);

Note

After wake-up, the system runs using the internal 16 MHz (RC16) clock oscillator. The clock manager (CM) switches back to the last system clock configuration (before sleep) and the FreeRTOS resumes its tasks either immediately or after the external 16MHz crystal (XTAL16M) has settled. This procedure is transparent to the application tasks. The CPM unblocks any task that has been blocked, waiting for the high precision clock (external crystal 16 MHz). The hardware blocks of the device that require the XTAL16M to properly work are the Bluetooth (BLE), USB and the UART peripheral.


'Import sleep'

Fig. 15 Possible Device’s States


  1. After all the mandatory configurations, all the application tasks that will be executed by the FreeRTOS scheduler should be declared.

Note

The last code line of system_init task is OS_TASK_DELETE( OS_GET_CURRENT_TASK() ) for deleting the task itself. This is done in order to release valuable system resources. After all, the initialization of the system only needs to be done once at beginning of the device’s start.