6. Code Overview

This section provides the code blocks needed to successfully execute this tutorial.

6.1. Header Files

In main.c, add the following header files:

/*
 * Required header files for the main.c source file
 */

#include "hw_wkup.h"
#include "sw_rtc_date_time.h"
#include "alert_mechanism.h"

6.2. System Init Code

In main.c, replace system_init() with the following code:

/* Task priorities */
#define mainRTC_TASK_PRIORITY    ( OS_TASK_PRIORITY_NORMAL )
#define mainALERT_TASK_PRIORITY  ( OS_TASK_PRIORITY_NORMAL )


/******** Notification Bitmasks *************/
#define SW_RTC_TRIG_NOTIF   (1 << 1)


/*
 * Macro for outputting the low power (lp) clock on P3.0 pin.
 * 1: configures P3.0 pin to output the lp clock.
 * 0: nothing
 *
 * \note: This macro is intended for debugging purposes only in order to measure
 *        the accuracy of the lp clock used. The device should stay active for
 *        as long as measurements on P3.0 pin are performed.
 */
#define TEST_LP_CLOCK_OUTPUT 0


/*
 * Functions prototypes
 */
void prvRTC_Task( void *pvParameters );
void prvALERT_Task( void *pvParameters );

extern void time_alert_led_blink_stop(void);


/* Task handlers */
static OS_TASK task_rtc_h   = NULL;
static OS_TASK task_alert_h = NULL;


static void system_init( void *pvParameters )
{
        OS_TASK task_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


#if (TEST_LP_CLOCK_OUTPUT == 1)
        /* Set the device to stay in active mode. */
        pm_set_sleep_mode(pm_mode_active);

        /*
         * Select which clock to map when PID = FUNC_CLOCK.
         *
         * Valid values are:
         * [0x00]: When XTAL32K is the selected lp clock
         * [0x02]: When RCX is the selected lp clock
         */
        REG_SETF(GPIO, GPIO_CLK_SEL, FUNC_CLOCK_SEL, 0x00);
#else
        /* Set the desired sleep mode. */
        pm_set_sleep_mode(pm_mode_extended_sleep);
#endif


     /* 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);


        /*
         * Task responsible for performing RTC related
         * operations
         */
        OS_TASK_CREATE( "RTC",

                        prvRTC_Task,
                        NULL,
                        300 * OS_STACK_WORD_SIZE,

                        mainRTC_TASK_PRIORITY,
                        task_rtc_h );
        OS_ASSERT(task_rtc_h);


        /*
         * Task responsible for performing alert related
         * operations
         */
        OS_TASK_CREATE( "ALERT",

                        prvALERT_Task,
                        NULL,
                        300 * OS_STACK_WORD_SIZE,

                        mainALERT_TASK_PRIORITY,
                        task_alert_h );
        OS_ASSERT(task_alert_h);

        /* The work of the SysInit task is done */
        OS_TASK_DELETE( xHandle );

}

6.3. Wake-Up Timer Code

In main.c, add the following code for handling external events, after periph_init().

/*
 * 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-defined
         * interrupt callback, to clear the interrupt flag.
         */
        hw_wkup_reset_interrupt();

       /*
        * Notify the [prvRTC_Task] task that time for performing an RTC operation has elapsed
        */
        OS_TASK_NOTIFY_FROM_ISR(task_rtc_h, SW_RTC_TRIG_NOTIF,
                                              OS_NOTIFY_SET_BITS);

        /*
         * Disable breath timer and resume the device to its
         * initial state, that is extended sleep mode!
         */
        time_alert_led_blink_stop();

}

/*
 * 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 on a pin 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);
}

6.4. Hardware Initialization

In main.c, replace both periph_init() and prvSetupHardware() with the following code for configuring pins after power-up/wake-up:

/**
 * @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);

#if (TEST_LP_CLOCK_OUTPUT == 1)
        // Output the selected lp clock on P3.0 pin - Intended for debugging purposes only.
        hw_gpio_set_pin_function(HW_GPIO_PORT_3, HW_GPIO_PIN_0, HW_GPIO_MODE_OUTPUT,
                        HW_GPIO_FUNC_CLOCK);
#endif

}

/**
 * @brief Hardware Initialization
 */
static void prvSetupHardware( void )
{
        /* Init hardware */
        pm_system_init(periph_init);
        init_wkup();
}

6.5. Task Code for RTC Operations

Code snippet of prvRTC_Task task responsible for performing RTC related operations. Create a new source file, for example sw_rtc_task.c and add the following code:

#include <string.h>
#include <stdio.h>

#include "sw_rtc_date_time.h"
#include "osal.h"


/******** Notification Bitmasks *************/
#define SW_RTC_TRIG_NOTIF        (1 << 1)


/*
 * Macro for enabling/disabling debugging messages on the serial console:
 *
 * 1: enables  debugging messages
 * 0: disables debugging messages
 */
#define SW_RTC_DBG_MESSAGES_CONSOLE   1


 /*
  * Structure that holds date/time related info
  */
__RETAINED_RW sw_rtc_date_time_t date_time = {
        /*
         * Declare all the info related to date.
         *
         * \note: The weekday is calculated automatically by the app.
         *
         * \note: The app performs sanity checks to identify values out of boundaries.
         *        For any invalid value, the code gets stuck in assertions!
         *
         */
        .date = {
                .year       = 2018,
                .month      = 9,
                .month_date = 9,
        },
        /*
         * Declare all the info related to time.
         *
         * \note: The max time resolution is microseconds (us) */
        .time = {
                .hour = 0,
                .min  = 0,
                .sec  = 0,
        }
};



/*
 * Task responsible for performing RTC operations
 */
void prvRTC_Task( void *pvParameters )
{
        char calendar_str[60];

        /*
         * Initialize the RTC mechanism. This should be the first invoked
         * function before any other related SW RTC API.
         */
        sw_rtc_init();


        /* Set the current date */
        sw_rtc_date_write(&date_time.date);

        /* Set the current time */
        sw_rtc_time_write(&date_time.time);

        /*
         * Instead of declaring date/time individually, sw_rtc_date_time_write()
         * can be used to set both of them at once:
         */


        memset((uint8_t *)&date_time, 0x00, sizeof(sw_rtc_date_time_t));

        for(;;) {

                OS_BASE_TYPE ret;
                uint32_t notif;

                /*
                 * Block forever waiting for any of the notification bits,
                 * then clear them all. In this example at every K1 button
                 * press (on pro DevKit)
                 */
                ret = OS_TASK_NOTIFY_WAIT(0, OS_TASK_NOTIFY_ALL_BITS,
                                                  &notif, OS_TASK_NOTIFY_FOREVER);

                /*
                 * Thus, the return value must always be OS_OK
                 */
                OS_ASSERT(ret == OS_OK);

                /*
                 * Has time for updating the calendar elapsed?
                 */
                if (notif & SW_RTC_TRIG_NOTIF) {

                      /* Read the current date */
                      sw_rtc_date_read(&date_time.date);

                      /* Read the current time */
                      sw_rtc_time_read(&date_time.time);


                      /*
                       * Instead of reading date/time individually, sw_rtc_date_time_read()
                       * can be used to read both of them at once:
                       */


                      /* If enabled, print the current date/time on the serial console */
                      if (SW_RTC_DBG_MESSAGES_CONSOLE) {

                              /* Clear the buffer */
                              memset(calendar_str, '\0', sizeof(calendar_str));

                              /*
                               * Prepare the debugging message (current date/time)
                               */
                              sprintf(calendar_str, "DATE: %lu-%lu-%lu (week day: %lu) TIME: %lu-%lu-%lu-%lu\n\r",
                                      date_time.date.month_date, date_time.date.month, date_time.date.year, date_time.date.week_date,
                                      date_time.time.hour,       date_time.time.min,   date_time.time.sec,  date_time.time.msec );


                              /* Send the message on the serial console */
                              printf("%s", calendar_str);
                              fflush(stdout);
                      }

                }

      } // end of main for(;;) loop
} // end of task

