2. Real-Time Clock Concept

This section explains the key features of the Real-Time Clock (RTC). It is sometimes necessary to accurately measure time, for instance you may need to measure the current time or how much time it takes for a routine to be executed. Advanced architectures often include a hardware block dedicated to such operations but this is not the case for all architectures including the DA1468x family of devices. For these architectures, an RTC must be implemented using an alternative approach. To do this, the SDK includes some APIs that can be used for measuring time with high accuracy.

2.1. OS Timers vs RTC

Usually, Real Time Operating Systems (RTOS) include timers that can be used for various RTOS-related operations. These timers are events with the Operating System’s (OS) tick granularity, thus their accuracy is not as good as the one of a hardware-implemented RTC. OS timers may also include delays caused by higher priority tasks or system interrupts. For these reasons, we recommend using the RTC-related APIs provided by the SDK if timestamping is required.

2.2. RTC Implementation

The RTC implementation measures the time, in low power (lp) clock cycles, that has elapsed since the device’s power up or hardware (HW) reset. This implies that the accuracy of the RTC functionality is the accuracy of the lp clock used. Using the number of the lp clock cycles that fit within a certain time interval and the period of each lp clock cycle, time can be easily computed using the following formula:

'Formula for converting lp clocks to time'

Fig. 1 Formula for Converting lp Clocks to Time

For instance, assuming the external crystal XTAL32K is selected as the lp clock and the RTC functionality has measured a value equal to 1000 lp clock cycles, time can be computed as follows:

'Formula for converting lp clocks to time'

Fig. 2 Formula for Converting lp Clocks to Time - Example

2.3. Header Files

The APIs related to RTC functionality can be found in sdk/cpm/include/sys_rtc.h. There are only two APIs that can be used by the developer. Table 1 briefly explains the available APIs (red indicates the path under which the files are stored, and green indicates which ones are used for RTC related operations).

'Headers for RTC implementation'

Fig. 3 Headers for RTC Implementaion.

Table 1 APIs for the RTC Functionality
API Name Description
rtc_get() This returns the current RTC time expressed in lp clock cycles, that is either XTAL32K or RCX.
rtc_get_fromISR() This has the same functionality as rtc_get(). It should be called from within an Interrupt Service Routine (ISR).

2.4. Supported Low Power Clocks

Fig. 4 illustrates the clocks that can be used as the lp clock when the device is in sleep mode. The device can also be clocked with an external digital clock in place of the external crystal. Currently, the SDK does not support the internal RC32K RC oscillator. For the DA1468x Pro DevKit, XTAL32K is the place where an external crystal of 32768 Hz has been be attached and RCX is the internal RC oscillator of the chip. At room temperature, the RC oscillator frequency is close to 10.5 kHz.

'Available sources for LP clocks in sleep mode'

Fig. 4 Available Sources for lp Clocks in Sleep Mode. Red indicates the sources that are supported by the SDK.

It is recommended that the customer uses the external crystal over the internal RC oscillator. The main reasons for this are stability and accuracy. In general, crystals are quite stable and feature more accuracy. Due to their nature, RC oscillators are vulnerable to both voltage and temperature variations. This implies that the RCX frequency changes over time depending on its surrounding. Thus calibrations need to be performed at regular time intervals.

Note

The RCX accuracy for the DA1468x family of devices is 500 ppm. This is enough for both BLE and non-BLE operations. To preserve the aforementioned accuracy, it is necessary for the RCX to be calibrated frequently.

When recording chip characterization information, the maximum time RCX can remain uncalibrated is 4 seconds at room temperature and the expected temperature dependent drift of the RCX is +/-50 ppm/oC.

The default SDK algorithm for RCX calibration assumes that there is a regular wake/sleep cycle of the device due to BLE activity. However, if there are either long sleep or long active periods, or, even worse, if the device is always active (for example, while plugged-in to a USB port and charging) then the RCX is not calibrated sufficiently and it will drift more than expected. This non-expected drifting of the RCX significantly affects the RCX accuracy and, in extreme cases, the stability of the BLE link. For such cases, request the RCX-patch from Dialog support which implements a more sophisticated RCX calibration approach including, adaptive to temperature ramping, RCX calibration.


The SDK comes with a few useful macros that should be used by the developer to compute the period of the lp clock used. These are useful for the RCX where its frequency is constantly changing. Table 2 briefly explains the most useful macros that can be used during RTC measurements.

