RTC (Real Time Clock) in ARM LPC2148

Introduction to RTC

Real Time Clock (RTC) is a time counter that counts real time continuously.

  • We can get present time details using RTC. It gives us time as well as date information.
  • RTC is clocked by separate 32.768KHz oscillator to continuously keep track of current time.
  • RTC is powered either by main system power or separately by battery backup in absence of main system power.
  • Since RTC needs to be running continuously, it should be provided with battery backup in case of main system power failure. Otherwise, it can lose present time information.
  • We can easily program the RTC with the current date and time information in the case of loss time and date information due to power failure.
  • But this is not a feasible solution in real time systems once they are deployed into the market, where there are many time constraints on the tasks and timely decision making is important. In such cases, the RTC must not fail. This makes battery backup of utmost importance.

 

LPC2148 has an inbuilt RTC. LPC2148RTC can be clocked by a separate 32.768 KHz oscillator or by a programmable prescale divider based on the APB clock.

 

  • It maintains a calendar and clock and provides seconds, minutes, hours, month, year, day of week, day of month and day of year.
  • It has power supply pin that can be connected to a battery or to the main 3.3V.
  • It uses little power in power down mode.
  • And most important, it has Alarm functionality.

 

ARM LPC2148 RTC Pins

LPC2148 RTC Pins
LPC2148 RTC Pins

 

RTCX1 & RTCX1 (RTC Crystal Pins)

These pins are used to connect RTC external crystal. Generally, 32.768 kHz crystal oscillator is connected to these pins.

VBAT (RTC Power pin)

Power to the RTC unit is given through this pin. Either it will be from main system power or from battery backup mechanism.

Now, let’s see RTC Registers that are used to set and control RTC operations.

 

RTC Registers Configuration

1.  ILR (Interrupt Location Register)

  • It is an 8-bit register.
  • It specifies which blocks are generating an interrupt.
ILR (Interrupt Location Register)
ILR (Interrupt Location Register)
  • Bit 0 – RTCCIF
    When this bit is 1, it means that the counter increment interrupt block generated an interrupt.
    Writing a 1 to this bit clears the counter increment interrupt. Writing a 0 has no effect.
  • Bit 1 – RTCALF
    When this bit is 1, it means that the alarm registers generated an interrupt.
    Writing a 1 to this bit clears the alarm interrupt. Writing a 0 has no effect.

 

2.  CTCR (Clock Tick Counter Register)

  • It is a 16-bit register.
  • It is a read only register.
CTCR (Clock Tick Counter Register)
CTCR (Clock Tick Counter Register)
  • It can be reset through the Clock Control Register (CCR).
  • It consists of the bits of the clock divide counter.
  • Bits 14:1 – Clock Tick Counter
    Prior to the seconds counter, the CTC counts 32,768 clocks per second. Due to RTC Prescalar, these 32,768 time increments may not all be of the same duration.

 

3.  CCR (Clock Control Register)

  • It is an 8-bit register.
  • It controls the operation of the clock divide circuit.
CCR (Clock Control Register)
CCR (Clock Control Register)
  • Bit 0 – CLKEEN (Clock Enable)
    0 = Timer counters are disabled. They should be initialized in this condition.
    1 = Timer counters are enabled
  • Bit 1 – CTRST (CTC Reset)
    When 1, elements in CTC (Clock Tick Counter) are reset. The elements remain reset until this bit is changed to 0.
  • Bit 3:2 – CTTEST (Test Enable)
    These bits should always be 0 during normal operation.
  • Bit 4 – CLKSRC
    0 = CTC takes clock from Prescalar
    1 = CTC takes clock from 32.768 kHz oscillator

 

Note: It is possible to use both 32.768kHz external oscillator as well as prescaler to run the RTC.

If 32.768 kHz oscillator is used to run the RTC, the RTC maintains the exact time (registers are updated by the 32.768 kHz clock which is powered by the battery) even if the power to the board is cut (only if a 3V battery is connected to the battery pins of the RTC).

