RTC — real-time clock

1. Introduction to RTC

Real Time Clock (RTC) is essentially a counter with a counting frequency of seconds and is specially used to record time.

Ordinary timers cannot be used as clocks because they cannot run when powered off.

RTC features:

  • Can provide time (number of seconds);
  • Can run after the MCU is powered off;
  • Low power consumption.

Contrast factors Built-in RTC External RTC
Information difference Seconds signal and calendar provided via seconds/sub-seconds
Power consumption High power consumption Low power consumption
Volume No need to occupy Extra volume Large volume
Cost Low cost High cost

“Subsecond” is not a unit of time, but a unit of rotational speed, which refers to a speed that does not reach seconds, that is, 1GHz/1.2 seconds. The second level is measured in seconds, that is, the speed is calculated in seconds, 1GHz/second.

Notice:

  • Generally, RTC peripheral circuits need to be designed;
  • Generally, an independent power supply can be set for the RTC;
  • Most RTC registers use BCD code to store time information.

2. Introduction to STM32 RTC block diagram

Use LSE (32.768kHz) as the clock source.

3. Introduction to RTC related registers (F1)

<1>.Backup register and RTC register characteristics

Some registers are write-protected:RTC_PRL, RTC_ALR, RTC_CNT and RTC_DIV registers will not be reset by the system;

Data storage function: The RTC and backup registers are not reset by the system or power reset source; nor are they reset when waking up from standby mode. The backup register can be used to save data when power is lost;

Independent operation: The RTC and backup register are powered by a switch, which selects VDD power supply when VDD is valid, otherwise it is powered by the VBAT pin. It can still continue to work when VBAT is powered;

2 independent resets: The APB1 interface is reset by the system; the RTC core can only be reset by the backup domain.

<2>.RTC basic configuration steps

  1. Enable access to RTC: enable PWR & amp; & amp;BKP clock, enable access to backup register and RTC — RCC_APB1ENR, PWR_CR;
  2. Set the RTC clock source: activate LSE and set the RTC counting clock source to LSE — RCC_BDCR;
  3. Enter configuration mode: wait for the RTOFF bit to be 1, set the CNF bit to 1 — RCC_CRL;
  4. Set the RTC register: set the frequency division value, count value, etc. Generally, only the frequency division value is set first, and the setting of CNT is independent — RTC_PRL, RCC_CNT;
  5. Exit configuration mode: clear the CNF bit and wait for RTOFF to be 1 to complete the configuration — RTC_CRL.

<3>. Register introduction

Enable PWR & &BKP clock

Enable access to the backup domain and RTC

Configure through RCC_BDCR: 1. Turn on the RTC clock; 2. Turn on the LSE clock; 3. Select the RTC counting clock source.

RTC enters configuration mode, paying attention to the bit.

The reset value is 0, and setting it to 1 can enable related interrupts.

Each time the hour counter counts is 1 second, which requires a frequency division of 32768.

The register RTC_DIV, which is similar to RTC_PRL, can obtain the current value of the prescaler counter without stopping the work of the prescaler counter.

Note: Registers can be operated only after entering configuration mode.

A register similar to RTC_CNT is RTC_ALR.

IV. Introduction to RTC related HAL library driver (F1)

Driver function Associated register Function description
HAL_RTC_Init() CRL/CRH/PRLH/PRLL Initialize RTC
HAL_RTC_MspInit() Initialization callback Enable RTC clock
HAL_RCC_OscConfig() RCC_CR/PWR_CR Enable LSE clock source
HAL_RCCEx_PeriphCLKConfig() RCC_BDCR Set RTC clock source Enable backup domain access permissions for LSE
HAL_PWR_EnableBKUpAccess() PWR_CR
HAL_RTCEx_BKUP Write/Read() BKP_DRx Read/write backup domain data register

Clock sources that need to be turned on: __HAL_RCC_RTC_ENABLE(), __HAL_RCC_PWR_CLK_ENABLE(), __HAL_RCC_BKP_CLK_ENABLE()