6.6. Task Code for Alert Operations

Code snippet of prvALERT_Task task responsible for performing alert related operations. Create a new source file, for example alert_task.c and add the following code:

#include <string.h>
#include <stdio.h>

#include "alert_mechanism.h"
#include "osal.h"

#include <stdbool.h>

#include "sys_power_mgr.h"
#include "hw_led.h"
#include "hw_breath.h"

#include "sw_rtc_date_time.h"



/* Handle of [prvALERT_Task] task */
__RETAINED_RW OS_TASK alert_task_h = NULL;

/******** Notification Bitmasks *************/
#define TIME_ALERT_TRIG_NOTIF_H  (1 << 1)
#define TIME_ALERT_TRIG_NOTIF_M  (1 << 2)
#define TIME_ALERT_TRIG_NOTIF_S  (1 << 3)


/*
 * Macro for enabling/disabling debugging messages on the serial console:

 * 1: enables  debugging messages
 * 0: disables debugging messages
 */
#define TIME_ALERT_DBG_MESSAGES_CONSOLE    1

/*
 * Macro for enabling/disabling blinking functionality upon an alert event:

 * 1: enables  blinking functionality
 * 0: disables blinking functionality
 */
#define TIME_ALERT_BREATH_TIMER_EN         0



/*
 * Keep track of device state (active/sleep)
 */
__RETAINED_RW static bool old_alerting = false;

/* Function prototypes */
void time_alert_set_mode(bool new_alerting);



/*
 * Turn on LED1 on Pro DevKit (blinking functionality).
 * LED1 is driven by breath timer.
 */
void time_alert_led_blink_start(breath_config *breath_cfg)
{
        /* Force the device to stay in active mode */
        time_alert_set_mode(true);

        /* Configure the breath timer */
        hw_breath_init(breath_cfg);

        /* Configure the white LED1 */
        hw_led_set_led1_src(HW_LED_SRC1_BREATH);
        hw_led_enable_led1(true);

        /* Enable the breath timer */
        hw_breath_enable();
}


/* Turn off LED1 on Pro DevKit */
void time_alert_led_blink_stop(void)
{
        /* Resume device to its initial state */
        time_alert_set_mode(false);

        /* Disable breath timer */
        hw_breath_disable();
}


/*
 * Check whether the device should be forced to stay
 * awake or it's time to resume to its initial state.
 *
 * \note Number of calls to pm_stay_alive() must match the
 *       number of calls to pm_resume_sleep()!!!!!!!
 */
void time_alert_set_mode(bool new_alerting)
{
        /* First check if a new alert flag has been assigned */
        if (old_alerting != new_alerting) {

                /* If flag is set to true then force the device to stay alive */
                if (new_alerting) {
                        pm_stay_alive();

                /*
                 * Otherwise the new value is zero and thus the device should
                 * resume to its initial state.
                 */
                } else {
                        pm_resume_sleep();
                }

                /* Make a copy of the new alert flag */
                old_alerting = new_alerting;
        }
}


/*
 * Structure that holds alert related parameters
 *
 * \note: There are three distinct alert classes which can be
 *        enabled-disabled-configured individually.
 *
 */
__RETAINED_RW time_alert_t my_alert = {
        .hour     = 4,   // Set alerts expressed in hours   (every x hours  ).
        .min      = 2,   // Set alerts expressed in minutes (every x minutes).
        .sec      = 5,   // Set alerts expressed in seconds (every x seconds).

        .repeat_h = false,  // If set to false, alerts are triggered once, repeatedly otherwise.
        .repeat_m = true,   // If set to false, alerts are triggered once, repeatedly otherwise.
        .repeat_s = false,  // If set to false, alerts are triggered once, repeatedly otherwise.
};


/*
 * User-defined callback function, called upon an alert event.
 * It should have that specific prototype!
 *
 * \note: All the alert classes will call the same callback. The \p source parameter
 *        should be used to identify the source of the event.
 *
 */
void my_alert_cb(uint8_t source)
{

        /*
         * Check whether Hours Alert Class triggered the alert.
         * Then notify the main task accordingly.
         */
        if (source & ALERT_SOURCE_HOURS_Msk) {

                OS_TASK_NOTIFY(alert_task_h, TIME_ALERT_TRIG_NOTIF_H,
                                                          OS_NOTIFY_SET_BITS);
        }

        /*
         * Check whether Minutes Alert Class triggered the alert.
         * Then notify the main task accordingly.
         */
        if (source & ALERT_SOURCE_MINUTES_Msk) {

                OS_TASK_NOTIFY(alert_task_h, TIME_ALERT_TRIG_NOTIF_M,
                                                          OS_NOTIFY_SET_BITS);
        }

        /*
         * Check whether Seconds Alert Class triggered the alert.
         * Then notify the main task accordingly.
         */
        if (source & ALERT_SOURCE_SECONDS_Msk) {

                OS_TASK_NOTIFY(alert_task_h, TIME_ALERT_TRIG_NOTIF_S,
                                                          OS_NOTIFY_SET_BITS);
        }
}


/*
 * Task responsible for performing alert operations.
 */
