MSP430 Interrupts
MSP430 Design Workshop
Waiting for an Event: Family Vacation
Polling
An engineering example...
Waiting for an Event: Family Vacation
Polling Interrupts
Wake me up when we get there...
An engineering example...
Waiting for an Event: Button Push
Polling Interrupts
while(1) // Port 1 interrupt service routine
{ #pragma vector=PORT1_VECTOR
if(P1IFG & BIT2) // P1.2 IFG set __interrupt void Port_1(void)
{ {
P1IFG &= ˜BIT2; // P1.2 IFG cleared P1OUT ˆ= BIT0; // Toggle LED at P1.0
P1OUT ˆ= BIT0; // Toggle LED at P1.0 P1IFG &= ˜BIT2; // P1.2 IFG cleared
} }
100% CPU Load > 0.1% CPU Load
How interrupts can affect system design…
How do Interrupts Work?
1. An interrupt occurs
…currently executing code
interrupt occurs
next_line_of_code
• UART
}
• GPIO
• Timers
• ADC
• Etc.
How do Interrupts Work?
1. An interrupt occurs
…currently executing code
interrupt occurs
next_line_of_code
• UART
}
• GPIO
• Timers
• ADC
• Etc.
2. It sets a flag bit
in a register
...
How are interrupts "enabled"?
Interrupt Flow
IFG bit IE bit SR.GIE
Interrupt Interrupt “Individual” “Global”
Source ‘Flag’ Int Enable Int Enable
GPIO 0
TIMER_A 1 CPU
… 0
NMI 0
Global Interrupt Enable (GIE)
Enables ALL maskable interrupts
Enable: __bis_SR_register( GIE );
Disable: __bic_SR_register( GIE );
How do Interrupts Work?
1. An interrupt occurs 3. CPU acknowledges INT by…
• Current instruction completes
…currently executing code • Saves return-to location on stack
interrupt occurs • Saves ‘Status Reg’ (SR) to the stack
next_line_of_code • Clears most of SR, which turns off
• UART interrupts globally (SR.GIE=0)
} • Determines INT source (or group)
• GPIO
• Timers • Clears non-grouped flag* (IFG=0)
• ADC • Reads interrupt vector & calls ISR
• Etc.
2. Sets a flag bit
(IFG) in register
...
How do Interrupts Work?
1. An interrupt 3. CPU acknowledges INT by…
occurs • Current instruction completes
• Saves return-to location on stack
• Saves ‘Status Reg’ (SR) to the stack
• Clears most of SR, which turns off
interrupts globally (SR.GIE=0)
• UART • Determines INT source (or group)
• GPIO • Clears non-grouped flag* (IFG=0)
• Timers • Reads interrupt vector & calls ISR
• A/D Converter
4. ISR (Interrupt Service Routine)
• Etc.
• Save context of system
• (optional) Re-enable interrupts
2. Sets a flag bit • *If group INT, read IV Reg to
(IFG) in register determines source & clear IFG
... • Run your interrupt’s code
• Restore context of system
• Continue where it left off (RETI)
4. Interrupt Service Routine (ISR)
…currently executing code Vector Table
interrupt occurs &myISR
next_line_of_code
}
#pragma vector=WDT_VECTOR
Using Interrupt Keyword interrupt myISR(void){
Compiler handles context save/restore • Save context of system
Call a function? Then full context is saved • (optional) Re-enable interrupts
No arguments, no return values • *If group INT, read assoc IV Reg
(determines source & clears IFG)
You cannot call any TI-RTOS scheduler
functions (e.g. Swi_post) • Run your interrupt’s code
Nesting interrupts is MANUAL • Restore context of system
• Continue where it left off (RETI)
}
Interrupt Priorities (F5529)
INT Source Priority
System Reset high There are 23 interrupts
(partially shown here)
System NMI
User NMI
If multiple interrupts (of the 23) are
pending, the highest priority is
Comparator responded to first
Timer B (CCIFG0) 0xFFFF
By default, interrupts are not
Timer B nested …
WDT Interval Timer That is, unless you re-enable INT’s
Serial Port (A) during your ISR, other interrupts will be
held off until it completes
Serial Port (B)
It doesn’t matter if the new INT is a
A/D Convertor higher priority
As already recommended, you should
GPIO (Port 1) keep your ISR’s short
GPIO (Port 2) Most of these represent ‘groups’ of
Real-Time Clock low interrupt source flags
145 IFG’s map into these 23 interrupts
Interrupt Vector (IV) Registers
Returns highest
pending Port 1 IFG Port 1 Interrupt Vector Register (P1IV)
IV = Interrupt Vector register
Most MSP430 interrupts can be caused by more than one
source; for example:
Each 8-bi GPIO port one has a single CPU interrupt
IV registers provide an easy way to determine which
source(s) actually interrupted the CPU
The interrupt vector register reflects only ‘triggered’
interrupt flags whose interrupt enable bits are also set
Reading the ‘IV’ register:
Clears the pending interrupt flag with the highest priority
Provides an address offset associated with the highest priority
pending interrupt source
Memory Map
Interrupt Vectors & Priorities (F5529)
INT Source IV Register Vector Address Loc’n Priority
System Reset SYSRSTIV RESET_VECTOR 63 high
System NMI SYSSNIV SYSNMI_VECTOR 62 Flash (128K)
User NMI SYSUNIV UNMI_VECTOR 61
Comparator CBIV COMP_B_VECTOR 60
Timer B (CCIFG0) CCIFG0 TIMER0_B0_VECTOR 59 0xFFFF
Timer B TB0IV TIMER0_B1_VECTOR 58 INT Vectors (80)
WDT Interval Timer WDTIFG WDT_VECTOR 57
Serial Port (A) UCA0IV USCI_A0_VECTOR 56 RAM (8K)
Serial Port (B) UCB0IV USCI_B0_VECTOR 55
USB RAM (2K)
A/D Convertor ADC12IV ADC12_VECTOR 54
Info Memory (512)
GPIO (Port 1) P1IV PORT1_VECTOR 47 Boot Loader (2K)
GPIO (Port 2) P12V PORT2_VECTOR 42 Peripherals (4K)
Real-Time Clock RTCIV RTC_VECTOR 41 low
Legend: Non-maskable Group’d IFG bits
Maskable Dedicated IFG bits The 'FR5969 is very similar...
Memory Map
Interrupt Vectors & Priorities (F5529)
INT Source IV Register Vector Address Loc’n Priority
System Reset SYSRSTIV RESET_VECTOR 63 high
System NMI SYSSNIV SYSNMI_VECTOR 62 Flash (128K)
User NMI SYSUNIV UNMI_VECTOR 61
Comparator CBIV COMP_B_VECTOR 60
Timer B (CCIFG0) CCIFG0 TIMER0_B0_VECTOR 59 0xFFFF
Timer B TB0IV TIMER0_B1_VECTOR 58 INT Vectors (80)
WDT Interval Timer WDTIFG WDT_VECTOR 57
Serial Port (A) UCA0IV USCI_A0_VECTOR 56 RAM (8K)
Serial Port (B) UCB0IV USCI_B0_VECTOR 55
USB RAM (2K)
A/D Convertor ADC12IV ADC12_VECTOR 54
Info Memory (512)
GPIO (Port 1) P1IV PORT1_VECTOR 47 Boot Loader (2K)
GPIO (Port 2) P12V PORT2_VECTOR 42 Peripherals (4K)
Real-Time Clock RTCIV RTC_VECTOR 41 low
Legend: Non-maskable Group’d IFG bits Let's look at the ISR for
Maskable Dedicated IFG bits the WDT (dedicated) interrupt...
Interrupt Service Routine (Dedicated INT)
INT Source IV Register Vector Address Loc’n
WDT Interval Timer WDTIFG WDT_VECTOR 57
#pragma vector assigns
‘myISR’ to correct location
in vector table #pragma vector=WDT_VECTOR
__interrupt void myWdtISR(void) {
GPIO_toggleOutputOnPin( ... );
}
Interrupt Service Routine (Dedicated INT)
INT Source IV Register Vector Address Loc’n
WDT Interval Timer WDTIFG WDT_VECTOR 57
#pragma vector assigns
‘myISR’ to correct location
in vector table #pragma vector=WDT_VECTOR
__interrupt void myWdtISR(void) {
__interrupt keyword tells
compiler to save/restore GPIO_toggleOutputOnPin( ... );
context and RETI
}
For a dedicated
interrupt, the MSP430
CPU auto clears the
WDTIFG flag
Interrupt Service Routine (Group INT)
INT Source IV Register Vector Address Loc’n
GPIO (Port 1) P1IV PORT1_VECTOR 47
#pragma vector assigns
‘myISR’ to correct location
in vector table #pragma vector=PORT1_VECTOR
__interrupt void myISR(void) {
__interrupt keyword tells
compiler to save/restore switch(__even_in_range( P1IV, 0x10 )) {
context and RETI case 0x00: break; // None
case 0x02: break; // Pin 0
case 0x04: break; // Pin 1
case 0x06: GPIO_toggleOutputOnPin(…); // Pin 2
break;
case 0x08: break; // Pin 3
case 0x0A: break; // Pin 4
case 0x0C: break; // Pin 5
case 0x0E: break; // Pin 6
case 0x10: break; // Pin 7
default: _never_executed();
}}c
Interrupt Service Routine (Group INT)
INT Source IV Register Vector Address Loc’n
GPIO (Port 1) P1IV PORT1_VECTOR 47
#pragma vector assigns
‘myISR’ to correct location
in vector table #pragma vector=PORT1_VECTOR
__interrupt void myISR(void) {
__interrupt keyword tells
compiler to save/restore switch(__even_in_range( P1IV, 0x10 )) {
context and RETI case 0x00: break; // None
case 0x02: break; // Pin 0
Reading P1IV register: case 0x04: break; // Pin 1
Returns value for case 0x06: GPIO_toggleOutputOnPin(…); // Pin 2
highest priority INT break;
for the Port 1 ‘group’ case 0x08: break; // Pin 3
Clears IFG bit case 0x0A: break; // Pin 4
case 0x0C: break; // Pin 5
case 0x0E: break; // Pin 6
case 0x10: break; // Pin 7
default: _never_executed();
}}
Interrupt Service Routine (Group INT)
INT Source IV Register Vector Address Loc’n
GPIO (Port 1) P1IV PORT1_VECTOR 47
#pragma vector assigns
‘myISR’ to correct location
in vector table #pragma vector=PORT1_VECTOR
__interrupt void myISR(void) {
__interrupt keyword tells
compiler to save/restore switch(__even_in_range( P1IV, 0x10 )) {
context and RETI case 0x00: break; // None
case 0x02: break; // Pin 0
Reading P1IV register: case 0x04: break; // Pin 1
Returns value for case 0x06: GPIO_toggleOutputOnPin(…); // Pin 2
highest priority INT break;
for the Port 1 ‘group’ case 0x08: break; // Pin 3
Clears IFG bit case 0x0A: break; // Pin 4
case 0x0C: break; // Pin 5
Tell compiler to ignore case 0x0E: break; // Pin 6
un-needed switch cases case 0x10: break; // Pin 7
by using intrinsics: default: _never_executed();
__even_in_range() }}
_never_executed()
Hardware ISR’s – Coding Practices
An interrupt routine must be declared with no arguments and must return void
Global variables are often used to “pass” information to or from an ISR
Do not call interrupt handling functions directly (Rather, write to IFG bit)
Interrupts can be handled directly with C/C++ functions using the interrupt
keyword or pragma
Calling functions in an ISR
If a C/C++ interrupt routine doesn’t call other functions, usually, only those
registers that the interrupt handler uses are saved and restored.
However, if a C/C++ interrupt routine does call other functions, the routine saves
all the save-on-call registers if any other functions are called
Why? The compiler doesn’t know what registers could be used by a nested
function. It’s safer for the compiler to go ahead and save them all.
Re-enable interrupts? (Nesting ISR’s)
DON’T – it’s not recommended – better that ISR’s are “lean & mean”
If you do, change IE masking before re-enabling interrupts
Disable interrupts before restoring context and returning (RETI re-enables int’s)
Beware – Only You Can Prevent Reentrancy…
Button Interrupts
Lab 5a – Pushing your Button
Create a CCS project that uses an interrupt
to toggle the LED when a button is pushed
This requires you to create:
o Setup code enabling the GPIO interrupt
o GPIO ISR for pushbutton pin
You’ll also create code to handle all the
interrupt vectors
Interrupt with Low power
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1DIR |= 0x01; // Set P1.0 to output direction
P1IE |= 0x10; // P1.4 interrupt enabled
P1IES |= 0x10; // P1.4 Hi/lo edge
P1IFG &= ~0x10; // P1.4 IFG cleared
_BIS_SR(LPM4_bits + GIE); // Enter LPM4 w/interrupt
}
// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
P1OUT ^= 0x01; // P1.0 = toggle
P1IFG &= ~0x10; // P1.4 IFG cleared
}