5. Basic RTC driver steps

  1. Enable the power clock and enable backup domain access:__HAL_RCC_PWR_CLK_ENABLE – Enable the power clock; __HAL_RCC_BKP_CLK_ENABLE() – Enable the backup clock; HAL_PWR_EnableBkUpAccess – Enable backup access;
  2. Turn on LSE/select RTC clock source/enable RTC clock:HAL_RCC_OscConfig – turn on LSE; HAL_RCCEx_PeriphCLKConfig() – select RTC clock source; __HAL_RCC_RTC_ENABLE() – enable RTC clock;
  3. Initialize RTC, set frequency division value and working parameters:HAL_RTC_Init – initialize RTC; HAL_RTC_MspInit() – complete RTC underlying initialization work;
  4. Set the date and time of RTC: Implement rtc_set_time by operating register;
  5. Get RTC current date and time: Define the rtc_get_time function.

6. Time setting and reading

Since the RTC of F1 does not have a calendar register, it only stores the total number of seconds when used, which is not conducive to direct setting and display, so it is necessary to write a function to turn the time into our daily calendar time.

The global structure variable calendar stores time information.

Driver function Function description
rtc_get_time() Convert total seconds to calendar time
rtc_is_leap_year() Determine whether it is a leap year
uint8_t rtc_get_week() Calculate the day of the week corresponding to the Gregorian calendar
static long rtc_date22sec() Convert the calendar time into the corresponding total Seconds

Seven. Code

Drive RTC and use the serial port to print the current time.

rtc.c

#include "./BSP/RTC/rtc.h"
#include "./BSP/LED/led.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"


RTC_HandleTypeDef g_rtc_handle; /* RTC control handle */
_calendar_obj calendar; /* Clock structure */

/**
 * @brief RTC writes to backup area SRAM
 * @param bkrx: Backup area register number, range: 0~41
                        Corresponds to RTC_BKP_DR1~RTC_BKP_DR42
 * @param data: data to be written, 16 bits in length
 * @retval None
 */
void rtc_write_bkr(uint32_t bkrx, uint16_t data)
{
    HAL_PWR_EnableBkUpAccess(); /* Cancel write protection of backup area */
    HAL_RTCEx_BKUPWrite( & amp;g_rtc_handle, bkrx + 1, data);
}

/**
 * @brief RTC reads the backup area SRAM
 * @param bkrx: Backup area register number, range: 0~41
                Corresponds to RTC_BKP_DR1~RTC_BKP_DR42
 * @retval value read
 */
uint16_t rtc_read_bkr(uint32_t bkrx)
{
    uint32_t temp = 0;
    temp = HAL_RTCEx_BKUPRead( & amp;g_rtc_handle, bkrx + 1);
    return (uint16_t)temp; /* Return the value read */
}

/**
 * @brief RTC initialization
 * @note
 * By default, LSE is tried to be used. When LSE fails to start, it switches to LSI.
 * Through the value of BKP register 0, it can be judged that RTC uses LSE/LSI:
 * When BKP0==0X5050, LSE is used
 * When BKP0==0X5051, LSI is used
 * Note: Switching LSI/LSE will cause the time/date to be lost and needs to be reset after switching.
 *
 * @param none
 * @retval 0, success
 * 1, Failed to enter initialization mode
 */
