Lab 4 Interrupts ReadMeFirst Lab Folder Content 1) ReadMeFirst 2) Interrupt Vector Table 3) Pin out Summary Objectives Understand how interrupts work Learn to program Interrupt Service Routines in C Language Get familiar with Low Power Mode Review of previous Labs Overview In embedded systems an Interrupt is defined as a signal that notifies the CPU of the occurrence of an event. It is one of the most important concepts in embedded systems. In general, there are two ways events can be recognized by the processor, Polling or Interrupts. In Polling the processor keeps asking periodically if a certain event has occurred, on the other hand, in polling the processor waits for a certain event to happen and is informed when the event happens. The use of Interrupts is much more efficient, because the processor can be put to sleep while waiting for the event and can wake up to process the event only when the event has happened. A typical example is the use of a push button. Let s say we want to turn on the LED when push button S1 is pushed. One way is using a while loop to keep reading the input signal from the push button, in which case we are polling the button. The other way to handle this is to generate an interrupt when an event is captured on the corresponding GPIO pin, and use an interrupt service routine (ISR) to turn on the LED. Compared to the Polling method, which would require 100% CPU load, the Interrupt method allows the CPU to go to sleep or go into other low power mode to conserve power. Background and Foreground Threads A program is usually composed of several individual and independent functions (code segments), the main() function and the ISRs. Since each one begins and ends execution without calling the others, these separate segments of code are called Threads. The main() thread
usually contains an infinite loop that keeps running forever, unless a hardware interrupt occurs. When an interrupt is received by the CPU, it preempts the main() thread and the associated ISR routine is executed. We usually call main() the background thread since it is the default thread, meaning the program always goes back to it when other threads are done. The ISRs are called the foreground threads, which preempt the background thread whenever an interrupt occurs. How Interrupts Works Four elements need to present for an interrupt to be triggered and handled properly by the processor, as shown below: 1. Occurrence of an Interrupt 2. Flagged and enabling of the Interrupt 3. Response of the CPU s Hardware 4. Handling of the Interrupt in a Software ISR Distributed Interrupt Management Instead of having a common, dedicated set of interrupt registers, MSP430 devices are designed with distributed interrupt management, meaning most IFG (Interrupt Flag) bits and IE (Interrupt Enable) bits are found inside each peripheral s control registers. For example, Timer_A interrupt enable bit (TAIE) and Timer_A interrupt flag (TAIFG) bit are located in the Timer_A control register (TACTL), while the ADC10 module has its own ADC10 Interrupt Enable Register (ADC10IE), ADC10 Interrupt Flag Register (ADC10IFG), and ADC10 Interrupt Vector Register (ADC10IV). Priorities & Vectors Not all interrupts are of the same priority. In fact, each MSP430 device datasheet defines the pending priority for each of its hardware interrupts. Specifically, for MSP430F5529, there are 23 interrupts of different priority levels. When an interrupt occurs and an ISR is being executed, the global interrupt (GIE) bit is disabled, so if any other interrupts are triggered at this time, their flags would be set and they would be pending until the CPU finishes executing the current ISR. If multiple interrupts are pending, the highest priority interrupt would be acknowledged and handled first. Multi sourced Interrupts/Grouped Interrupts Most MSP430 interrupts can be caused by more than one source. For example, all 8 pins in Port 1 generate the same CPU interrupt. To determine which source(s) actually interrupted
the CPU, a register called Interrupt Vector Register (IV) is provided, which, when read, will return the highest priority, pending interrupt in that group. Therefore, reading the IV register will do the following two things: 1. Clears the pending interrupt flag with the highest priority 2. Provides an address offset associated with the highest priority pending interrupt source For example, the Interrupt Vector Register for Port1 (P1IV) is described as follows, which can be found in the Family User s Guide (slau208p) section 12.4.1: As you can see under its description, the register always returns an even number ranging from 0 to 16. Interrupt Vector Table On MSP430F5529, there is an area of memory in flash that specifies the vectors (i.e. ISR address) for each interrupt source, which is called the Interrupt Vector Table. Table 1 shows the interrupt sources and the corresponding vector addresses in the decreasing priority. For example, the I/O Port P1 interrupt has a vector address of 0FFDEh on the device s flash memory. When you create a new project in CCS, a corresponding device specific header file (e.g. msp430f5529.h) is included to handle the hardware mapping for you. Therefore,
instead of using the physical address (0FFDEh in this case), you can always use PORT1_VECTOR to represent the vector address for I/O Port P1 interrupt. Other than the vector address, you can also tell whether an interrupt is a multisourced/grouped interrupt or dedicated interrupt by looking at the Interrupt Flag column in Table 1. For example, WDTIFG and TA0CCR0 CCIFG0 are dedicated interrupts, where there is only one interrupt flag correspond to that interrupt source. For a multi sourced interrupt, an Interrupt Vector Register (IV) is usually provided to record the highest priority pending interrupt in that group, such as ADC12IV, TA0IV, P1IV, P2IV. Table 1. Interrupt Sources, Flags, and Vectors INTERRUPT SOURCE System Reset Watchdog Timer_A Interval Timer Mode USCI_A0 Receive or Transmit ADC12_A TA0 (CCIFG0) TA0 I/O Port P1 I/O Port P2 INTERRUPT FLAG WDTIFG, KEYV (SYSRSTIV) SYSTEM INTERRUPT Reset VECTOR ADDRESS RESET_VECTOR (0FFFEh) WDTIFG WDT_VECTOR (0FFF2h) UCA0RXIFG, UCA0TXIFG (UCA0IV) ADC12IFG0 to ADC12IFG15 (ADC12IV) TA0CCR0 CCIFG0 TA0CCR1 CCIFG1 to TA9CCR4, TA0IFG (TA0IV) P1IFG.0 to P1IFG.7 (P1IV) P2IFG.0 to P2IFG.7 (P2IV) USCI_A0_VECTOR (0FFF0h) ADC12_VECTOR (0FFECh) TIMER0_A0_VECTOR (0FFEAh) TIMER0_A1_VECTOR (0FFE8h) PORT1_VECTOR (0FFDEh) PORT2_VECTOR (0FFD4h) PRIORITY 63, highest 57 56 54 53 52 47 42
Writing ISR in C Language When writing an ISR for interrupts, there are three steps that you need to follow: 1. Put the ISR address into the vector table (using the vector #pragma) 2. Save/Restore the CPU context (using the interrupt keyword) 3. Write your interrupt handler code As we discussed earlier, the ISR can be considered a separate thread from the main() thread, meaning the two code segments execute without calling each other. Therefore, the only way to get into the ISR is through hardware interrupts. The vector #pragma is used here to put the ISR address into the vector table. As in the following example, PORT1_VECTOR is the vector address for I/O Port 1 Interrupt. (Note: All interrupt vectors are pre defined and can be found in the Interrupt Vector Table.) The keyword interrupt is then used to declare the function as an interrupt, which would allow the compiler to handle context save/restore for you. To be specific, before entering the ISR, every CPU register, status register (SR) and the current program counter (PC) are saved onto the stack; and when the ISR finishes its execution, the compiler would restore all the CPU registers and return to the previous status starting from where the execution was interrupted. Programming for Multi sourced interrupts When programming your ISR for a multi source interrupt, a good practice is to read the Interrupt Vector (IV) register at the beginning and use the return value to determine the source of the interrupt, which can be implemented using Switch/Case statement to select the appropriate code to run. even_in_range(param1, param2) is an intrinsic function which indicates to the compiler that the first parameter is an even number between zero and the second parameter. The following example shows a sample ISR which handles the PORT1 Interrupt:
Programming for Dedicated Interrupts For dedicated interrupt, such as the watchdog interrupt (WDTIFG), your ISR code has no need to distinguish the source of the interrupt because there is only one dedicated source. In this case, the CPU hardware would automatically clears the interrupt flag when branching to the corresponding ISR. Low Power Mode The MSP430 family is designed for ultra low power applications and uses different operating modes to adjust for different system requirements. Other than Active mode, the default operating mode that we have been using so far, there are low power modes, LPM0 through LPM4, which allows different levels of power conservation. The relation between the operation modes and the CPU and Clocks Status are summarized below: Operation CPU and Clocks Status Modes CPU, MCLK ACLK SMCLK DCO FLL Active Enabled Active Optionally active (SMCLKOFF = 0) sources ACLK, MCLK, or SMCLK DCO is enabled
LPM0 Disabled Active Optionally active (SMCLKOFF = 0) LPM1 Disabled Active Optionally active (SMCLKOFF = 0) sources ACLK or SMCLK sources ACLK or SMCLK DCO is enabled Disabled LPM2 Disabled Active Disabled Disabled sources ACLK LPM3 Disabled Active Disabled Disabled sources ACLK LPM4 Disabled Disabled Disabled Disabled Disabled As you can see, CPU (i.e. MCLK) is always disabled in Low Power Mode LPM0 through LPM4, while other clocks (ACLK and SMCLK) may remain active. DCO, the Digitally Controlled Oscillator, is enabled only when it sources the active clock(s). FLL, the Frequency Locked Loop hardware, is enabled when DCO is enabled and it is in active mode or LPM0, but remains disabled for further power conservation. These six operation modes are ordered in increasing power conservation level, from Active mode, which has the most clock modules being active, to LPM4, where all clocks are disabled. LPM Configuration LPM0 through LPM4 are configured with the CPUOFF, OSCOFF, SCG0, and SCG1 bits in the status register (SR). For example, setting to LPM1 is equivalent to setting the control bits as CPUOFF = 1, OSCOFF = 0, SCG0 = 1, SCG1 = 0. o What is the advantage of including the mode control bits in the status register (SR) in respect to the flow of interrupt? Fortunately, macros (LPM0, LPM1, LPM2, LPM3, LPM4) are defined in the devicespecific header files (msp430f5520.h in this case), which allows us to configure the operation modes easily. For example, to configure your microcontroller to LPM1, you can simply use the following code, This is equivalent to using or in assembly language,
Note that when setting any of the mode control bits, the selected operating modes takes effect immediately, meaning the CPU is killed once the device is set to any low power mode. Experiments Experiment 1 Control LEDs with Push Button In this experiment, you will set up the Push Button S1 on the LaunchPad to control the green and the red LEDs using interrupts. This experiment is also used as a review of our previous labs, you may want to review your code for Lab02_exp2 and Lab03_exp2. o Refer to the Interrupt Vector Table, what Interrupt Vector should be used? The objective is to have the following behavior: When S1 is not pressed, the red LED should be ON When S1 is pressed down, the green LED should be ON and red LED should be OFF When S1 is released, the green LED should turn OFF and red LED should turn ON Instead of reading the input from S1 constantly (the polling method), you should use interrupt and code your own ISR to turn on/off the LEDs accordingly. For further power conservation, the microcontroller will be put into low power mode and the CPU will only wake up when a hardware interrupt occurs (i.e. push button S1 is pressed or released depending on your interrupt edge select). As for clocks, we will use the Digitally Controlled Oscillator (DCO) as the clock source of main clock (MCLK) because of its short wake up time from low power mode. In your program, include the following parts: 1) Pin Configuration Configure the input pin for S1 with pull up resistor Configure output pins for the green and red LEDs 2) Clock Setup Select DCOCLKDIV as the clock source of MCLK (default settings) Set MCLK to 500kHz (you may use clock divider) Verify your clock setup using the oscilloscope. Take a screenshot. (Hint: since by default, both SMCLK and MCLK are sourced from DCOCLKDIV, we can output SMCLK to observe the frequency of DCOCLKDIV on P2.2 and apply divider of MCLK)
3) Interrupt Enable Enable individual interrupt for Port1 Enable global interrupt (GIE) 4) Set Low Power Mode Inside the infinite loop, put the microcontroller to Low Power Mode 3 (LPM3) Note: the CPU will be killed once it is put to LPM 5) Interrupt Service Routine (ISR) Use the correct interrupt vector Use interrupt keyword to save the CPU status on stack Read Port2 s Interrupt Vector Register (P2IV), if the interrupt is triggered by push button S1, turn on/off the LEDs and configure interrupt edge select accordingly. You can follow the pseudo code below: If input is low (i.e. S1 is pressed) Turn on green LED and turn off red LED; Set rising edge trigger; Else (i.e. S1 is not pressed) End Turn on red LED and turn off Green LED; Set falling edge trigger;