If peripheral clock is used to run the RTC, the RTC maintains the register values (registers are not updated since there is no clock source) present at the time of power cut (only if a 3V battery is connected to the battery pins of the RTC).

 

4.  CIIR (Counter Increment Interrupt Register)

  • It is an 8-bit register.
CIIR (Counter Increment Interrupt Register)
CIIR (Counter Increment Interrupt Register)
  • It provides the option to generate an interrupt every time a counter is incremented.
  • This interrupt remains valid until it is cleared by writing a 1 to RTCCIF bit in ILR.
  • Bit 0 – IMSEC
    When 1, an increment of the Seconds value generates an interrupt.
  • Bit 1 – IMMIN
    When 1, an increment of the Minutes value generates an interrupt.
  • Bit 2 – IMHOUR
    When 1, an increment of the Hours value generates an interrupt.
  • Bit 3 – IMDOM
    When 1, an increment of the Day of Month value generates an interrupt.
  • Bit 4 – IMDOW
    When 1, an increment of the Day of Week value generates an interrupt.
  • Bit 5 – IMDOY
    When 1, an increment of the Day of Year value generates an interrupt.
  • Bit 6 – IMMON
    When 1, an increment of the Month value generates an interrupt.
  • Bit 7 – IMYEAR 
    When 1, an increment of the Year value generates an interrupt.

 

5.  AMR (Alarm Mask Register)

  • It is an 8-bit register.
AMR (Alarm Mask Register)
AMR (Alarm Mask Register)
  • It allows user to mask any of the alarm registers.
  • Writing a 1 to any bit in this register masks the corresponding register.
  • For alarm function, every non-masked alarm register must match the corresponding time counter for an interrupt to be generated.
  • The interrupt is generated only when the counter comparison first changes from no match to match.
  • The interrupt is removed by writing 1 to the RTCALF bit in ILR.
  • If all the bits in this register are set, then the alarm is disabled.
  • Bit 0 – AMRSEC
    When 1, the Seconds value is not compared for alarm.
  • Bit 1 – AMRMIN
    When 1, the Minutes value is not compared for alarm.
  • Bit 2 – AMRHOUR
    When 1, the Hours value is not compared for alarm.
  • Bit 3 – AMRDOM
    When 1, the Day of Month value is not compared for alarm.
  • Bit 4 – AMRDOW
    When 1, the Day of Week value is not compared for alarm.
  • Bit 5 – AMRDOY
    When 1, the Day of Year value is not compared for alarm.
  • Bit 6 – AMRMON
    When 1, the Month value is not compared for alarm.
  • Bit 7 – AMRYEAR
    When 1, the Year value is not compared for alarm.

 

6.  Consolidated Time Registers

  • The 3 consolidated time registers together provide an alternative to reading the values of 8 time counter registers.
  • These are read only registers.
  • These are 32-bit registers.

​​​​​​

       a)  CTIME0 (Consolidated Time Register 0)

CTIME0 (Consolidated Time Register 0)
CTIME0 (Consolidated Time Register 0)
  • Bits 5:0 – Seconds
    Seconds value in the range of 0 to 59.
  • Bits 13:8 – Minutes
    Minutes value in the range of 0 to 59.
  • Bits 20:16 – Hours
    Hours value in the range of 0 to 23.
  • Bits 26:24 – Day of Week
    Day of Week value in the range of 0 to 6.

 

        b)  CTIME1 (Consolidated Time Register 1)

CTIME1 (Consolidated Time Register 1)
CTIME1 (Consolidated Time Register 1)
  • Bits 4:0 – Day of Month
    Day of Month value in the range of 1 to 28, 29, 30 or 31 (depending on the month and whether it is a leap year).
  • Bits 11:8 – Month
    Month value in the range of 1 to 12.
  • Bits 27:16 – Year
    Year value in the range of 0 to 4095.

 

        c)  CTIME2 (Consolidated Time Register 2)