uint8_t rtc_init(void)
{
    /* Check if this is the first time to configure the clock */
    uint16_t bkpflag = 0;
    
    __HAL_RCC_PWR_CLK_ENABLE(); /* Enable power clock */
    __HAL_RCC_BKP_CLK_ENABLE(); /* Enable backup clock */
    HAL_PWR_EnableBkUpAccess(); /* Cancel write protection of backup area */

    bkpflag = rtc_read_bkr(0); /* Read the value of BKP0 */

    g_rtc_handle.Instance = RTC;
    g_rtc_handle.Init.AsynchPrediv = 32767; /*Clock cycle setting, theoretical value: 32767, RTC_AUTO_1_SECOND can also be used here */
    g_rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
    if (HAL_RTC_Init( & amp;g_rtc_handle) != HAL_OK)
    {
        return 1;
    }

    if ((bkpflag != 0X5050) & amp; & amp; (bkpflag != 0x5051)) /* Not initialized before, reconfigure */
    {
        rtc_set_time(2020, 4, 25, 20, 25, 35); /* Set time */
    }

    __HAL_RTC_ALARM_ENABLE_IT( & amp;g_rtc_handle, RTC_IT_SEC); /* Allow seconds interrupt */
    
    HAL_NVIC_SetPriority(RTC_IRQn, 0x2, 0); /* Priority setting */
    HAL_NVIC_EnableIRQ(RTC_IRQn); /* Enable RTC interrupt channel */

    rtc_get_time(); /* update time */
    
    return 0;
}

/**
 * @brief RTC initialization
 * @note
 * RTC bottom driver, clock configuration, this function will be called by HAL_RTC_Init()
 * @param hrtc:RTC handle
 * @retval None
 */
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
    uint16_t retry = 200;
    
    __HAL_RCC_RTC_ENABLE(); /* RTC clock enable */

    RCC_OscInitTypeDef rcc_oscinitstruct;
    RCC_PeriphCLKInitTypeDef rcc_periphclkinitstruct;
    
    /* Use registers to detect whether LSE can work normally */
    RCC->BDCR |= 1 << 0; /* Turn on the external low-speed oscillator LSE */
    
    while (retry & amp; & amp; ((RCC->BDCR & amp; 0X02) == 0)) /* Wait for LSE to be ready */
    {
        retry--;
        delay_ms(5);
    }
    
    if (retry == 0) /* LSE failed to start oscillation, use LSI */
    {
        rcc_oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_LSI; /* Select the oscillator to be configured */
        rcc_oscinitstruct.LSEState = RCC_LSI_ON; /* LSI status: on */
        rcc_oscinitstruct.PLL.PLLState = RCC_PLL_NONE; /* No configuration for PLL */
        HAL_RCC_OscConfig( & amp;rcc_oscinitstruct); /* rcc_oscinitstruct of configuration settings */

        rcc_periphclkinitstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; /* Select the peripheral RTC to be configured */
        rcc_periphclkinitstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; /* RTC clock source selection LSI */
        HAL_RCCEx_PeriphCLKConfig( & amp;rcc_periphclkinitstruct); /* Configure rcc_periphClkInitStruct */
        rtc_write_bkr(0, 0X5051);
    }
    else
    {
        rcc_oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; /* Select the oscillator to configure */
        rcc_oscinitstruct.LSEState = RCC_LSE_ON; /* LSE status: on */
        rcc_oscinitstruct.PLL.PLLState = RCC_PLL_NONE; /* PLL is not configured */
        HAL_RCC_OscConfig( & amp;rcc_oscinitstruct); /* rcc_oscinitstruct of configuration settings */
        
        rcc_periphclkinitstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; /* Select the peripheral RTC to be configured */
        rcc_periphclkinitstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; /* RTC clock source selection LSE */
        HAL_RCCEx_PeriphCLKConfig( & amp;rcc_periphclkinitstruct); /* rcc_periphclkinitstruct for configuration settings */
        rtc_write_bkr(0, 0X5050);
    }
}

/**
 * @brief RTC clock interrupt
 * @note Seconds interrupt service function, incidentally handles the alarm clock flag
 * Distinguish which interrupt is based on the SECF and ALRF bits of the RTC_CRL register
 * @param None
 * @retval None
 */