void prvALERT_Task( void *pvParameters )
{

        /* Get the handler of the current task */
        alert_task_h = OS_GET_CURRENT_TASK();


        /*
         * Initialize the alert mechanism. This function should be invoked
         * before any other related API.
         */
        time_alert_init();


        /*
         * Register callback function that will be called upon an alert event.
         */
        time_alert_register_cb(my_alert_cb);


        /*
         * Set alerts, expressed in minutes.
         *
         * \note If repeat flag is set to true, alerts are triggered every x minutes
         *
         * \note If a new alert configuration is performed before the execution of the current alert,
         *       the old alert will be discarded and the new one will be taken into consideration.
         *
         * \note At any time, alerts can be disabled by calling time_alert_class_clear()
         *
         */
        time_alert_class_set(my_alert.min, my_alert.repeat_m, SET_ALERT_CLASS_Min);


        /*
         * Set alerts expressed in hours. Configure an alert to be triggered after 1 hour.
         *
         * \note If repeat flag is set to true, alerts are triggered every x hours
         *
         * \note If a new alert configuration is performed before the execution of the current alert,
         *       the old alert will be discarded and the new one will be taken into consideration.
         *
         * \note At any time, alerts can be disabled by calling time_alert_class_clear()
         *
         */
        time_alert_class_set(my_alert.hour, my_alert.repeat_h, SET_ALERT_CLASS_Hour);


        /*
         * Set alerts expressed in seconds
         *
         * \note If repeat flag is set to true, alerts are triggered every x seconds
         *
         * \note If a new alert configuration is performed before the execution of the current alert,
         *       the old alert will be discarded and the new one will be taken into consideration.
         *
         * \note At any time, alerts can be disabled by calling time_alert_class_clear()
         *
         */
        time_alert_class_set(my_alert.sec, my_alert.repeat_s, SET_ALERT_CLASS_Sec);


#if (TIME_ALERT_BREATH_TIMER_EN == 1)
        /*
         * Breath timer parameters
         */
        breath_config breath_cfg = {
                .dc_min   = 0,
                .dc_max   = 255,
                .freq_div = 255,
                .dc_step  = 96,
                .polarity = HW_BREATH_PWM_POL_POS
        };
#endif

        for (;;) {

                OS_BASE_TYPE ret;
                uint32_t notif;

                /*
                 * Block forever waiting for any of the notification bits,
                 * then clear them all
                 */
                ret = OS_TASK_NOTIFY_WAIT(0, OS_TASK_NOTIFY_ALL_BITS,
                                           &notif, OS_TASK_NOTIFY_FOREVER);

                /*
                 * Thus, the return value must always be OS_OK
                 */
                OS_ASSERT(ret == OS_OK);


                /*
                 * This code block is executed upon an alert event
                 * produced by Hours Alert Class.
                 */
                if (notif & TIME_ALERT_TRIG_NOTIF_H) {

#if (TIME_ALERT_BREATH_TIMER_EN == 1)
                        /* Start blinking white LED D1 on pro DevKit */
                        time_alert_led_blink_start(&breath_cfg);
#endif

                        /* Print out a debugging message */
                        if (TIME_ALERT_DBG_MESSAGES_CONSOLE) {
                                printf("The source of the alert is: 'HOURS'\n\r");
                        }
                }


                /*
                 * This code block is executed upon an alert event
                 * produced by Minutes Alert Class.
                 */
                if (notif & TIME_ALERT_TRIG_NOTIF_M) {

#if (TIME_ALERT_BREATH_TIMER_EN == 1)
                        /* Start blinking white LED D1 on pro DevKit */
                        time_alert_led_blink_start(&breath_cfg);
#endif

                        /* Print out a debugging message */
                        if (TIME_ALERT_DBG_MESSAGES_CONSOLE) {
                                printf("The source of the alert is: 'MINUTES'\n\r");
                        }
                }


                /*
                 * This code block is executed upon an alert event
                 * produced by Seconds Alert Class.
                 */
                if (notif & TIME_ALERT_TRIG_NOTIF_S) {

#if (TIME_ALERT_BREATH_TIMER_EN == 1)
                        /* Start blinking white LED D1 on pro DevKit */
                        time_alert_led_blink_start(&breath_cfg);
#endif


                        /* Print out a debugging message */
                        if (TIME_ALERT_DBG_MESSAGES_CONSOLE) {
                                printf("The source of the alert is: 'SECONDS'\n\r");
                        }

                }
        }
}

6.7. Date/Time Implementation Source File

Code snippet of date/time mechanism implementation. Create a new source file, for example sw_rtc_date_time.c and add the following code:

#include "sw_rtc_date_time.h"

#include <stdbool.h>
#include <stdlib.h>

#include <math.h>
#include "osal.h"

#include "sys_rtc.h"
#include "sys_power_mgr.h"

#include <string.h>
#include <stdio.h>



/* Months of a year */
typedef enum  {
        JANUARY   = 1,
        FEBRUARY  = 2,
        MARCH     = 3,
        APRIL     = 4,
        MAY       = 5,
        JUNE      = 6,
        JULY      = 7,
        AUGUST    = 8,
        SEPTEMBER = 9,
        OCTOBER   = 10,
        NOVEMBER  = 11,
        DECEMBER  = 12
} SW_RTC_MONTH;


/*
 * Set this macro to perform a forced RTC measurement via an OS Timer.
 */
#define SW_RTC_FORCED_DATE_TIME_EN    1


/* Mutex handle */
__RETAINED_RW static OS_MUTEX sw_rtc_mutex = NULL;


/*
 * Time interval, expressed in milliseconds, for triggering a forced RTC operation.
 * This macro is used only when SW_RTC_FORCED_DATE_TIME_EN is set. Default value is
 * 1 day, that is:
 *
 * (24 hrs * 60 minutes/hour * 60 seconds/minute * 1000 milliseconds/second) = 86,400,000
 *
 */
#define SW_RTC_FORCED_DATE_TIME_IN_MS   86400000


/*
 * Variables used to store the lp clocks, using the old/new scheme
 */
__RETAINED_RW static uint64_t sw_rtc_new = 0;
__RETAINED_RW static uint64_t sw_rtc_old = 0;


/*
 * SW RTC parameters. This structure holds the current date/time.
 */
__RETAINED_RW static sw_rtc_date_time_t *calendar;



#if (SW_RTC_FORCED_DATE_TIME_EN == 1)

/* OS timer handle */
static OS_TIMER sw_rtc_timer_h;


/* Callback function called after OS timer's expiration */
void sw_rtc_trg_cb(OS_TIMER timer)
{
        OS_BASE_TYPE ret;

        /*
         * Try to get the mutex without blocking. If the mutex was successfully acquired then
         * no other tasks perform RTC measurements. If not, another task is already performing
         * RTC operations and it's OK to abort the operation.
         *
         * \warning Blocking within timer's callback is not permitted!!!
         *
         */
        ret = OS_MUTEX_GET(sw_rtc_mutex, OS_MUTEX_NO_WAIT);

        if (ret == OS_MUTEX_TAKEN) {
                /* Allocate memory both date/time */
                sw_rtc_date_time_t *date_time = (sw_rtc_date_time_t *) OS_MALLOC(sizeof(sw_rtc_date_time_t));

                /* Just perform a read date/time operation */
                sw_rtc_date_time_read(date_time);

                /* Release the previously allocated memory */
                OS_FREE(date_time);

                /* Release the already acquired mutex */
                OS_MUTEX_PUT(sw_rtc_mutex);

        }
}

#endif


/*
 * Initialize the SW RTC mechanism. First, memory for the SW RTC parameters
 * is allocated and then an OS timer responsible for performing forced RTC
 * operations is created.
 *
 */
void sw_rtc_init()
{
        /* Allocate memory for the calendar (date/time) structure */
        calendar = (sw_rtc_date_time_t *) OS_MALLOC(sizeof(sw_rtc_date_time_t));
        OS_ASSERT(calendar);

        /* Clear the calendar structure */
        memset((uint8_t *)calendar, 0x00, sizeof(sw_rtc_date_time_t));


#if (SW_RTC_FORCED_DATE_TIME_EN == 1)

        uint64_t timer_ticks = (((uint64_t)SW_RTC_FORCED_DATE_TIME_IN_MS * configTICK_RATE_HZ) / 1000);

        /*
         * Create an OS timer to trigger a forced RTC operation.
         */
        sw_rtc_timer_h = OS_TIMER_CREATE("RTC_Trigger",
                                (uint32_t)timer_ticks, OS_TIMER_SUCCESS,
                                                   (void *) OS_GET_CURRENT_TASK(), sw_rtc_trg_cb);
        OS_ASSERT(sw_rtc_timer_h);

        /* Start the OS timer */
        OS_TIMER_START(sw_rtc_timer_h, OS_TIMER_FOREVER);
#endif

        /* Create mutex for RTC operations */
        OS_MUTEX_CREATE(sw_rtc_mutex);
        OS_ASSERT(sw_rtc_mutex != NULL);

}