Table 2 Useful Macros for RTC Measurements
Macro Name Description
configSYSTICK_CLOCK_HZ This macro returns the frequency of the selected lp clock. It should be used for all of the supported lp clocks.
rcx_clock_period This macro returns the period of the lp clock and should only be used for the RCX. Please note that the returned value is multiplied by (1024 * 1024) to increase accuracy.

Note

The calibration process of the RCX uses the external crystal XTAL16M as a reference clock. This means that if the XTAL16M is not trimmed, then this inaccuracy is reflected in the RCX calibration. Therefore, if the RCX is the selected lp clock source then its drift is also reflected in the RTC functionality.

2.5. Best Practices for Dealing with RTCs

2.5.1. Optimizing CPU Performance Using Integer Values

For devices that do not incorporate a Floating-Point Unit (FPU), it is good practice for the developer to use integer numbers as much as possible. Otherwise, it takes many clock cycles for the CPU to execute non-integer calculations and this has adverse consequences on the overall system’s performance. If possible, use integer numbers that are a power of two, for instance 2^10 = 1024 or (2^10 * 2^10 ) = (1024 * 1024). In fact, any multiplication which involves numbers that are a power of two, is interpreted by the compiler as a bit-shift operation to the left. Similarly, any division which involves numbers that are a power of two, is interpreted as a bit-shift operation to the right.

2.5.2. Optimizing CPU Performance Using Small Numbers

As mentioned, the RTC mechanism measures lp clock cycles on device power up or cold boot (HW reset). This implies that values returned by the rtc_get() function increase as time passes. Instead of measuring all the lp clock cycles every time an RTC measurement is performed, it is good practice to only use the lp clock cycles since the last RTC measurement. Fig. 5 illustrates this concept using a pair of variables named old and new respectively.

'Consecutive RTC measurements using the old-new scheme'

Fig. 5 Consecutive RTC Measurements Using the Old-New Scheme

The following code snippet demonstrates a possible RTC measurement routine using this old-new scheme:

__RETAINED static uint64_t sw_rtc_new = 0;
__RETAINED static uint64_t sw_rtc_old = 0;

void old_new_scheme(void) {

        uint64_t sw_rtc_dif = 0;

        /* Get the current RTC value expressed in ticks of the lp clock used. */
        sw_rtc_new = rtc_get();

        /*
         * To reduce values used in various calculations, calculate
         * only the lp clocks since the last RTC measurement.
         * (instead of invoking all the lp clock cycles measured
         * from the very beginning of chip's operation)
         */
        sw_rtc_dif = sw_rtc_new - sw_rtc_old;


        /*
         * The current RTC value becomes the old value
         * for the next RTC measurement.
         */
        sw_rtc_old = sw_rtc_new;
}

2.6. Converting lp Clocks to Time

The following code snippet implements all the aforementioned concepts, tips, and insights, and illustrates a routine that could be used for converting and translating lp clock cycles to time.

/*
 * Convert low power (lp) clocks into time expressed in microseconds.
 */
uint64_t sw_rtc_convert_lp_to_time(uint64_t lp_clocks)
{
        uint64_t time = 0;

        /*
         * Identify the lp clock used
         */
        if ((dg_configUSE_LP_CLK == LP_CLK_32768)
                         || (dg_configUSE_LP_CLK == LP_CLK_32000)) {

                /*
                 * Compute the period of the lp clock used
                 * and then multiply with 1024 to increase
                 * accuracy!
                 */
                const uint32_t lp_clk_period = ((1000000 * 1024)
                                                /configSYSTICK_CLOCK_HZ);

                /*
                 * Convert lp clocks into time
                 */
                time = (lp_clocks * (uint64_t)lp_clk_period);
                time = (time >> 10); // divide by 1024

        } else if (dg_configUSE_LP_CLK == LP_CLK_RCX) {

                /*
                 * Use [rcx_clock_period] to get the current RCX
                 * period in microseconds. Please note that this
                 * value is multiplied by (1024 * 1024).
                 */
                time = (lp_clocks * (uint64_t)rcx_clock_period);
                time = (time >> 20); // divide with (1024 * 1024)

        } else {

                OS_ASSERT(0); // Invalid lp clock source
        }

        return time;
}