<h2 style='border: none'><center>Embedded Systems Lab 05</center></h2> ### <center>Timers & Interrupts</center> ###### Author: Mohammed Nafiz ALMadhoun --- <p style='text-align: justify;'> In this lab, we are going to talk about Timers, then we will talk about interrupts and what is the benefits of interrupts, finally, we will setup timers and use interrupts to catch the timers events. </p> ### **Timers in Microcontrollers** <p style='text-align: justify;'> As you may have noticed in the previous labs, we've used timing for a lot of tasks, from the first task that toggles a LED every 1 sec, to controlling LCD which requires timing constraints. </p> <p style='text-align: justify;'> One of the main issues we have faced is that we don't know how much a code will take to execute, and the blocking behaviour of doing timing using loops. </p> <p style='text-align: justify;'> The previous method will be obsolete if we've considered creating a medium system that does more than one task, so using the integrated peripherals in the microcontrollers to archive this task will give us the potential to create a bigger system with limited resources. </p> <p style='text-align: justify;'> By returning to your OS course, you may notice that timers play a critical role in task scheduling, and without timers, your OS will depend on the programmes itself to give back control to the OS, which means if a program hangs the OS will never take control! </p> ### **Interrupts in Microcontrollers** <p style='text-align: justify;'> If you think about computers in general, the sequential execution behaviour is one of the most important things the guarantee that our code will work correctly, but without considering the cases where you need the CPU to work on a certain thing the computers will struggle in pooling to know the state of its components. </p> <p style='text-align: justify;'> For example, if you have a microcontroller with a lot of buttons, if you need to know which button is pressed you will have to loop and check each button individually, but with interrupts and working at a hardware level, the microcontroller could sense any changes in the buttons signals and execute a certain chunk of code. </p> <p style='text-align: justify;'> The interrupts mechanism is not a trivial thing that just executes code when a certain event happens, the CPU will be responsible for saving the state of the previous code to make sure that we can return from the interrupt to the same statue we've been before. </p> <p style='text-align: justify;'> This will require us to setup an interrupt table that points to the code we want to execute when certain events happen (e.g. Code execution exception, timers, and UART). </p> ### **Setting up Timers** As you may notice from your course, there are different types of timers for example: 1. SysTick Timer: Used for normal main control operations (e.g OS process handler) 2. TPM Timers: Used for normal timing stuff (Shin a LED!) 3. Watchdog Timers: Used in/for safety-critical systems (e.g Blue screen in Windows). Note: *The SysTick timer is CPU architecture-specific (e.g. Cortex-M0), the second and third one is just an extension (A peripheral).* ### **SysTick Setup** Please return to the [LPC111x User Manual](https://www.keil.com/dd/docs/datashts/nxp/lpc11xx/lpc111x_lpc11cxx_um.pdf) Chapter 24. <p style='text-align: justify;'> You will find in the first section that the SysTick timer does not export its singles to any pins, so to monitor the SysTick we will need to setup a GPIO pin, then you will notice that the clock for the SysTick timer is the CPU clock, or an external clock using (CPU clock)/2 as reference. Note: The SysTick timer is a 24-bit timer that counts down to zero. Return to Table 356 to see the registers of the SysTick timer. </p> #### **Init clock out to watch the CPU clock** This section is very similar to Monitoring Clock Section in the previous Lab, but in this lab, we will use the LPC11x header file, which contains definitions for peripherals registers. ```c= #include <LPC11xx.h> int main (void) { LPC_SYSCON->CLKOUTCLKSEL = 0x3; // Clock source = CPU clock LPC_SYSCON->CLKOUTDIV = 0x1; // Div by 1 LPC_SYSCON->CLKOUTUEN = 0x0; // Enabling Seq. LPC_SYSCON->CLKOUTUEN = 0x1; LPC_IOCON->PIO0_1 = 0x1; // Set PIO0_1 As clock out while (1); } ``` After knowing what is our CPU clock, we can easily calculate the SysTick reload reg to create 1-second timers (Reload Register = Frequnacy In Hz). #### **Init SysTick** ```c= ... SysTick->LOAD = 12000000; // CPU clock = 12MHz SysTick->VAL = 0; // As recommended in the manual SysTick->CTRL = 0x5; // 0b101 Enable and Select CPU clock while (1) { if (SysTick->CTRL & 1<<16) { LPC_GPIO2->DATA ^= 0x1; // Flip the first bit SysTick->CTRL = 0x5; // Trick to fix Proteus bug } } ... ``` <p style='text-align: justify;'> You will notice that we are just waiting for the timer to reach zero, so we can know for sure that one second passed, this code is not useful as it will just waste our CPU time waiting for the timer, so in the next section, we will setup SysTick to fire an interrupt every one second. </p> #### **Setting up an interrupt** Please return to your assembly course to understand vector table. At first we will define a function to handle our SysTick intrupt. ```c= void IntSysTickHandler(void) { LPC_GPIO2->DATA ^= 0x1; } ``` <p style='text-align: justify;'> Then we will need to setup vtable.c in our project to set this function as the SysTick interrupt, note that if you just write the function name in vtable.c, the compiler won't know where is your handler, so you should mark it as extern function in the file. </p> ```c= extern void IntSysTickHandler(void); ``` Note: *Return to the user manual section 28.4.3.4 to know more about our vector table.* ### **TPM Timers Setup** <p style='text-align: justify;'> In this section we are going to talk about 16-bit timers, these timers are peripherals that are integrated with our microcontroller (LPC111x), these timers could be configured to achieve a lot of different tasks, in the first example we will use a timer as a simple timer to toggle a LED every 1 second, then we will use it as PWM output which can drive a lot of other components (e.g. DC Motors, RGB LEDs). </p> Note: *Return to chapter 18 in the user manual to understand more.* #### **Free running timer** ```c= #include <LPC11xx.h> int main (void) { LPC_GPIO2->DIR |= 0x1; // Set Port 2 Pin 0 as OUTPUT LPC_GPIO2->DATA = 0x0; // Set Port 2 Pin 0 as LOW LPC_SYSCON->SYSAHBCLKCTRL |= 1 << 7; // Enable TMR16B0 CLK LPC_TMR16B0->PR = 9999; // Set Prescaler as 9999, so CLK/10000 LPC_TMR16B0->TCR = 0x1; // Enable Timer while (1) { if (LPC_TMR16B0->TC >= 1200){ LPC_GPIO2->DATA ^= 0x1; // Flip Port 2 Pin 0 LPC_TMR16B0->TCR = 0x3; // Reset the timer LPC_TMR16B0->TCR = 0x1; // Unreset tje timer } } return 0; } ``` At the setup stage, we've enabled the Timer clock, then we set the Prescaler register to 9999, which mean the clock will be divided by 10000, finally, we enabled the timer. Then in the loop stage, we've checked if the timer passed the value 1200 (12MHz/10000), if true, we will flip the pin then reset the timer. #### **One Second Timer Intrrupt** In this example we've used an external GPIO, but you can use External Match Pins (see table 290). ```c= #include <LPC11xx.h> void IntCT16B0Handler(void) { // Handler, add it to vtable.c if (LPC_TMR16B0->IR & 0x1) { // Check if the event on MR0 LPC_GPIO2->DATA ^= 0x1; // Flip the pin LPC_TMR16B0->IR = 0x1; // Reset IR event on MR0 } } int main (void) { LPC_GPIO2->DIR |= 0x1; LPC_GPIO2->DATA = 0x0; LPC_SYSCON->SYSAHBCLKCTRL |= 1 << 7; LPC_TMR16B0->PR = 9999; LPC_TMR16B0->TCR = 0x1; LPC_TMR16B0->MR0 = 1200; // Set MR0 to 1200 LPC_TMR16B0->MCR = 0x3 ; // Enable Intrrupt and Reset for MR0 NVIC_EnableIRQ(TIMER_16_0_IRQn); // Enable TMR16B0 IRQ // TIMER_16_0_IRQn = 16, see LPC11xx.h while (1) ; // Do whatever you want! return 0; } ``` Note: *The TMR16B0 is a Maskable Interrupt, which we should enable by writing to ISER register, or use NVIC_EnableIRQ.* #### **PWM Timer** In this mode, we will use the timers as PWM output, please return to your textbook to understand PWM, to use PWM functionality in the timer, you should set the values of PWMC, you can select the PWM cycle length using a match register, or you can consider the max value of the counter as your cycle length. <center> ![Figure 1: Pulse width modulation](https://i.imgur.com/7xzE9xk.png =400x) Figure 1: Pulse Width Modulation. [^1] </center> [^1]: [What is PWM: Pulse Width Modulation - CircuitDigest](https://circuitdigest.com/tutorial/what-is-pwm-pulse-width-modulation) In the following example, we will use the PWM to control an RGB LED, which will shine in the color according to the voltages on its legs, not that we won't convert any digital output to analog, but we will use the PWM to emulate the voltage. ```c= #include <LPC11xx.h> int main (void) { LPC_SYSCON->SYSAHBCLKCTRL |= 1 << 7; LPC_TMR16B0->PR = 0xF; LPC_TMR16B0->TCR = 0x1; LPC_IOCON->PIO0_8 = 0x2; // Select the output to CT16B0_MAT0 LPC_IOCON->PIO0_9 = 0x2; // Select the output to CT16B0_MAT1 LPC_IOCON->JTAG_TCK_PIO0_10 = 0x3; // Select the output to CT16B0_MAT2 LPC_TMR16B0->PWMC = 0xF; // Enable PWM for M0 to M3 LPC_TMR16B0->MR0 = 255 - 123; // 255 - R LPC_TMR16B0->MR1 = 255 - 230; // 255 - G LPC_TMR16B0->MR2 = 255 - 100; // 255 - B LPC_TMR16B0->MR3 = 256; // PWM cycle length LPC_TMR16B0->MCR = 1 << 10; // Reset PWM when MR3 Match while (1) ; return 0; } ``` <center> ![Figure 2: How you will connect the LED](https://i.imgur.com/oMmzXNr.png) Figure 2: How you will connect the LED in simulation. *You will need some resistors in real life* </center> <center> ### END OF LAB 5</center> <div style="display:none"> # Embedded Systems Lab 05 </div>