/* Reset calendar values to zero */
void sw_rtc_date_time_reset(void)
{
        /* Reset all the calendar related parameters */
        sw_rtc_new = 0;
        sw_rtc_old = 0;

        /* Reset date parameters */
        calendar->date.year       = 0;
        calendar->date.month      = 1;
        calendar->date.month_date = 1;

        /* Reset time parameters */
        calendar->time.hour = 0;
        calendar->time.min  = 0;
        calendar->time.sec  = 0;
        calendar->time.msec = 0;
        calendar->time.usec = 0;
}


/*
 * Get the number of days of a month.
 */
uint8_t sw_rtc_get_days_of_month(void)
{
        uint8_t days = 0;

        /* Given the current month, calculate its maximum days */
        switch(calendar->date.month) {

        case JANUARY:
                days = 31;
                break;
        case FEBRUARY:
                /*
                 * Check if the year is a leap year, taking into consideration
                 * the following three criteria:
                 * 1. the year can be evenly divided by 4
                 * 2. the year is divisible by 400
                 * 3. the year is not divisible by 100
                 */
                if ((!(calendar->date.year % 4)) ||
                        ((!(calendar->date.year % 400)) && (calendar->date.year % 100))) {
                        days = 29;
                } else {
                        days = 28;
                }
                break;
        case MARCH:
                days = 31;
                break;
        case APRIL:
                days = 30;
                break;
        case MAY:
                days = 31;
                break;
        case JUNE:
                days = 30;
                break;
        case JULY:
                days = 31;
                break;
        case AUGUST:
                days = 31;
                break;
        case SEPTEMBER:
                days = 30;
                break;
        case OCTOBER:
                days = 31;
                break;
        case NOVEMBER:
                days = 30;
                break;
        case DECEMBER:
                days = 31;
                break;
        default:
                OS_ASSERT(0); // Invalid value!
        }

        return days;
}




/* Check whether the given year is a leap year */
bool sw_rtc_is_leap_year(void)
{
        /*
         * Check if the year is a leap year, taking into consideration
         * the following three criteria:
         * 1. the year can be evenly divided by 4
         * 2. the year is divisible by 400
         * 3. the year is not divisible by 100
         */
        if ((!(calendar->date.year % 4)) ||
                ((!(calendar->date.year % 400)) && (calendar->date.year % 100))) {
                return true;
        } else {
                return false;
        }
}


/* Calculate the month code used in weekday computations */
uint8_t sw_rtc_compute_month_code(void)
{
        uint8_t month_code;

        switch(calendar->date.month) {

        case JANUARY:
                month_code = 0;
                break;
        case FEBRUARY:
                month_code = 3;
                break;
        case MARCH:
                month_code = 3;
                break;
        case APRIL:
                month_code = 6;
                break;
        case MAY:
                month_code = 1;
                break;
        case JUNE:
                month_code = 4;
                break;
        case JULY:
                month_code = 6;
                break;
        case AUGUST:
                month_code = 2;
                break;
        case SEPTEMBER:
                month_code = 5;
                break;
        case OCTOBER:
                month_code = 0;
                break;
        case NOVEMBER:
                month_code = 3;
                break;
        case DECEMBER:
                month_code = 5;
                break;
        default:
                OS_ASSERT(0); // Invalid value!
        }

        return month_code;
}

/* Calculate the century code used in weekday computations */
uint8_t sw_rtc_compute_century_code(void)
{
        uint8_t century_code = 0;

        if (calendar->date.year >= 2000 && calendar->date.year < 2100) {
                century_code = 6;
        } else if (calendar->date.year >= 2100 && calendar->date.year < 2200) {
                century_code = 4;
        } else if (calendar->date.year >= 2200 && calendar->date.year < 2300) {
                century_code = 2;
        } else if (calendar->date.year >= 2300 && calendar->date.year < 2400) {
                century_code = 0;
        }

        return century_code;
}

/* Function responsible for computing the current day of the week */
uint32_t sw_rtc_compute_date_of_week(void)
{
      /* Isolate the last two digits from the year e.g. 2018 -> 18 */
      uint8_t year_code = (uint8_t)(calendar->date.year % 100);

      /* Compute the year code */
      year_code = ((year_code + (year_code / 4)) % 7);

      /* Compute the month code */
      uint8_t month_code = sw_rtc_compute_month_code();

      /* Compute the century code */
      uint8_t centure_code = sw_rtc_compute_century_code();

      /* Check whether the year is a leap year */
      uint8_t leap_year = (uint8_t)sw_rtc_is_leap_year();

      /* Add all the codes */
      uint8_t day_week = (year_code + month_code + centure_code + (uint8_t)calendar->date.month_date);


      if (leap_year && ((calendar->date.month == JANUARY) || (calendar->date.month == FEBRUARY)) ) {
              return (uint32_t)((day_week - 1) % 7);
      } else {
              return (uint32_t)(day_week % 7);
      }

}



/*
 * Function responsible for converting time, expressed in microseconds (us), into full calendar (date/time).
 */