CTIME2 (Consolidated Time Register 2)
CTIME2 (Consolidated Time Register 2)
  • Bits 11:0 – Day of Year
    Day of Year value in the range of 1 to 365 (366 for leap years).

 

7.  Time Counter Group

  • It consists of 8 counters.
  • These can be read/write.
  • They are given in the table below.
NameSizeDescription
SEC6Seconds value in the range of 0 to 59
MIN6Minutes value in the range of 0 to 59
HOUR5Hours value in the range of 0 to 23
DOM5Day of Month value in the range of 1 to 28, 29, 30 or 31 (depending on month and whether it is a leap year)
DOW3Day of Week value in the range of 0 to 6
DOY9Day of Year value in the range of 1 to 365 (366 for leap years    0
MONTH4Month value in the range of 1 to 12
YEAR12Year value in the range of 0 to 4095

 

8.  Alarm Register Group

  • It consists of 8 counters.
  • The values in these registers are compared with the timer counters.
  • If all unmasked alarm registers match their corresponding time counters, then an interrupt is generated.
  • These registers are given in the table below.
NameSizeDescription
ALSEC6Alarm value for Seconds
ALMIN6Alarm value for Minutes
ALHOUR5Alarm value for Hours
ALDOM5Alarm value for Day of Month
ALDOW3Alarm value for Day of Week
ALDOY9Alarm value for Day of Year
ALMON4Alarm value for Month
ALYEAR12Alarm value for Year

 

9.  PREINT (Prescaler Integer Register)

  • When PCLK acts as the clock source for RTC, then, this prescaler allows generation of 32.768 kHz reference clock from any PCLK greater than 65.536 kHz (2 * 32.768).
  • This allows RTC to run at proper rate irrespective of the PCLK.
  • PREINT is the integer portion of the prescale value.
  • PREINT = int (PCLK/32768) – 1
  • PREINT must be greater than or equal to 1.
PREINT (Prescaler Integer Register)
PREINT (Prescaler Integer Register)
  • Bits 12:0 – Prescale Integer
    Contains the integer value of the RTC prescaler.

 

10.  PREFRAC (Prescaler Fraction Register)

  • This is the fractional part of the prescale value.
  • PREFRAC = PCLK – ((PREINT+1) * 32768)
PREFRAC (Prescaler Fraction Register)
PREFRAC (Prescaler Fraction Register)
  • Bits 14:0 - Prescale Fraction
    Contains the fraction value of the RTC prescaler.

 

Let’s take example for prescale register values

For PCLK = 15 MHz

PREINT = int (15000000/32768) – 1 = 457 – 1 = 456 (0x1C8)

PREFRAC = 15000000 – ((456+1) * 32768) = 15000000 – 14974976 = 25024 (0x61C0)

 

Note: Leap Year

RTC considers all the years that are divisible by 4 as a leap year. This algorithm is accurate from the year 1901 to 2099, but fails for the year 2100 which is not a leap year.

 

Example

Let’s write a program to set RTC time and generate alarm at desired time. UART0 is used to display time and alarm event on PC terminal.  An LED is also used as an indicator of the alarm. The LED is blinked for 6 seconds when the alarm is turned on.

We will see programs for RTC run using 32.768 kHz crystal as well as using prescaler.

LPC2148 RTC Example
LED Interfacing to LPC2148

 

Program for RTC run using Prescaler

/*
  RTC in LPC2148(ARM7)
  http://www.electronicwings.com/arm7/lpc2148-rtc-real-time-clock
*/

#include <lpc214x.h>
#include <stdint.h>
#include <stdio.h>
#include "UART0.h"

uint8_t alarm, flag;

__irq void RTC_ISR(void)
{
	if (ILR & 0x01)
	{
		flag = 1;
		ILR = ILR | 0x01;
	}
	if (ILR & 0x02)
	{
		alarm = 1;
		ILR = ILR | 0x02;
	}
	VICVectAddr = 0;
}

typedef struct
{
	uint8_t seconds;
	uint8_t minutes;
	uint8_t hours;
	uint8_t day_of_month;
	uint8_t day_of_week;
	uint16_t day_of_year;
	uint8_t month;
	uint16_t year;
}RTC_Time;

void RTC_Set_Time( RTC_Time Set_Time)
{
	SEC = Set_Time.seconds;
	MIN = Set_Time.minutes;
	HOUR = Set_Time.hours;
	DOM = Set_Time.day_of_month;
	DOW = Set_Time.day_of_week;
	DOY = Set_Time.day_of_year;
	MONTH = Set_Time.month;
	YEAR = Set_Time.year;
}

void RTC_Set_Alarm_Time( RTC_Time Alarm_Time)
{
	ALSEC = Alarm_Time.seconds;
	ALMIN = Alarm_Time.minutes;
	ALHOUR = Alarm_Time.hours;
	ALDOM = Alarm_Time.day_of_month;
	ALDOW = Alarm_Time.day_of_week;
	ALDOY = Alarm_Time.day_of_year;
	ALMON = Alarm_Time.month;
	ALYEAR = Alarm_Time.year;
}

RTC_Time RTC_Get_Time(void)
{
	RTC_Time time;
	time.seconds = SEC;
	time.minutes = MIN;
	time.hours = HOUR;
	time.day_of_month = DOM;
	time.day_of_week = DOW;
	time.day_of_year = DOY;
	time.month = MONTH;
	time.year = YEAR;
	return time;
}

int main(void)
{
	/* Setting Time + Alarm */
	RTC_Time set_time, alarm_time, current_time;
	char timestr[30], datestr[30];
	alarm = 0;
	flag = 0;
	IO0DIR = 0x00000010; /* P0.4 as output pin for LED */
	UART0_init();
	ILR = 0x0; 			/* No RTC interrupts */
	CCR = 0x02; 		/* Reset Clock Tick Counter */
	CCR = 0x00; 
	CIIR = 0x00; 		/* No interrupts */
	AMR = 0x00; 		/* Alarm registers not masked */
	PREINT = 0x01C8; 	/* For 15MHz Fpclk, PREINT value */
	PREFRAC = 0x61C0; 	/* For 15MHz Fpclk, PREFRAC value */
	VICVectAddr0 = (unsigned) RTC_ISR;
	VICVectCntl0 = 0x0000002D;
	VICIntEnable = 0x00002000;
	VICIntSelect = 0x00000000;
	set_time.seconds = 00;
	set_time.minutes = 25;
	set_time.hours = 11;
	set_time.day_of_month = 6;
	set_time.day_of_week = 5;
	set_time.day_of_year = 279;
	set_time.month = 10;
	set_time.year = 2017;
	RTC_Set_Time(set_time);
	CIIR = 0x01; 		/* Seconds value increment interrupt */
	alarm_time.seconds = 15;
	alarm_time.minutes = 25;
	alarm_time.hours = 11;
	alarm_time.day_of_month = 6;
	alarm_time.day_of_week = 5;
	alarm_time.day_of_year = 279;
	alarm_time.month = 10;
	alarm_time.year = 2017;
	RTC_Set_Alarm_Time(alarm_time);	
	CCR = 0x01; 		/* Clock Enable */
	ILR = 0x03; 		/* RTC interrupts enabled */
	IO0CLR = 0x00000010;
	/* Set the Time and Alarm once using above code lines */
	/* Once the time and alarm is set, comment out the above code lines and uncomment the code lines for "Only RTC Read" and program the device */	
	/* If this is not done, the time will be set repeatedly to same value whenever the device is reset or powered */
	

	/* Only RTC Read */
//	RTC_Time current_time;
//	char timestr[30], datestr[30];
//	alarm = 0;
//	flag = 0;
//	IO0DIR = 0x00000010; 	/* P0.4 as output pin for LED */
//	UART0_init();
//	CCR = 0x02; 			/* Reset Clock Tick Counter */
//	AMR = 0x00; 			/* Alarm registers not masked */
//	PREINT = 0x01C8; 		/* For 15MHz Fpclk, PREINT value */
//	PREFRAC = 0x61C0; 		/* For 15MHz Fpclk, PREFRAC value */
//	VICVectAddr0 = (unsigned) RTC_ISR;
//	VICVectCntl0 = 0x0000002D;
//	VICIntEnable = 0x00002000;
//	VICIntSelect = 0x00000000;
//	CCR = 0x01; 			/* Clock Enable */
//	ILR = 0x03; 			/* RTC interrupts enabled */
//	IO0CLR = 0x00000010;	
	
	/* Code lines below are common for "Setting time + Alarm" as well as "Only RTC Read" */
	while(1)
	{
		if(alarm == 1)
		{
			current_time = RTC_Get_Time();
			sprintf(timestr,"Alarm!!!: %d:%d:%d \r\n",current_time.hours,current_time.minutes,current_time.seconds);
			UART0_SendString(timestr);
			uint8_t i;
			for(i=0;i<10;i++)
			{
				IO0SET = 0x00000010;
				delay_ms(300);
				IO0CLR = 0x00000010;
				delay_ms(300);
			}
			alarm = 0;
		}
		if (flag == 1)
		{
			current_time = RTC_Get_Time();
			sprintf(timestr,"Time: %d:%d:%d ",current_time.hours,current_time.minutes,current_time.seconds);
			sprintf(datestr,"Date: %d/%d/%d \r\n",current_time.day_of_month,current_time.month,current_time.year);
			UART0_SendString(timestr);
			UART0_SendString(datestr);
			flag = 0;
		}
	}
}

 

Program for RTC run using 32.768kHz crystal 

/*
  RTC in LPC2148(ARM7)
  http://www.electronicwings.com/arm7/lpc2148-rtc-real-time-clock
*/

#include <lpc214x.h>
#include <stdint.h>
#include <stdio.h>
#include "UART0.h"

uint8_t alarm, flag;

__irq void RTC_ISR(void)
{
	if (ILR & 0x01)
	{
		flag = 1;
		ILR = ILR | 0x01;
	}
	if (ILR & 0x02)
	{
		alarm = 1;
		ILR = ILR | 0x02;
	}
	VICVectAddr = 0;
}

typedef struct
{
	uint8_t seconds;
	uint8_t minutes;
	uint8_t hours;
	uint8_t day_of_month;
	uint8_t day_of_week;
	uint16_t day_of_year;
	uint8_t month;
	uint16_t year;
}RTC_Time;

void RTC_Set_Time( RTC_Time Set_Time)
{
	SEC = Set_Time.seconds;
	MIN = Set_Time.minutes;
	HOUR = Set_Time.hours;
	DOM = Set_Time.day_of_month;
	DOW = Set_Time.day_of_week;
	DOY = Set_Time.day_of_year;
	MONTH = Set_Time.month;
	YEAR = Set_Time.year;
}

void RTC_Set_Alarm_Time( RTC_Time Alarm_Time)
{
	ALSEC = Alarm_Time.seconds;
	ALMIN = Alarm_Time.minutes;
	ALHOUR = Alarm_Time.hours;
	ALDOM = Alarm_Time.day_of_month;
	ALDOW = Alarm_Time.day_of_week;
	ALDOY = Alarm_Time.day_of_year;
	ALMON = Alarm_Time.month;
	ALYEAR = Alarm_Time.year;
}

RTC_Time RTC_Get_Time(void)
{
	RTC_Time time;
	time.seconds = SEC;
	time.minutes = MIN;
	time.hours = HOUR;
	time.day_of_month = DOM;
	time.day_of_week = DOW;
	time.day_of_year = DOY;
	time.month = MONTH;
	time.year = YEAR;
	return time;
}

int main(void)
{
	/* Setting Time + Alarm */
	RTC_Time set_time, alarm_time, current_time;
	char timestr[30], datestr[30];
	alarm = 0;
	flag = 0;
	IO0DIR = 0x00000010; /* P0.4 as output pin for LED */
	UART0_init();
	PCONP = (PCONP | (1<<9)); /* PCRTC = 1 */
	/* The RTC registers cannot be written to unless we make PCRTC = 1 */
	ILR = 0x0; /* No RTC interrupts */
	CCR = 0x12; /* 32.768kHz clock and Reset Clock Tick Counter */
	CCR = 0x10; 
	CIIR = 0x00; /* No interrupts */
	AMR = 0x00; /* Alarm registers not masked */
	VICVectAddr0 = (unsigned) RTC_ISR;
	VICVectCntl0 = 0x0000002D;
	VICIntEnable = 0x00002000;
	VICIntSelect = 0x00000000;
	set_time.seconds = 00;
	set_time.minutes = 25;
	set_time.hours = 11;
	set_time.day_of_month = 6;
	set_time.day_of_week = 5;
	set_time.day_of_year = 279;
	set_time.month = 10;
	set_time.year = 2017;
	RTC_Set_Time(set_time);
	CIIR = 0x01; /* Seconds value increment interrupt */
	alarm_time.seconds = 15;
	alarm_time.minutes = 25;
	alarm_time.hours = 11;
	alarm_time.day_of_month = 6;
	alarm_time.day_of_week = 5;
	alarm_time.day_of_year = 279;
	alarm_time.month = 10;
	alarm_time.year = 2017;
	RTC_Set_Alarm_Time(alarm_time);	
	CCR = 0x11; /* 32.768kHz clock and clock Enable */
	ILR = 0x03; /* RTC interrupts enabled */
	IO0CLR = 0x00000010;	
	/* Set the Time and Alarm once using above code lines */
	/* Once the time and alarm is set, comment out the above code lines and uncomment the code lines for "Only RTC Read" and program the device */	
	/* If this is not done, the time will be set repeatedly to same value whenever the device is reset or powered */
	
	
	/* Only RTC Read */
//	RTC_Time current_time;
//	char timestr[30], datestr[30];
//	alarm = 0;
//	flag = 0;
//	IO0DIR = 0x00000010; /* P0.4 as output pin for LED */
//	UART0_init();
//	AMR = 0x00; /* Alarm registers not masked */
//	CCR = 0x10;
//	VICVectAddr0 = (unsigned) RTC_ISR;
//	VICVectCntl0 = 0x0000002D;
//	VICIntEnable = 0x00002000;
//	VICIntSelect = 0x00000000;
//	CCR = 0x11; /* 32.768kHz clock and clock enable */
//	ILR = 0x03; /* RTC interrupts enabled */
//	IO0CLR = 0x00000010;
	
    /* Code lines below are common for "Setting time + Alarm" as well as "Only RTC Read" */
	while(1)
	{
		if(alarm == 1)
		{
			current_time = RTC_Get_Time();
			sprintf(timestr,"Alarm!!!: %d:%d:%d \r\n",current_time.hours,current_time.minutes,current_time.seconds);
			UART0_SendString(timestr);
			uint8_t i;
			for(i=0;i<10;i++)
			{
				IO0SET = 0x00000010;
				delay_ms(300);
				IO0CLR = 0x00000010;
				delay_ms(300);
			}
			alarm = 0;
		}
		if (flag == 1)
		{
			current_time = RTC_Get_Time();
			sprintf(timestr,"Time: %d:%d:%d ",current_time.hours,current_time.minutes,current_time.seconds);
			sprintf(datestr,"Date: %d/%d/%d \r\n",current_time.day_of_month,current_time.month,current_time.year);
			UART0_SendString(timestr);
			UART0_SendString(datestr);
			flag = 0;
		}
	}
}

 

Video


Components Used

ARM7 LPC2148
ARM7 LPC2148
1
Breadboard
Breadboard
1
LED 5mm
LED 5mm
1

Downloads

RTC_uVision_Project Download
LPC2148 Datasheet Download
LPC2148 User Manual Download
Ad