void RTC_IRQHandler(void)
{
    if (__HAL_RTC_ALARM_GET_FLAG( & amp;g_rtc_handle, RTC_FLAG_SEC) != RESET) /* Second interrupt */
    {
        rtc_get_time(); /* update time */
        __HAL_RTC_ALARM_CLEAR_FLAG( & amp;g_rtc_handle, RTC_FLAG_SEC); /* Clear seconds interrupt */
        //printf("sec:%d\r\\
", calendar.sec); /* Print seconds */
    }

    /* Handle the alarm clock flag incidentally */
    if (__HAL_RTC_ALARM_GET_FLAG( & amp;g_rtc_handle, RTC_FLAG_ALRAF) != RESET) /* Alarm clock flag */
    {
        __HAL_RTC_ALARM_CLEAR_FLAG( & amp;g_rtc_handle, RTC_FLAG_ALRAF); /* Clear alarm flag */
        printf("Alarm Time:%d-%d-%d %d:%d:%d\\
", calendar.year, calendar.month, calendar.date, calendar.hour, calendar.min, calendar .sec);
    }

    __HAL_RTC_ALARM_CLEAR_FLAG( & amp;g_rtc_handle, RTC_FLAG_OW); /* Clear the overflow interrupt flag */
    while (!__HAL_RTC_ALARM_GET_FLAG( & amp;g_rtc_handle, RTC_FLAG_RTOFF)); /* Wait for the RTC register operation to complete, that is, wait for RTOFF == 1 */
}

/**
 * @brief Determine whether the year is a leap year
 * @note Month day table:
 *Month 1 2 3 4 5 6 7 8 9 10 11 12
 * Leap year 31 29 31 30 31 30 31 31 30 31 30 31
 * Non-leap year 31 28 31 30 31 30 31 31 30 31 30 31
 * @param year: year
 * @retval 0, not a leap year; 1, is a leap year;
 */
static uint8_t rtc_is_leap_year(uint16_t year)
{
    /* Leap year rules: There will be no leap year if there is a leap year in four years, and there will be a leap year in four hundred years */
    if ((year % 4 == 0 & amp; & amp; year % 100 != 0) || (year % 400 == 0))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/**
 * @brief Set time, including year, month, day, hour, minute and second
 * @note Based on January 1, 1970, the accumulated time will be added later
 * The legal year range is: 1970 ~ 2105
                HAL defaults to the year starting point being 2000
 * @param syear: year
 * @param smon: month
 * @param sday: date
 * @param hour: hour
 * @param min : minutes
 * @param sec: seconds
 * @retval 0, success; 1, failure;
 */
uint8_t rtc_set_time(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
{
    uint32_t seccount = 0;

    seccount = rtc_date2sec(syear, smon, sday, hour, min, sec); /* Convert year, month, day, hour, minute and second into total seconds */

    __HAL_RCC_PWR_CLK_ENABLE(); /* Enable power clock */
    __HAL_RCC_BKP_CLK_ENABLE(); /* Enable backup domain clock */
    HAL_PWR_EnableBkUpAccess(); /* Cancel backup domain write protection */
    /* The above three steps are necessary! */
    
    RTC->CRL |= 1 << 4; /* Allow configuration */
    
    RTC->CNTL = seccount & 0xffff;
    RTC->CNTH = seccount >> 16;
    
    RTC->CRL & amp;= ~(1 << 4); /* Configuration update */

    while (!__HAL_RTC_ALARM_GET_FLAG( & amp;g_rtc_handle, RTC_FLAG_RTOFF)); /* Wait for the RTC register operation to complete, that is, wait for RTOFF == 1 */

    return 0;
}

/**
 * @brief Set the alarm clock, specific to the year, month, day, hour, minute and second
 * @note Based on January 1, 1970, the accumulated time will be added later
 * The legal year range is: 1970 ~ 2105
 * @param syear: year
 * @param smon: month
 * @param sday: date
 * @param hour: hour
 * @param min : minutes
 * @param sec: seconds
 * @retval 0, success; 1, failure;
 */
uint8_t rtc_set_alarm(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
{
    uint32_t seccount = 0;

    seccount = rtc_date2sec(syear, smon, sday, hour, min, sec); /* Convert year, month, day, hour, minute and second into total seconds */

    __HAL_RCC_PWR_CLK_ENABLE(); /* Enable power clock */
    __HAL_RCC_BKP_CLK_ENABLE(); /* Enable backup domain clock */
    HAL_PWR_EnableBkUpAccess(); /* Cancel backup domain write protection */
    /* The above three steps are necessary! */
    
    RTC->CRL |= 1 << 4; /* Allow configuration */
    
    RTC->ALRL = seccount & 0xffff;
    RTC->ALRH = seccount >> 16;
    
    RTC->CRL & amp;= ~(1 << 4); /* Configuration update */

    while (!__HAL_RTC_ALARM_GET_FLAG( & amp;g_rtc_handle, RTC_FLAG_RTOFF)); /* Wait for the RTC register operation to complete, that is, wait for RTOFF == 1 */

    return 0;
}

/**
 * @brief Get the current time
 * @note This function does not return the time directly, the time data is stored in the calendar structure
 * @param none
 * @retval None
 */
void rtc_get_time(void)
{
    static uint16_t daycnt = 0;
    uint32_t seccount = 0;
    uint32_t temp = 0;
    uint16_t temp1 = 0;
    const uint8_t month_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* Month and date table in ordinary years */

    seccount = RTC->CNTH; /* Get the value in the counter (number of seconds) */
    seccount <<= 16;
    seccount + = RTC->CNTL;

    temp = seccount / 86400; /* Get the number of days (corresponding to the number of seconds) */

    if (daycnt != temp) /* More than one day */
    {
        daycnt = temp;
        temp1 = 1970; /* Starting from 1970 */

        while (temp >= 365)
        {
            if (rtc_is_leap_year(temp1)) /* It is a leap year */
            {
                if (temp >= 366)
                {
                    temp -= 366; /* Number of seconds in leap year */
                }
                else
                {
                    break;
                }
            }
            else
            {
                temp -= 365; /* normal year */
            }

            temp1++;
        }

        calendar.year = temp1; /* get the year */
        temp1 = 0;

        while (temp >= 28) /* More than one month */
        {
            if (rtc_is_leap_year(calendar.year) & amp; & amp; temp1 == 1) /* Is the current year a leap year/February */
            {
                if (temp >= 29)
                {
                    temp -= 29; /* Number of seconds in leap year */
                }
                else
                {
                    break;
                }
            }
            else
            {
                if (temp >= month_table[temp1])
                {
                    temp -= month_table[temp1]; /* normal year */
                }
                else
                {
                    break;
                }
            }

            temp1++;
        }

        calendar.month = temp1 + 1; /* get the month */
        calendar.date = temp + 1; /* get date */
    }

    temp = seccount % 86400; /* Get the number of seconds */
    calendar.hour = temp / 3600; /* hour */
    calendar.min = (temp % 3600) / 60; /* minutes */
    calendar.sec = (temp % 3600) % 60; /* seconds */
    calendar.week = rtc_get_week(calendar.year, calendar.month, calendar.date); /* Get the day of the week */
}

/**
 * @brief Convert year, month, day, hour, minute and second into seconds
 * @note Enter the Gregorian calendar date to get the day of the week (the starting time is: March 1, year 0, enter any subsequent date to get the correct week)
 * Calculated using Kim Larson’s calculation formula, please see this post for the principle explanation:
 * https://www.cnblogs.com/fengbohello/p/3264300.html
 * @param syear: year
 * @param smon: month
 * @param sday: date
 * @retval 0, Sunday; 1 ~ 6: Monday ~ Saturday
 */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day)
{
    uint8_t week = 0;

    if (month < 3)
    {
        month + = 12;
        --year;
    }

    week = (day + 1 + 2 * month + 3 * (month + 1) / 5 + year + (year >> 2) - year / 100 + year / 400) % 7;
    return week;
}

/**
 * @brief Convert year, month, day, hour, minute and second into seconds
 * @note Based on January 1, 1970, January 1, 1970, 0 hours, 0 minutes and 0 seconds, represents the 0th second
 * The maximum representation is to 2105, because uint32_t represents the maximum number of seconds in 136 years (excluding leap years)!
 * This code only refers to the Linux mktime function. For the principle description, see this post:
 * http://www.openedv.com/thread-63389-1-1.html
 * @param syear: year
 * @param smon: month
 * @param sday: date
 * @param hour: hour
 * @param min : minutes
 * @param sec: seconds
 * @retval Number of seconds after conversion
 */
static long rtc_date2sec(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
{
    uint32_t Y, M, D, X, T;
    signed char monx = smon; /* Convert the month into a signed value to facilitate subsequent operations */

    if (0 >= (monx -= 2)) /* 1..12 -> 11,12,1..10 */
    {
        monx + = 12; /* Puts Feb last since it has leap day */
        syear -= 1;
    }

    Y = (syear - 1) * 365 + syear / 4 - syear / 100 + syear / 400; /* The number of leap years from 1 AD to the present */
    M = 367 * monx / 12 - 30 + 59;
    D = sday - 1;
    X = Y + M + D - 719162; /* Subtract the number of days from AD 1 to 1970 */
    T = ((X * 24 + hour) * 60 + min) * 60 + sec; /* Total seconds */
    return T;
}

rtc.h

#ifndef __RTC_H
#define __RTC_H

#include "./SYSTEM/sys/sys.h"


/* Time structure, including year, month, day, week, hour, minute, second and other information */
typedef struct
{
    uint8_t hour; /* hour */
    uint8_t min; /* minutes */
    uint8_t sec; /* seconds */
    /* Gregorian calendar year, month, day and week */
    uint16_t year; /* year */
    uint8_t month; /* month */
    uint8_t date; /* day */
    uint8_t week; /* week */
} _calendar_obj;
extern _calendar_obj calendar; /* time structure */


/* static function */
static uint8_t rtc_is_leap_year(uint16_t year); /* Determine whether the current year is a leap year */
static long rtc_date2sec(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec); /* Convert year, month, day, hour, minute and second into seconds */

/* Interface function */
uint8_t rtc_init(void); /* Initialize RTC */
void rtc_get_time(void); /* Get RTC time information */
uint16_t rtc_read_bkr(uint32_t bkrx); /* Read the backup register */
void rtc_write_bkr(uint32_t bkrx, uint16_t data); /* Write backup register */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day); /* Get the day of the week based on the year, month and day */
uint8_t rtc_set_time(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec); /* Set time */
uint8_t rtc_set_alarm(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec); /* Set alarm time */

#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/RTC/rtc.h"

/* Define character array for displaying week */
char* weekdays[]={"Sunday","Monday","Tuesday","Wednesday",
                  "Thursday","Friday","Saterday"};

int main(void)
{
    uint8_t tbuf[40];
    uint8_t t = 0;

    HAL_Init(); /* Initialize HAL library */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* Set clock, 72Mhz */
    delay_init(72); /* Delay initialization */
    usart_init(115200); /* The serial port is initialized to 115200 */
    usmart_dev.init(72); /* Initialize USMART */
    led_init(); /* Initialize LED */
    lcd_init(); /* Initialize LCD */
    rtc_init(); /* Initialize RTC */

    rtc_set_alarm(2020, 4, 26, 9, 23, 45); /* Set an alarm clock */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "RTC TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);

    while (1)
    {
        t + + ;

        if ((t % 10) == 0) /* Update display data every 100ms */
        {
            rtc_get_time();
            sprintf((char *)tbuf, "Time: d: d: d", calendar.hour, calendar.min, calendar.sec);
            lcd_show_string(30, 120, 210, 16, 16, (char *)tbuf, RED);
            sprintf((char *)tbuf, "Date: d- d- d", calendar.year, calendar.month, calendar.date);
            lcd_show_string(30, 140, 210, 16, 16, (char *)tbuf, RED);
            sprintf((char *)tbuf, "Week:%s", weekdays[calendar.week]);
            lcd_show_string(30, 160, 210, 16, 16, (char *)tbuf, RED);
        }

        if ((t % 20) == 0)
        {
            LED0_TOGGLE(); /* Every 200ms, flip LED0 */
        }

        delay_ms(10);
    }
}