void sw_rtc_convert_time_to_calendar(uint64_t usec_time)
{
        calendar->time.usec += usec_time;

        /*
         * Check if there is an overflow in [microseconds],
         * that is greater than 999.
         */
        if (calendar->time.usec > 999) {
                /* Compute the right next greater unit, that is milliseconds */
                calendar->time.msec += (uint32_t)(calendar->time.usec / 1000);

                /* Then compute the new value for [microseconds] */
                calendar->time.usec %= 1000;

                /*
                 * Check if there is an overflow in [milliseconds],
                 * that is greater than 999.
                 */
                if (calendar->time.msec > 999) {
                        /* Compute the right next greater unit, that is [seconds] */
                        calendar->time.sec += (calendar->time.msec / 1000);

                        /* Then compute the new value for [milliseconds] */
                        calendar->time.msec %= 1000;

                        /*
                         * Check if there is an overflow in [seconds],
                         * that is greater than 59
                         */
                        if (calendar->time.sec > 59) {
                                /* Compute the right next greater unit, that is [minutes] */
                                calendar->time.min += (calendar->time.sec / 60);

                                /* Then compute the new value for [seconds] */
                                calendar->time.sec %= 60;

                                /*
                                 * Check is there is an overflow in [minutes],
                                 * that is greater than 59
                                 */
                                if (calendar->time.min > 59) {
                                        /* Compute the right next greater unit, that is [hours] */
                                        calendar->time.hour += (calendar->time.min / 60);

                                        /* Then compute the new value for [minutes] */
                                        calendar->time.min %= 60;

                                        /*
                                         * Check if there is an overflow in hours,
                                         * that is greater than 23
                                         */
                                        if (calendar->time.hour > 23) {
                                                /* Compute the right next greater unit, that is [days] */
                                                calendar->date.month_date += (calendar->time.hour / 24);

                                                /* Then compute the new value for [hours] */
                                                calendar->time.hour %= 24;

                                                /*
                                                 * Check if there is an overflow in days,
                                                 * that is greater than 28/29/30 or 31
                                                 * depending on the current month.
                                                 */
                                                if (calendar->date.month_date >
                                                                sw_rtc_get_days_of_month()) {

                                                        /* Calculate months and days */
                                                        do {
                                                                calendar->date.month_date -= sw_rtc_get_days_of_month();
                                                                calendar->date.month++;

                                                                /*
                                                                 * Check if there is an overflow in [months],
                                                                 * that is greater than 12
                                                                 */
                                                                if (calendar->date.month > 12) {
                                                                        calendar->date.year += (calendar->date.month / 12);
                                                                        calendar->date.month %= 12;
                                                                }

                                                        } while (calendar->date.month_date > sw_rtc_get_days_of_month());


                                                } // end of days monitoring
                                        } // end of hours monitoring
                                } // end of minutes monitoring
                        } // end of seconds monitoring
                } // end of milliseconds monitoring
        } // end of microseconds monitoring

        /* Finally compute the day of the week */
        calendar->date.week_date = sw_rtc_compute_date_of_week();

} // end of function




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

        /*
         * Check whether the lp clock source is either the external crystal XTAL32K
         * or an external clock source.
         */
        if ((dg_configUSE_LP_CLK == LP_CLK_32768) ||
                             (dg_configUSE_LP_CLK == LP_CLK_32000)) {

                /*
                 * Compute the period of the lp clock source - expressed in microseconds (us)
                 *
                 * \note Multiply by [1024] to increase accuracy!
                 *
                 */
                const uint32_t lp_clk_period = ((1000000UL * 1024)
                                                /configSYSTICK_CLOCK_HZ);

                /*
                 * Compute the time that has elapsed.
                 */
                time = (lp_clocks * (uint64_t)lp_clk_period);
                time = (time >> 10); // divide with 1024

       /* Check whether the lp clock source is the internal RCX */
        } else if (dg_configUSE_LP_CLK == LP_CLK_RCX) {

                /*
                 * Use [rcx_clock_period] to get the current RCX period in usec.
                 * 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;
}



/*
 * \callgraph
 *
 * \brief This function performs a SW RTC measurement using APIs from the RTC mechanism
 *        (provided by the SDK), to calculate the number of low power clock cycles that
 *        fit within a given time interval.
 */
void sw_rtc_date_time_read(sw_rtc_date_time_t *val)
{

        uint64_t sw_rtc_value_us = 0;
        uint64_t sw_rtc_dif = 0;


        /* Get the current RTC value expressed in lp clock ticks. */
        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;

#if (DBG == 1)
        printf("RTC diff: %ld\r\n", (uint32_t)sw_rtc_dif);
#endif


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


        /* Convert the lp clock cycles into [microseconds]  */
        sw_rtc_value_us = sw_rtc_convert_lp_to_time(sw_rtc_dif);


        /* Update the calendar using the current time in [microseconds]  */
        sw_rtc_convert_time_to_calendar(sw_rtc_value_us);


        /* Safeguard the structure that holds the current date/time */
        OS_MUTEX_GET(sw_rtc_mutex, OS_MUTEX_FOREVER);

        /* Copy the current date/time */
        memcpy((uint8_t *)val, (uint8_t *)calendar, sizeof(sw_rtc_date_time_t));

        /* Release the previously acquired mutex so that other tasks can use it e.g the daemon task */
        OS_MUTEX_PUT(sw_rtc_mutex);
}


/*
 * Function that performs an SW RTC measurement and returns the current date.
 */
void sw_rtc_date_read(sw_rtc_date_t *val)
{

        /* Allocate memory to hold both date and time */
        sw_rtc_date_time_t *date_time = (sw_rtc_date_time_t *) OS_MALLOC(sizeof(sw_rtc_date_time_t));
        OS_ASSERT(date_time);

        /* Read the current date/time */
        sw_rtc_date_time_read(date_time);

        /* Safeguard the structure that holds the current date/time */
        OS_MUTEX_GET(sw_rtc_mutex, OS_MUTEX_FOREVER);

        /* Copy the current date */
        memcpy((uint8_t *)val, (uint8_t *)&date_time->date, sizeof(sw_rtc_date_t));

        /* Release the previously acquired mutex so that other tasks can use it e.g the daemon task */
        OS_MUTEX_PUT(sw_rtc_mutex);


        OS_FREE(date_time);
}



/*
 * Function that performs an SW RTC measurement and returns the current time.
 */
void sw_rtc_time_read(sw_rtc_time_t *val)
{

        /* Allocate memory to hold both date and time */
        sw_rtc_date_time_t *date_time = (sw_rtc_date_time_t *) OS_MALLOC(sizeof(sw_rtc_date_time_t));
        OS_ASSERT(date_time);

        /* Read the current date/time */
        sw_rtc_date_time_read(date_time);

        /* Safeguard the structure that holds the current date/time */
        OS_MUTEX_GET(sw_rtc_mutex, OS_MUTEX_FOREVER);

        /* Copy the current time  */
        memcpy((uint8_t *)val, (uint8_t *)&date_time->time, sizeof(sw_rtc_time_t));

        /* Release the previously acquired mutex so that other tasks can use it e.g the daemon task */
        OS_MUTEX_PUT(sw_rtc_mutex);


        OS_FREE(date_time);
}


/* Sanity checks for the date structure */
int sw_rtc_date_write_sanity_check(sw_rtc_date_t *val)
{
        if ((val->month_date > 31) || (val->month_date == 0) || (val->month > 12) || (val->month == 0) || (val->week_date > 6)) {
                return -1;
        } else {
                return 0;
        }
}


/* Sanity checks for the time structure */
int sw_rtc_time_write_sanity_check(sw_rtc_time_t *val)
{
        if ((val->hour > 23) || (val->min > 59) || (val->sec > 59) || (val->msec > 999) || (val->usec > 999))  {
                return -1;
        } else {
                return 0;
        }
}


/*
 * Function responsible for setting the current date and time
 */
void sw_rtc_date_time_write(sw_rtc_date_time_t *val)
{
        int error_date;
        int error_time;


        /**
         * Perform sanity checks on the provided values.
         *
         * \note If the returned value is -1 then the provided values are out of the boundaries
         *
         */
        error_date = sw_rtc_date_write_sanity_check(&val->date);
        error_time = sw_rtc_time_write_sanity_check(&val->time);

        OS_ASSERT((error_date == 0) && (error_time == 0));


        /* Safeguard the structure that holds the current date/time */
        OS_MUTEX_GET(sw_rtc_mutex, OS_MUTEX_FOREVER);

        /* Update date/time parameters */
        memcpy((uint8_t *)calendar, (uint8_t *)val, sizeof(sw_rtc_date_time_t));

        /* Release the previously acquired mutex so that other tasks can use it e.g the daemon task */
        OS_MUTEX_PUT(sw_rtc_mutex);
}




/*
 * Function responsible for setting the current date
 */
void sw_rtc_date_write(sw_rtc_date_t *val)
{
        int error;


        /**
         * Perform sanity checks on the provided values.
         *
         * \note If the returned value is -1 then the provided values are out of the boundaries
         *
         */
        error = sw_rtc_date_write_sanity_check(val);
        OS_ASSERT(error == 0);

        /* Safeguard the structure that holds the current date */
        OS_MUTEX_GET(sw_rtc_mutex, OS_MUTEX_FOREVER);

        /* Update date parameters */
        memcpy((uint8_t *)&calendar->date, (uint8_t *)val, sizeof(sw_rtc_date_t));

        /* Release the previously acquired mutex so that other tasks can use it e.g the daemon task */
        OS_MUTEX_PUT(sw_rtc_mutex);
}


/*
 * Function responsible for setting the current time
 */
void sw_rtc_time_write(sw_rtc_time_t *val)
{
        int error;


        /**
         * Perform sanity checks on the provided values.
         *
         * \note If the returned value is -1 then the provided values are out of  boundaries
         *
         */
        error = sw_rtc_time_write_sanity_check(val);
        OS_ASSERT(error == 0);


        /* Safeguard the structure that holds the current time */
        OS_MUTEX_GET(sw_rtc_mutex, OS_MUTEX_FOREVER);

        /* Update time parameters */
        memcpy((uint8_t *)&calendar->time, (uint8_t *)val, sizeof(sw_rtc_time_t));

        /* Release the previously acquired mutex so that other tasks can use it e.g the daemon task */
        OS_MUTEX_PUT(sw_rtc_mutex);
}

6.8. Date/Time Implementation Header File

Create a new header file, for example sw_rtc_date_time.h and add the following code:

#include <stdint.h>
#include <stdbool.h>


/* Days of a week */
typedef enum {
        WEEK_DATE_SUNDAY     = 0,
        WEEK_DATE_MONDAY     = 1,
        WEEK_DATE_TUESDAY    = 2,
        WEEK_DATE_WEDNESDAY  = 3,
        WEEK_DATE_THURSDAY   = 4,
        WEEK_DATE_FRIDAY     = 5,
        WEEK_DATE_SATURDAY   = 6,
} WEEK_DATE;


/*
 * Date related parameters
 */
typedef struct sw_rtc_date {
        uint32_t year;       // Current year: there is no limitation
        uint32_t month;      // Range: (1..12)
        uint32_t month_date; // Range: (1..31) or (1..30) or (1..29) or (1..28)
        uint32_t week_date;  // Range: (0:6) where 0 = Sunday ... 6 = Saturday
} sw_rtc_date_t;


/*
 * Time related parameters
 */
typedef struct sw_rtc_time {
        uint32_t hour;  // Range: (0..23)
        uint32_t min;   // Range: (0..59)
        uint32_t sec;   // Range: (0..59)
        uint32_t msec;  // Range: (0..999)
        uint64_t usec;  // Range: (0..999)
} sw_rtc_time_t;


/*
 * Date/time (calendar) related parameters
 */
typedef struct sw_rtc_calendar {
        sw_rtc_date_t date;
        sw_rtc_time_t time;
} sw_rtc_date_time_t;



/**
 *
 * \brief SW RTC initialization
 *
 * This function initializes all the resources required for the SW RTC mechanism.
 *
 * \note It should be the first invoked API before any other SW RTC related operations.
 *
 */
void sw_rtc_init();


/**
 *
 * \brief SW RTC date/time read operation
 *
 * This function is responsible for reading the current date and time. It should be
 * considered as a shortcut function since it combines both sw_rtc_date_read()
 * and sw_rtc_time_read().
 *
 *
 * \param [out] val  A structure in which the current date/time will be stored
 *
 * \note This function can be called by any task at any time, to read the current date.
 *       A protection mechanism against multiple accesses has been implemented.
 *
 */
void sw_rtc_date_time_read(sw_rtc_date_time_t *val);




/*
 * \brief SW RTC date read operation
 *
 * This function is responsible for reading the current date.
 *
 *
 * \param [out] val   A structure in which the current date will be stored
 *
 * \note This function can be called by any task at any time, to read the current date.
 *       A protection mechanism against multiple accesses has been implemented.
 *
 */
void sw_rtc_date_read(sw_rtc_date_t *val);


/*
 * \brief SW RTC time read operation
 *
 * This function is responsible for reading the current time.
 *
 *
 * \param [out] val  A structure in which the current time will be stored
 *
 * \note This function can be called by any task at any time, to read the current date.
 *       A protection mechanism against multiple accesses has been implemented.
 *
 */
void sw_rtc_time_read(sw_rtc_time_t *val);



/*
 * \brief SW RTC date/time write operation
 *
 * This function is responsible for setting the current date and time. It should be
 * considered as a shortcut function since it combines both sw_rtc_date_write()
 * and sw_rtc_time_write()
 *
 *
 * \param[in] val The preferred values for date/time.
 *
 * \note The weekday is calculated automatically by the app.
 *
 * \note The code performs sanity checks to identify values out of boundaries.
 *       For any invalid value, the code gets stuck in assertions!
 *
 * \note This function can be called by any task at any time, to set date/time.
 *       A protection mechanism against multiple accesses has been implemented.
 *
 */
void sw_rtc_date_time_write(sw_rtc_date_time_t *val);



/*
 * \brief SW RTC date write operation
 *
 * This function is responsible for setting the current time.
 *
 *
 * \param[in] val The preferred values for time
 *
 * \note The code performs sanity checks to identify values out of boundaries.
 *       For any invalid value, the code gets stuck in assertions!
 *
 * \note This function can be called by any task at any time, to set date/time.
 *       A protection mechanism against multiple accesses has been implemented.
 *
 */
void sw_rtc_time_write(sw_rtc_time_t *val);



/*
 * \brief SW RTC time write operation
 *
 * This function responsible for setting the current time.
 *
 *
 * \param[in] val The preferred values for date.
 *
 * \note The weekday is calculated automatically by the code.
 *
 * \note The code performs sanity checks to identify values out of boundaries.
 *       For any invalid value, the code gets stuck in assertions!
 *
 * \note This function can be called by any task at any time, to set date/time.
 *       A protection mechanism against multiple accesses has been implemented.
 *
 */
void sw_rtc_date_write(sw_rtc_date_t *val);

6.9. Alert Implementation Source File

Code snippet of alert mechanism implementation. Create a new source file, for example alert_mechanism.c and add the following code:

#include "alert_mechanism.h"

#include <string.h>
#include "osal.h"

/*
 * Masks for positioning alert sources (alert class)
 */
#define ALERT_SOURCE_HOURS_Pos   (0x00)
#define ALERT_SOURCE_MINUTES_Pos (0x01)
#define ALERT_SOURCE_SECONDS_Pos (0x02)


/* Handles of OS timers - One for each ALERT class */
__RETAINED_RW static OS_TIMER timer_h = NULL;
__RETAINED_RW static OS_TIMER timer_m = NULL;
__RETAINED_RW static OS_TIMER timer_s = NULL;



/*
 * Hold the current user-defined callback function
 */
__RETAINED_RW static alert_cb cb = NULL;

/*
 * Hold the current alert configurations.
 */
__RETAINED_RW static time_alert_t *alert_cfg = NULL;



/*
 * OS timer callback, dedicated for Hours Alert Class.
 * This callback is called upon timer's expiration.
 */
static void timer_h_cb(OS_TIMER timer)
{
        // Check whether the [repeat] flag is set, then restart timer.
        if (alert_cfg->repeat_h == pdTRUE) {
                OS_TIMER_START(timer, OS_TIMER_FOREVER);
        }

        // Check whether a user-defined function has been declared.
        if (cb) {
                cb((uint8_t)(1 << ALERT_SOURCE_HOURS_Pos));
        }

}


/*
 * OS timer callback, dedicated for Minutes Alert Class.
 * This callback is called upon timer's expiration.
 */
void timer_m_cb(OS_TIMER timer)
{
        // Check whether the [repeat] flag is set, then restart timer.
        if (alert_cfg->repeat_m == pdTRUE) {
                OS_TIMER_START(timer, OS_TIMER_FOREVER);
        }

        // Check whether a user-defined function has been declared.
        if (cb) {
                cb((uint8_t)(1 << ALERT_SOURCE_MINUTES_Pos));
        }

}



/*
 * OS timer callback, dedicated for Seconds Alert Class.
 * This callback is called upon timer's expiration.
 */
void timer_s_cb(OS_TIMER timer)
{
        // Check whether the [repeat] flag is set, then restart timer.
        if (alert_cfg->repeat_s == pdTRUE) {
                OS_TIMER_START(timer, OS_TIMER_FOREVER);
        }

        // Check whether a user-defined function has been declared.
        if (cb) {
                cb((uint8_t)(1 << ALERT_SOURCE_SECONDS_Pos));
        }

}




/*
 * Initialize the alert mechanism. Three OS timers are being created,
 * one for each ALERT class, without enabling them.
 */
void time_alert_init(void)
{
       /* Allocate memory for the alert parameters */
       alert_cfg = (time_alert_t *) OS_MALLOC(sizeof(time_alert_t));
       OS_ASSERT(alert_cfg);

       /* Clear the alert structure */
       memset((uint8_t *)alert_cfg, 0x00, sizeof(time_alert_t));


       /*
        * Create one-shot OS timer dedicated for Hours Alert Class.
        *
        * \note  Timer period - expressed in OS ticks -
        *        must be greater than 1, otherwise an assertion is issued!
        */
       timer_h = OS_TIMER_CREATE("HOURS", OS_MS_2_TICKS(10),
                                       OS_TIMER_FAIL, (void *)0, timer_h_cb);
       OS_ASSERT(timer_h);

       /*
        * Create one-shot OS timer dedicated for Minutes Alert Class.
        *
        * \note:  Timer's period - expressed in OS ticks -
        *         must be greater than 1, otherwise an assertion is issued!
        */
       timer_m = OS_TIMER_CREATE("MINUTES", OS_MS_2_TICKS(10),
                                        OS_TIMER_FAIL, (void *)0, timer_m_cb);
       OS_ASSERT(timer_m);

       /*
        * Create one-shot OS timer dedicated for Seconds Alert Class.
        *
        * \note: Timer's period - expressed in OS ticks -
        *        must be greater than 1, otherwise an assertion is issued.
        */
       timer_s = OS_TIMER_CREATE("SECONDS", OS_MS_2_TICKS(10),
                                         OS_TIMER_FAIL, (void *)0, timer_s_cb);
       OS_ASSERT(timer_s);

}


/*
 * Function responsible for setting an alert
 */
void time_alert_class_set(uint16_t value, bool repeat, SET_ALERT_CLASS alert)
{
        OS_BASE_TYPE ret = 0;
        uint64_t timer_ticks = 0;

        switch(alert) {

        /* Set alerts for Hours Alert Class */
        case SET_ALERT_CLASS_Hour:
                /* Then check whether the new value assigned is non-zero */
                if (value > 0) {
                        /*
                         * Recalculate timer period, that is,
                         * (hours * 60 minutes/hour * 60 seconds/minute * 1000 milliseconds/second)
                         *
                         * Note: Changing the period of a dormant timer will also
                         * start the timer!
                         */

                        timer_ticks = (((uint64_t)value * 3600000 * configTICK_RATE_HZ) / 1000);

                        /* Perform a sanity check for 32-bit max number */
                        if (timer_ticks >= (uint64_t)4294967295UL) {
                              OS_ASSERT(0);
                        }

                        ret = OS_TIMER_CHANGE_PERIOD(timer_h,
                                        (uint32_t)timer_ticks, OS_TIMER_FOREVER);

                        OS_ASSERT(ret == OS_OK);

                 /* Otherwise the new value is zero and hence timer should be halted */
                 } else {
                         ret = OS_TIMER_STOP(timer_h, OS_TIMER_FOREVER);
                         OS_ASSERT(ret == OS_OK);
                 }

                /* Store the new parameters for Hours Alert Class */
                alert_cfg->hour     = value;
                alert_cfg->repeat_h = repeat;
                break;

        /* Set alerts for Minutes Alert Class */
        case SET_ALERT_CLASS_Min:
                /* Then check whether the new value assigned is non-zero */
                if (value > 0) {
                        /*
                         * Recalculate timer period, that is,
                         * (minutes * 60 seconds/minute * 1000 milliseconds/second)
                         *
                         * Note: changing the period of a dormant timer will also
                         * start the timer!
                         */
                         timer_ticks = (((uint64_t)value * 60000 * configTICK_RATE_HZ) / 1000);

                         /* Perform a sanity check for 32-bit max number */
                         if (timer_ticks >= (uint64_t)4294967295UL) {
                               OS_ASSERT(0);
                         }

                         ret = OS_TIMER_CHANGE_PERIOD(timer_m,
                                          (uint32_t)timer_ticks, OS_TIMER_FOREVER);

                         OS_ASSERT(ret == OS_OK);

                  /* Otherwise the new value is zero and hence timer should be halted */
                  } else {
                          ret = OS_TIMER_STOP(timer_m, OS_TIMER_FOREVER);
                          OS_ASSERT(ret == OS_OK);
                        }

                /* Store the new parameters for Minutes Alert Class */
                alert_cfg->min = value;
                alert_cfg->repeat_m = repeat;
                break;

        /* Set alerts for Seconds Alert Class */
        case SET_ALERT_CLASS_Sec:
                /* Then check whether the new value assigned is non-zero */
                if (value > 0) {
                        /*
                         * Recalculate timer period, that is,
                         * (seconds * 1000 milliseconds/second)
                         *
                         * Note: changing the period of a dormant timer will also
                         * start the timer!
                         */

                         timer_ticks = (((uint64_t)value * 1000 * configTICK_RATE_HZ) / 1000);

                         /* Perform a sanity check for 32-bit max number */
                         if (timer_ticks >= (uint64_t)4294967295UL) {
                               OS_ASSERT(0);
                         }

                         ret = OS_TIMER_CHANGE_PERIOD(timer_s,
                                       (uint32_t)timer_ticks, OS_TIMER_FOREVER);

                         OS_ASSERT(ret == OS_OK);

                        /* Otherwise the new value is zero and hence timer should be halted */
                  } else {
                          ret = OS_TIMER_STOP(timer_s, OS_TIMER_FOREVER);
                          OS_ASSERT(ret == OS_OK);
                        }

                /* Store the new parameters for Seconds Alert Class */
                alert_cfg->sec = value;
                alert_cfg->repeat_s = repeat;
                break;

        default:
                OS_ASSERT(0);
        }

}


/*
 * Function responsible for settings alerts of all the available alert classes
 */
void time_alert_class_set_all(time_alert_t *alert)
{
        time_alert_class_set(alert->hour, alert->repeat_h, SET_ALERT_CLASS_Hour);
        time_alert_class_set(alert->min,  alert->repeat_m, SET_ALERT_CLASS_Min);
        time_alert_class_set(alert->sec,  alert->repeat_s, SET_ALERT_CLASS_Sec);
}


/*
 * Function responsible for clearing alerts
 */
void time_alert_class_clear(CLEAR_ALERT_CLASS alert)
{

        switch(alert) {

        /* Disable alerts for Hours Alert Class */
        case CLEAR_ALERT_CLASS_Hour:
                /* Clear the associated variables */
                alert_cfg->hour = 0;
                alert_cfg->repeat_h = false;

                /*Stop the associated OS timer */
                OS_TIMER_STOP(timer_h, OS_TIMER_FOREVER);
                break;

        /* Disable alerts for Minutes Alert Class */
        case CLEAR_ALERT_CLASS_Min:
                /* Clear the associated variables */
                alert_cfg->min = 0;
                alert_cfg->repeat_m = false;

                /*Stop the associated OS timer */
                OS_TIMER_STOP(timer_m, OS_TIMER_FOREVER);
                break;

        /* Disable alerts for Seconds Alert Class */
        case CLEAR_ALERT_CLASS_Sec:
                /* Clear the associated variables */
                alert_cfg->sec = 0;
                alert_cfg->repeat_s = false;

                /*Stop the associated OS timer */
                OS_TIMER_STOP(timer_s, OS_TIMER_FOREVER);
                break;

        /* Disable alerts for all classes */
        case CLEAR_ALERT_CLASS_All:
                /* Clear all the alert variables */
                memset((uint8_t *)alert_cfg, 0x00, sizeof(time_alert_t));

                /*Stop all the associated OS timers */
                OS_TIMER_STOP(timer_h, OS_TIMER_FOREVER);
                OS_TIMER_STOP(timer_m, OS_TIMER_FOREVER);
                OS_TIMER_STOP(timer_s, OS_TIMER_FOREVER);
                break;

        default:
                OS_ASSERT(0);
        }

}



/*
 * Function responsible for registering the user-defined callback function
 */
void time_alert_register_cb(alert_cb user_cb)
{
        cb = user_cb;
}

/*
 * Function responsible for deleting the user-defined callback function
 */
void time_alert_unregister_cb(void)
{
        cb = NULL;
}

6.10. Alert Implementation Header File

Create a new header file, for example alert_mechanism.h and add the following code:

#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>


/**
 *
 * \brief Alert related parameters.
 *
 * \note There are three distinct ALERT classes that can be configured individually.
 *
 */
typedef struct time_alert {
        /*
         * A value equal to zero means that the corresponding alert class is disabled.
         */
        uint16_t hour;
        uint16_t min;
        uint16_t sec;

        /*
         * Define whether an alert will be repeatable. Valid values are:
         * true: for repeatable alarms, false otherwise.
         */
        bool repeat_h;
        bool repeat_m;
        bool repeat_s;
} time_alert_t;


/**
 *
 * \brief Clear an alert class
 *
 */
typedef enum {
     CLEAR_ALERT_CLASS_Hour = 0,
     CLEAR_ALERT_CLASS_Min  = 1,
     CLEAR_ALERT_CLASS_Sec  = 2,
     CLEAR_ALERT_CLASS_All  = 3,
} CLEAR_ALERT_CLASS;


/**
 *
 * \brief Set an alert class
 *
 */
typedef enum {
        SET_ALERT_CLASS_Hour = 0,
        SET_ALERT_CLASS_Min  = 1,
        SET_ALERT_CLASS_Sec  = 2,
} SET_ALERT_CLASS;



/**
 *
 * \brief Alert Class Masks
 *
 * These masks should be used within callback functions to identify
 * the source of an alert event. This is necessary since all the
 * ALERT classes will call the same callback function.
 *
 */
#define ALERT_SOURCE_HOURS_Msk   (0x01)
#define ALERT_SOURCE_MINUTES_Msk (0x02)
#define ALERT_SOURCE_SECONDS_Msk (0x04)



/**
 *
 * \brief User-defined callback function
 *
 * This function will be called upon an alert event. It should have that specific prototype!
 *
 * \note All the alert classes will call the same callback. User is responsible to identify
 *       the source of an alert event. Following the recommended callback usage:
 *
 * \code{.c}
 * void my_alert_cb(uint8_t source)
 * {
 *      if (source & ALERT_SOURCE_HOURS_Msk) {
 *              // Do something...
 *       }
 *
 *       if (source & ALERT_SOURCE_MINUTES_Msk) {
 *              // Do something...
 *       }
 *
 *       if (source & ALERT_SOURCE_SECONDS_Msk) {
 *              // Do something...
 *       }
 * }
 * \endcode
 */
typedef void (*alert_cb)(uint8_t source);



/**
 *
 * \brief Alert mechanism initialization
 *
 * This function initializes all the resources required for the alert mechanism.
 *
 * \note It should be the first invoked API before any other alert related operation.
 *
 */
void time_alert_init(void);



/**
 *
 * \brief Callback function registration
 *
 *
 * This function should be used for registering a callback function, called upon an alert event.
 *
 *
 * \param [in] user_cb  The user-defined callback function.
 *
 * \note If \p user_cb is set to NULL, none callback function will be called upon an alert event.
 *
 */
void time_alert_register_cb(alert_cb user_cb);


/**
 *
 * \brief Callback function deletion
 *
 * This function should be used for unregistering any previously declared callback.
 *
 */
void time_alert_unregister_cb(void);



/**
 *
 * \brief Set alerts for a specific ALERT class
 *
 * This function should be used for declaring alerts of a specific ALERT class.
 * There are three distinct alert classes that can be configured individually.
 *
 * The following code demonstrates alert configurations, expressed in minutes. Assuming we
 * want alerts to be triggered every 5 minutes (triggered repeatedly).
 *
 * \code{.c}
 * time_alert_set_class(5, true, SET_ALERT_CLASS_Min)
 * \endcode
 *
 * The following code demonstrates alert configurations, expressed in seconds. Assuming we
 * want an alert after 5 minutes (triggered once)
 *
 * \code{.c}
 * time_alert_set_class(5, false, SET_ALERT_CLASS_Min)
 * \endcode
 *
 *
 * \param [in] value   Time interval of alert events.
 * \param [in] repeat  If set to false, alerts will be triggered once, repeatedly otherwise.
 * \param [in] alert   One of the three alert classes.
 *
 *
 * \note If \p value is set to zero, alerts for that specific alert class will be disabled.
 *
 */
void time_alert_class_set(uint16_t value, bool repeat, SET_ALERT_CLASS alert);


/**
 * \brief Set alert events for all the three alert classes
 *
 * This is a shortcut function since it can configure all the three alert classes.
 *
 * \param [in] alert  Parameters for all the tree alert classes.
 *
 */
void time_alert_class_set_all(time_alert_t *alert);



/**
 *
 * \brief Clear alert events
 *
 * This function should be used for disabling alert events of an individual or all the three alert classes
 *
 * \param [in] alert  The alert class
 *
 */
void time_alert_class_clear(CLEAR_ALERT_CLASS alert);