This time we will go through ARM Cortex-M0 LPC1114 Timer Tutorial. In a previous LPC1114 tutorial we did a blinky example using GPIO and harcoded delays, now its time to improvise and use precise delay using timers. LPC111x MCUs have two 16-bit Timers and two 32-bit Timers. Both of them are similar in operation except for the bit size. We will be using 32-bit timers in this tutorial. Each Timer block can be used as a ‘Timer’ (e.g. triggering an interrupt every ‘t’ microseconds) or as a ‘Counter’ and can be also used to demodulate PWM signals given as input, find frequency of external signal, etc.
Introduction
Each Timer module has its own Timer Counter(TC) and Prescale Register(PR) associated with it. When a Timer is Reset and Enabled, the TC is set to 0 and incremented by 1 every ‘PR+1’ clock cycles – where PR is the value stored in Prescale Register. When it reaches its maximum value it gets reset to 0 and hence restarts counting. Prescale Register is used to define the resolution of the timer. If PR is 0 then TC is incremented every 1 clock cycle of the peripheral clock. If PR=1 then TC is incremented every 2 clock cycles of peripheral clock and so on. By setting an appropriate value in PR we can make timer increment or count: every peripheral clock cycle or 1 microsecond or 1 millisecond or 1 second and so on.
LPC1114/302 (LPC1100L Series) which is found on LPCXpresso Board has four Match Outputs and one Capture Input.
Match Register
A Match Register is a Register which contains a specific value set by the user. When the Timer starts – every time after TC is incremented the value in TC is compared with match register. If it matches then it can Reset the Timer or can generate an interrupt as defined by the user. We are only concerned with match registers in this tutorial.
Match Registers can be used to:
- Stop Timer on Match(i.e when the value in count register is same as than in Match register) and trigger an optional interrupt.
- Reset Timer on Match and trigger an optional interrupt.
- To count continuously and trigger an interrupt on match.
External Match Output
When a corresponding Match register(MRx) equals the Timer Counter(TC) the match output can be controlled using External Match Register(EMR) to : either toggle, go HIGH, go LOW or do nothing.
Capture Register
As the name suggests it is used to Capture Input signal. When a transition event occurs on a Capture pin , it can be used to copy the value of TC into any of the 4 Capture Register or to genreate an Interrupt. Hence these can be also used to demodulated PWM signals. We are not going to use them in this tutorial since we are only concerned with using Timer block as a ‘Timer’. We’ll see them in a later tutorial.
Registers used in LPC111x Timer Programming
We will be using CMSIS based lpc11xx.h header file for programming. In CMSIS, all the Registers used to program and use 32-bit timers are defined as members of structure(pointer) LPC_TMR32Bx where x is the 32-bit Timer module 0 or 1. So for Timer0 we will use LPC_TMR32B0 and so on. Registers can be accessed by de-referecing the pointer using “->” operator. For, example we can access TCR of Timer0 block as LPC_TMR32B0->TCR. Similary for 16-bit Timer block, the structure is defined as LPC_TMR16Bx where x is the 16-bit Timer module 0 or 1. Now lets see some of the main 32-bit timer registers.
1) PR (TMR32BxPR): Prescale Register – Stores the maximum value of Prescale counter after which it is reset.
2) PC (TMR32BxPC): Prescale Counter Register – This register increments on every PCLK(Peripheral clock). This register controls the resolution of the timer. When PC reaches the value in PR , PC is reset back to 0 and Timer Counter is incremented by 1. Hence if PR=0 then Timer Counter Increments on every 1 PCLK. If PR=9 then Timer Counter Increments on every 10th cycle of PCLK. Hence by selecting an appropriate prescale value we can control the resolution of the timer.
3) TC (TMR32BxTC): Timer Counter Register – This is the main counting register. Timer Counter increments when PC reaches its maximum value as specified by PR. If timer is not reset explicitly(directly) or by using an interrupt then it will act as a free running counter which resets back to zero when it reaches its maximum value which is 0xFFFFFFFF.
4) TCR (TMR32BxTCR): Timer Control Register – This register is used to enable , disable and reset TC. When bit0 is 1 timer is enabled and when 0 it is disabled. When bit1 is set to 1 TC and PC are set to zero together in sync on the next positive edge of PCLK. Rest of the bits of TCR are reserved.
5) CTCR (TMR32BxCTCR): Count Control register – Used to select Timer/Counter Mode. When the value of the CTCR is set to 0x0 Timer Mode is selected.
6) MCR (TMR32BxMCR): Match Control register – This register is used to control which all operations can be done when the value in MR matches the value in TC. Bits 0,1,2 are for MR0 , Bits 3,4,5 for MR1 and so on.. Here’s a quick table which shows the usage:
For MR0:
- Bit 0 : Interrupt on MR0 i.e. trigger an interrupt when MR0 matches TC. Interrupts are enabled when set to 1 and disabled when set to 0.
- Bit 1 : Reset on MR0. When set to 1 , TC will be reset when it matched MR0. Disabled when set to 0.
- Bit 2 : Stop on MR0. When set to 1 , TC & PC will stop when MR0 matches TC.
Similarly bits 3-5 , 6-8 , 9-11 are for MR1 , MR2 , MR3 respectively.
7) IR (TMR32BxIR): Interrupt Register – It contains the interrupt flags for 4 match and 2 capture interrupts. Bit0 to bit3 are for MR0 to MR3 interrupts respectively. And similarly the next 2 for CR0 & CR1 interrupts. when an interrupt is raised the corresponding bit in IR will be set to 1 and 0 otherwise. Writing a 1 to the corresponding bit location will reset the interrupt – which is used to acknowledge the completion of the corresponding ISR execution.
8) EMR (TMR32BxEMR): External Match Register – It provides both status and control of External Match Output Pins. First four bits are for EM0 to EM3. Next 8 bits are for EMC0 to EMC3 in pairs of 2.
- Bit 0 – EM0: External Match 0. When a match occurs between TC and MR0, depending on bits[5:4] i.e. EMC0 of this register, this bit can either toggle, go LOW, go HIGH, or do nothing. This bit is driven to MATx.0 where x=Timer number.
- Similarly for Bits 1 (EM1), 2 (EM2) & 3 (EM3).
- Bits[5:4] – EMC0: External Match 0. The values in these bits select the functionality of EM0 as follows:
- 0x0 – Do nothing
- 0x1 – Clear the corresponding External Match output to 0 (MATx.m pin is LOW).
- 0x2 – Set the corresponding External Match output to 1 (MATx.m pin is HIGH).
- 0x3 – Toggle the corresponding External Match output.
- Similarly for Bits[7:6] – EMC1, Bits[9,8] – EMC2, Bits[11:10] – EMC3.
Configuring Timers in LPC1114
To use timers we need to first configure & initialize them. We need to set appropriate values in CTCR, IR, PR registers and reset PC, TC registers. Finally we assign TCR = 0x01 which enables the timer.
- Enable Clock Input for the Timer Block using LPC_SYSCON->SYSAHBCLKCTRL register
- Set appropriate value in LPC_TMR32Bx->CTCR
- Define the Prescale value in LPC_TMR32Bx->PR
- Set Value(s) in Match Register(s) if required
- Set appropriate value in LPC_TMR32Bx->MCR if using Match registers / Interrupts
- Reset Timer using LPC_TMR32Bx->TCR – Which resets PR and TC
- Set LPC_TMR32Bx->TCR to 0x01 to Enable the Timer when required
- Reset LPC_TMR32Bx->TCR to 0x00 to Disable the Timer when required
Selecting Match Outputs: Match outputs for Timer0(CT32B0) and Timer1(CT32B1) is available as an alternate function on pins given below. Match output function must be selected using IOCON registers for the following pins:
- Timer0 MAT 0 (CT32B0_MAT0) – PIO1_6
- Timer0 MAT 1 (CT32B0_MAT1) – PIO1_7
- Timer0 MAT 2 (CT32B0_MAT2) – PIO0_1
- Timer0 MAT 3 (CT32B0_MAT3) – PIO2_8
- Timer1 MAT 0 (CT32B1_MAT0) – PIO1_1
- Timer1 MAT 1 (CT32B1_MAT1) – PIO1_2
- Timer1 MAT 2 (CT32B1_MAT2) – PIO1_3
- Timer1 MAT 3 (CT32B1_MAT3) – PIO1_4
You can go through my previous tutorial on IOCON register regarding its usage.
Enabling System AHB Clock for Timer: The input clock for timers is from System AHB clock or simply the System Clock. The System Clock can be derived from the Main Clock using System AHB Clock Divider Register SYSAHBLKDIV. System Clock for Peripherals like Timers needs to be enabled, before using them, by setting appropriate bits (1=enable, 0=disable) in System AHB Clock Control Register SYSAHBCLKCTRL as given below:
- Bit 7 For 16-Bit Timer0 (CT16B0)
- Bit 8 For 16-Bit Timer1 (CT16B1)
- Bit 9 For 32-Bit Timer0 (CT32B0)
- Bit 10 For 32-Bit Timer1 (CT32B1)
LPC1114 Timer Prescaler Calculations:
The delay or time required for 1 clock cycle when PCLK/CCLK(System Clock) = ‘X’ Mhz is given by:
=
Seconds
It is also the maximum resolution Timer block can provide at a given PCLK(CCLK) frequency of X Mhz. The general formula for Timer resolution at X Mhz PCLK(CCLK) and a given value for prescale (PR) is as given below:
=
Seconds
Hence, we get the Formula for Prescaler (PR) for required Timer resolution (TRES in Secs) at given PCLK(in Hz) frequency as:
Note that here, the resolution is also the time delay required to increment TC by 1.
Hence, Prescaler value for 1 micro-second resolution/ 1us time delay at 48 Mhz PCLK(CCLK) is,
Prescale for 1 mS (milli-second) resolution at 48Mhz PCLK(CCLK) is,
Now lets implement to basic function required for Timer Operation:
1. void initTimer0(void);
2. void delayMS(unsigned int milliseconds);
#1) initTimer0(void); [Used in Example #1]
#define PRESCALE (48000-1)
void initTimer0(void)
{
/*Assuming CCLK (System Clock) = 48Mhz (set by startup code)*/
LPC_TMR32B0->CTCR = 0x0;
LPC_TMR32B0->PR = PRESCALE; //Increment TC at every 47999+1 clock cycles
//48000 clock cycles @48Mhz = 1 mS
LPC_TMR32B0->TCR = 0x02; //Reset Timer
}
#2) delayMS(unsigned int milliseconds); – LPC1114 Timer Delay Function
void delayMS(unsigned int milliseconds) //Using Timer0
{
LPC_TMR32B0->TCR = 0x02; //Reset Timer
LPC_TMR32B0->TCR = 0x01; //Enable timer
while(LPC_TMR32B0->TC < milliseconds); //wait until timer counter reaches the desired delay
LPC_TMR32B0->TCR = 0x00; //Disable timer
}
Real World LPC1114 Timer Examples with sample code
Example 1) – LPC111x Blinky Example using Timer & GPIO
Now lets write a C/C++ blinky program which flashes an LED every half a second. Since 0.5 second = 500 millisecond we will invoke timer delay function ‘delayMS’ as delayMS(500). The LED is connected to Pin PIO0_7.
/*(C) Umang Gajera - www.ocfreaks.com 2011-17.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
LPC1114 Basic Timer example. License: GPL.*/
#include <lpc11xx.h>
#define PRESCALE (48000-1) //48000 PCLK clock cycles to increment TC by 1
void delayMS(unsigned int milliseconds);
void initTimer0(void);
int main(void)
{
//SystemInit(); //called by Startup Code before main(), hence no need to call again.
initTimer0(); //Initialize Timer0
LPC_GPIO0->DIR = (1<<7); //Configure PIO0_7 as output
while(1)
{
LPC_GPIO0->DATA = (1<<7); //Turn LED ON
delayMS(500); //0.5 Second(s) Delay
LPC_GPIO0->DATA = ~(1<<7); //Turn OFF LED
delayMS(500);
}
//return 0; //normally this wont execute ever
}
void initTimer0(void)
{
/*Assuming CCLK (System Clock) = 48Mhz (set by startup code)*/
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<9); //Enable 32Bit Timer0 Clock
LPC_TMR32B0->CTCR = 0x0;
LPC_TMR32B0->PR = PRESCALE; //Increment LPC_TMR32B0->TC at every 47999+1 clock cycles
//48000 clock cycles @48Mhz = 1 mS
LPC_TMR32B0->TCR = 0x02; //Reset Timer
}
void delayMS(unsigned int milliseconds) //Using Timer0
{
LPC_TMR32B0->TCR = 0x02; //Reset Timer
LPC_TMR32B0->TCR = 0x01; //Enable timer
while(LPC_TMR32B0->TC < milliseconds); //wait until timer counter reaches the desired delay
LPC_TMR32B0->TCR = 0x00; //Disable timer
}
Example 2) – LPC111x example code using Timer & Match Outputs
In this C/C++ Example we will use Match outputs 0 and 1 of 32-bit Timer 0 i.e. CT32B0_MAT0 and CT32B0_MAT1. CT32B0_MAT0 is pinned to PIO1_6(P1.6) and CT32B0_MAT1 pinned to PIO1_7(P1.7). You can connect LED to any of the Match ouput pin (PIO1_6 or PIO1_7) and see the code in action. Note that, on LPCXpresso 1114 Board PIO1_6 is marked as RXD and PIO1_7 is marked as TXD since these pin have UART alternate functions.
/*(C) Umang Gajera - https://www.ocfreaks.com 2011-17.
More Embedded tutorials @ https://www.ocfreaks.com/cat/embedded/
LPC1114 Timer example 2. License: GPL.*/
#include <lpc11xx.h>
#define PRESCALE (48000-1) //48000 PCLK clock cycles to increment TC by 1
void initTimer0(void);
int main (void)
{
//SystemInit(); //called by Startup Code before main(), hence no need to call again.
LPC_IOCON->PIO1_6 |= 0x2; //Select CT32B0_MAT0 function - Marked as TXD on LPCXpresso Board
LPC_IOCON->PIO1_7 |= 0x2; //Select CT32B0_MAT1 function - Marked as RXD on LPCXpresso Board
initTimer0(); //Initialize Timer0
while(1)
{
//Idle loop
}
//return 0; //normally this won't execute
}
void initTimer0(void)
{
/*Assuming CCLK (System Clock) = 48Mhz (set by startup code)*/
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<9); //Enable 32Bit Timer0 Clock
LPC_TMR32B0->CTCR = 0x0;
LPC_TMR32B0->PR = PRESCALE; //Increment LPC_TMR32B0->TC at every 47999+1 clock cycles
//48000 clock cycles @48Mhz = 1 mS
LPC_TMR32B0->MR0 = 500; //toggle time in mS
LPC_TMR32B0->MCR = (1<<1); //Reset on MR0 Match
LPC_TMR32B0->EMR |= (1<<7) | (1<<6) | (1<<5) | (1<<4); //Toggle Match output for MAT0.0(P2.5), MAT0.1(P2.6)
LPC_TMR32B0->TCR = 0x02; //Reset Timer
LPC_TMR32B0->TCR = 0x01; //Enable timer
}
Example 3) – LPC1114 Timer Interrupt Example Code
Here we will use a timer interrupt function which will be called periodically to blink an LED. The Timer Interrupt Service Routine (ISR) will toggle PIO0_7(P0.7) every time it is called. If you are using C++ file then you will need to add extern “C” before the ISR definition or C++ linker won’t be able to link it properly to generate finally binary/hex file. For C code this is not required.
/*(C) Umang Gajera - https://www.ocfreaks.com
More Embedded tutorials @ https://www.ocfreaks.com/cat/embedded/
LPC1114 Timer example 3. License: GPL.*/
#include <lpc11xx.h>
#define PRESCALE (48000-1) //48000 PCLK clock cycles to increment TC by 1
void initTimer0();
int main(void)
{
//SystemInit(); //called by Startup Code before main(), hence no need to call again.
LPC_GPIO0->DIR |= (1<<7); //set PIO0_7 as output
initTimer0();
while(1)
{
//Idle loop
}
//return 0; //normally this won't execute
}
void initTimer0(void)
{
/*Assuming CCLK (System Clock) = 48Mhz (set by startup code)*/
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<9); //Enable 32Bit Timer0 Clock
LPC_TMR32B0->CTCR = 0x0;
LPC_TMR32B0->PR = PRESCALE; //Increment LPC_TMR32B0->TC at every 47999+1 clock cycles
//48000 clock cycles @48Mhz = 1 mS
LPC_TMR32B0->MR0 = 500; //Toggle Time in mS
LPC_TMR32B0->MCR |= (1<<0) | (1<<1); // Interrupt & Reset on MR0 match
LPC_TMR32B0->TCR |= (1<<1); //Reset Timer0
NVIC_EnableIRQ(TIMER_32_0_IRQn); //Enable timer interrupt
LPC_TMR32B0->TCR = 0x01; //Enable timer
}
extern "C" void TIMER32_0_IRQHandler(void) //Use extern "C" so C++ can link it properly, for C it is not required
{
LPC_TMR32B0->IR |= (1<<0); //Clear MR0 Interrupt flag
LPC_GPIO0->DATA ^= (1<<7); //Toggle LED
}