MSP430 Launchpad Tutorial - Part 2 - Interrupts and timers
What is an "interrupt"? It is a signal that informs our MCU that a certain event has happened, causing the interruption of the normal flow of the main program and the execution of an "interrupt routine", that handles the event and takes a specified action.
Quick Links
- Part 1: MSP430 Launchpad Tutorial - Part 1 - Basics
- Part 2: MSP430 Launchpad Tutorial - Part 2 - Interrupts and timers
- Part 3: MSP430 LaunchPad Tutorial - Part 3 - ADC
- Part 4: MSP430 LaunchPad Tutorial - Part 4 - UART Transmission
Interrupts are essential to avoid wasting the processor's valuable time in polling loops, waiting for external events (in fact they are used in Real-Time Operating Systems, RTOS).
In the MSP430 architecture, there are several types of interrupts: timer interrupts, port interrupts, ADC interrupts and so on. Each one of them needs to be enabled and configured to work, and there is a separate "service routine" for every interrupt.
In this tutorial we'll see how to use timer and port interrupts to flash some leds, we'll keep the ADC interrupt for the next tutorial. So, let's write some code!
#include "msp430g2231.h" void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT
You should recognize those lines,we used them in the last tutorial to add the definition file for our MCU, declare the main function and stop the watchdog timer.
CCTL0 = CCIE; // CCR0 interrupt enabled TACTL = TASSEL_2 + MC_1 + ID_3; // SMCLK/8, upmode CCR0 = 10000; // 12.5 Hz
Here's some interesting stuff. These lines configure the timer interrupt. We first enable it by setting the CCIE bit in the CCTL0 register.
Then we set the clock for the timer module in the TimerA control register.
If you have a look at the msp430g2231.h file, you can see that:
- TASSEL_2 selects the SMCLK (supplied by an internal DCO which runs at about 1 MHz);
- MC_1 selects the "UP mode", the timer counts up to the number stored in the CCR0 register;
- ID_3 selects an internal 8x divider for the supplied clock (in our case we have SMCLK/8).
Finally, we set the CCR0 register. We configured the TimerA module to count up to the number stored in this register before overflowing and triggering the interrupt.
By setting it at 10000, we get an overflow-frequency of 12,5 Hz. In fact we have (SMCLK/8)/10000 = 12,5 .
You may obtain several frequencies by changing this number (remember that the MSP430 has a 16-bit timer, so the value stored in the CCR0 register must not be higher than 65535), changing the dividers or adding an if-else block with a counter in the interrupt routine. Let's go ahead.
P1OUT &= 0x00; // Shut down everything P1DIR &= 0x00; P1DIR |= BIT0 + BIT6; // P1.0 and P1.6 pins output the rest are input P1REN |= BIT3; // Enable internal pull-up/down resistors P1OUT |= BIT3; //Select pull-up mode for P1.3
These lines should be familiar too, but there are some additions: firstly, we clear the PORT1 output and direction registers. Then we set the P1.0 and P1.6 pins as outputs and the rest as inputs. The last two lines enable the pull-up resistor on the switch (BIT3) so that the normal state (button not pressed) will be "1".
P1IE |= BIT3; // P1.3 interrupt enabled P1IES |= BIT3; // P1.3 Hi/lo edge P1IFG &= ~BIT3; // P1.3 IFG cleared
With these lines of code, we first tell the MCU to listen to the P1.3 pin for logic-state changes (effectively enabling the interrupt on that particular pin).
Then we select the edge when the interrupt is raised (from High to Low or Low to High); remember that the button on the LaunchPad connects the input pin to GND when pushed and to VCC when not. For this reason we seletct Hi/Lo edge.
Finally we clear the interrupt flag for that pin. The interrput flag register P1IFG reports when an interrupt is raised, and it should be cleared at the end of the interrupt service routine.
_BIS_SR(CPUOFF + GIE); // Enter LPM0 w/ interrupt while(1) //Loop forever, we do everything with interrupts! {} }
With this line, as you can remember, we shut down the CPU to spare some power while keeping the interrupts enabled. Then we enter a loop to be sure the MCU does nothing else, as we do our job with interrupts.
// Timer A0 interrupt service routine #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { P1OUT ^= BIT0; // Toggle P1.0 }
This is the TimerA interrupt service routine. Every time the TimerA overflows, the code inserted in this routine (note the special declaration) is executed. As you can see we only toggle the P1.0 pin (red led on LaunchPad), then we return to normal execution.
// Port 1 interrupt service routine #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { P1OUT ^= BIT6; // Toggle P1.6 P1IFG &=~BIT3; // P1.3 IFG cleared }
This is the Port1 interrupt service routine. Every time the we push the P1.3 button, the code inserted in this routine (note the special declaration) is executed. We toggle the P1.6 pin (greenled on LaunchPad), clear the P1.3 interrupt flag (very important) and then we return to normal execution.
Compile and program the LaunchPad, you should see the red led blink, and the green led toggle when you press the P1.3 button.
Here's the full code, enjoy!
#include "msp430g2231.h" void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT CCTL0 = CCIE; // CCR0 interrupt enabled TACTL = TASSEL_2 + MC_1 + ID_3; // SMCLK/8, upmode CCR0 = 10000; // 12.5 Hz P1OUT &= 0x00; // Shut down everything P1DIR &= 0x00; P1DIR |= BIT0 + BIT6; // P1.0 and P1.6 pins output the rest are input P1REN |= BIT3; // Enable internal pull-up/down resistors P1OUT |= BIT3; //Select pull-up mode for P1.3 P1IE |= BIT3; // P1.3 interrupt enabled P1IES |= BIT3; // P1.3 Hi/lo edge P1IFG &= ~BIT3; // P1.3 IFG cleared _BIS_SR(CPUOFF + GIE); // Enter LPM0 w/ interrupt while(1) //Loop forever, we work with interrupts! {} } // Timer A0 interrupt service routine #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { P1OUT ^= BIT0; // Toggle P1.0 } // Port 1 interrupt service routine #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { P1OUT ^= BIT6; // Toggle P1.6 P1IFG &= ~BIT3; // P1.3 IFG cleared }
- Comments
- Write a Comment Select to add a comment
I translated the example to MSP-EXP430F5529LP I hope it helped to some of you guys!
#include "msp430F5529.h" //Contains definitions for registers and built-in functions
#include
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
TA0CCTL0 = CCIE; // CCR0 interrupt enabled
TA0CTL = TASSEL_2 | MC_1 | ID_3; // SMCLK/8, upmode
TA0CCR0 = 10000; // I have to calculate it, but I was lazy Hz
P1OUT &= 0x00; // Shut down pins on P1
P1DIR &= 0x00; // Set P1 pins as output
P1DIR |= BIT0; // P1.0 pin set as output the rest are input
P4OUT &= 0x00; // Shut down pins on P4
P4DIR &= 0x00; // Set P4 pins as output
P4DIR |= BIT7; // P4.7 pin set as output the rest are input
P2REN |= BIT1; // Enable internal pull-up/down resistors for P2
P2OUT |= BIT1; //Select pull-up mode for P2.1
P2IE |= BIT1; // P2.1 interrupt enabled
P2IES |= BIT1; // P2.1 Hi/lo edge
P2IFG &= ~BIT1; // P2.1 IFG cleared
_BIS_SR(CPUOFF + GIE); // Enter LPM0 w/ interrupt
while(1) //Loop forever, we work with interrupts!
{}
}
// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0 (void)
{
P1OUT ^= BIT0; // Toggle P1.0
}
// Port 2 interrupt service routine
#pragma vector=PORT2_VECTOR
__interrupt void Port_2(void)
{
static uint8_t debounce = 0;
while(debounce <= 100)
{
if (~P2IN & BIT1) debounce++;
else debounce = 0;
}
P4OUT ^= BIT7; // Toggle P4.7
debounce = 0;
P2IFG &= ~BIT1; // P2.1 IFG cleared
}
#include
/*
* main.c
*/
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
TA0CCTL0 = CCIE; /* TA0CCTL0 Register: If you plan to use a Timer, the first register that you need to set is CCTL0. It is a 16 bit register and settings of this register effects how we use the register. For our purpose we just tell it to enable the interrupt using */
TA0CTL = TASSEL_2 + MC_1 + ID_3; /* TASSEL_2: use SMCLK MC_1: upcount ID_3: divide by 8*/
TA0CCR0 = 10000; // count up to 10000
P1OUT = 0;
P1DIR= BIT0; //P0 as output
P1REN |= BIT1; // enable pull up/down for P1
P1OUT |= BIT1; // pull up res for P1
P4OUT = 0;
P4DIR = 0;
P4DIR= BIT7;
P1IE |= BIT1; //P1.0 interrupt enable
P1IES |= BIT1; // High/low edge
P1IFG &= ~BIT1; // clear flag
_BIS_SR(CPUOFF + GIE); // sleep untill interupt
while(1) {}
return 0;
}
// Timer A interrupt
#pragma vector=TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR(void)
{
P4OUT ^= BIT7;
}
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
P1OUT ^= BIT0;
P1IFG &= ~BIT1;
}
Thanks a lot!
Nice piece of tutorial. May I know where you got the material from? I mean, the instruction set. Thanks!
I have a problem about clearing interrupt flag.
Why we don't need to clear interrupt flag in Timer's interrupt service routine, but need to clear interrupt flag in Port1 interrupt service routine?
430x2xxx don't have IV and if many IFG flags can be set at one time and if all flags was cleared by just reading it you could not have multiple if bittest of the IFG registers, so you have clear that bit manually.
i am trying get an external interrupt and blink an LED. but i get error like 'pragma vector= accepts numeric arguments or "unused_interrupts" but not PORT_VECTOR'
int main(){
// Stop Watch Dog Timer
WDTCTL = WDTPW + WDTHOLD;
P1DIR &= ~BIT4; //configure as an input
P1OUT |= BIT4;
P3DIR |= BIT4;
P3OUT |= BIT4;
P1IES = 0x01;
P1IE = 0x01; //1b = Corresponding port interrupt enabled
P1IFG = 0x01; //PxIFG flag is set with a high-to-low transition
P1REN = 0x01; //Pullup or pulldown enabled
}
#pragma vector = PORT_VECTOR
__interrupt void Port_1 (void){
if( (P1IN & BIT4)==0 ){
// read input as GND
P3OUT |= BIT4;
}
else if( (P1IN & BIT4)==1 ){
P3OUT &= ~BIT4;
}
}
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
P1OUT ^= BIT6; // Toggle P1.6
P1IFG &= ~BIT3; // P1.3 IFG cleared
}
If we pressed the button 1.3 we went to the ISR because P1IE and P1IES was configured for that, in te routine P1OUT XOR BIT6 and then P1IFG and then the program back to the "main ()"? I mean, the ISR ends by itself and back automatically? If that's rigth I imagine if we press the button again, the program goes again to ISR. I mean.
But whit TimerA, who tells to MCU "go to ISR_TimerA Routine?_TimerA begin to count and at the end of the count, the program goes to ISR_TimerA? and if that's right, what line begin the count of timerA. And how we coud stop the ISR_TimerA for example... after 5 times the same interruption.
I spect my questions don´t be so ridiculous.
I donnow why it doesen't show.
The above example works pretty well. Now, if I want to blink 2 LED's at different frequency using timer_A what I have to do?
I have taken TACCRO = 10000 and TACCR1 = 60000
Accordingly enabling interrupt by TACCTLO = CCIE and TACCTL1 = CCIE
Also made two ISR:
#pragma vector=TIMERA0_VECTOR
__interrupt void ISR1 (void)
{ }
and #pragma vector=TIMERA1_VECTOR
__interrupt void ISR2 (void)
{ }
but it not working properly. One LED blinks perfectly but anotherone not blinking. Can anybody please suggest. Thanks in advance.
What does #
vector=PORT1_VECTOR mean?Also if I connect 2 switches to 2 different pins of port 1 and wish to execute different routines for them, how do I achieve it?
Hello all,
I struggled a little to make the code work for the FR5994, so I thought this may help others using the fr5994 based launchpad
//***************************************************************************************
// MSP430 Blink the LED Demo - Software Toggle P1.0
//
// MSP430x5994
// -----------------
// /|\| XIN|-
// | | |
// --|RST XOUT|-
// | |
// | P1.0|-->LED1
// P1.1|-->LED2
// P5.5|<--P5.5
//
// Texas Instruments, Inc
// July 2013
//***************************************************************************************
#include <msp430.h>
void main(void)
{
//CLK SETUP
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
PM5CTL0 &= ~LOCKLPM5; // Disable the GPIO power-on default high-impedance mode
TA0CCTL0 = CCIE; // CCR0 interrupt enabled
TA0CTL = TASSEL_1 + MC_1 + ID_3; // ACLK=32.768kHz/8, upmode
TA0CCR0 = 4096; // 1Hz Hz
P1OUT &= 0x00; // Shut down everything
P1DIR &= 0x00;
P1DIR |= BIT0 + BIT1; // P1.0 and P1.1 pins output the rest are inputs. The outputs are used for the two LEDs
P5REN |= BIT5; // Enable internal pull-up/down resistors
P5OUT |= BIT5; // Required to set pull-up high resistor
P5IE |= BIT5; // P5.5 interrupt enabled
P5IES |= BIT5; // P5.5 Hi/lo edge
P5IFG &= ~BIT5; // P5.5 IFG cleared
_BIS_SR(CPUOFF + GIE); // Enter LPM0 w/ interrupt
while(1) //Loop forever, we work with interrupts!
{}
}
// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0 (void)
{
P1OUT ^= 0x01; // Toggle P1.0
//TA0CTL &= ~TAIFG;
}
// Port 5 interrupt service routine
#pragma vector=PORT5_VECTOR
__interrupt void Port_1(void)
{
P1OUT ^= 0x02; // Toggle P1.1
P5IFG &= ~BIT5; // P5.5 IFG cleared
}
Thank you, it's really important to set clock to 1 Hz. I guess, before development, we should look at clock sources.
Hello, Enrico. Very nice series of articles. It seems like many people are getting the error: "
#2580-D
pragma vector = accepts numeric arguments or "unused_interrupts" but
not PORT2_Vector" as they try to use interrupts. I have looked on many forums, and none of the suggested solutions seem to really address the problem. I am simply trying to generate an interrupt when one of the pins of Port 2 gets pulled low. I have rewritten my code several times to try different Ports and different Pins, but always get the same warning.
Any suggestions as to a solution? I am using CCS 9, and the MPS430 FR2355 evaluation board.
Sincerely, Bob
Bob, you should try our forum.
Thanks. I will give it a try.
"Each bit in each PxREN register enables or disables the pullup/pulldown resistor of the corresponding I/O pin.
The corresponding bit in the PxOUT register selects if the pin is pulled up (1) or pulled down (0)."
I made that clearer with a comment in the code, thanks for reporting it!
You just have to enable the pin interrupt with the P1IE and P1IES registers. Then in the PORT1 interrupt routine you can execute different code for each pin with an if statement on the P1IFG register, that will let you know which pin has been pressed.
I am working on MPS430g2553 controller,I want set time from a keypad where it has 5 keys,What is flow for it?
#pragma vector=TIMER0_A0_VECTOR
instead of
#pragma vector=TIMERA0_VECTOR
for the timer interrupt service routine and it should work. I tested it 10 minutes ago!
Hello,
I want to understand what does the line #pragma vector=TIMER0_A0_VECTOR does when compiling?
Thanks for your tutorial.. ur way of explanation on the code is really nice and easy to breakdown the code into simpler way to understand, thanks a lot.
i changed the line " P1IES |= BIT3; " into " P1IES &= ~BIT3; " ie,. from rising edge to falling edge.
it worked as expected,..
at the same time i tried this too
I changed the line " P1OUT |= BIT3; ", into "P1OUT &= ~BIT3; ".
if im correct which is clearing the bit ie,. setting the bit as pull down resistor mode.
there is no change in the output.!
It should not work, right ,..???!
Now,when the button is pressed,it becomes low,in polling,it is checked with( ~BIT2 & P1IN). How is this done using interrupts coding?
I am using this port and timer interrupt program on g2553.
It is showing error as : "Port" is not recognized as an internal or external command.
I have also changed interrupt names in accordance with g2553.h , but still the same error......
For reference.....here is the code :::
#include "msp430g2553.h"
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
CCTL0 = CCIE; // CCR0 interrupt enabled
TACTL = TASSEL_2 + MC_1 + ID_3; // SMCLK/8, upmode
CCR0 = 10000; // 12.5 Hz
P1OUT &= 0x00; // Shut down everything
P1DIR &= 0x00;
P1DIR |= BIT0 + BIT6; // P1.0 and P1.6 pins output the rest are input
P1REN |= BIT3; // Enable internal pull-up/down resistors
P1OUT |= BIT3; //Select pull-up mode for P1.3
P1IE |= BIT3; // P1.3 interrupt enabled
P1IES |= BIT3; // P1.3 Hi/lo edge
P1IFG &= ~BIT3; // P1.3 IFG cleared
_BIS_SR(CPUOFF + GIE); // Enter LPM0 w/ interrupt
while(1) //Loop forever, we work with interrupts!
{}
}
// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
P1OUT ^= BIT0; // Toggle P1.0
}
// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void PORT1_ISR(void)
{
P1OUT ^= BIT6; // Toggle P1.6
P1IFG &= ~BIT3; // P1.3 IFG cleared
}
TA0CTL=TASSEL_2+MC_1+ID_3;
TA0CCR0=10000;
TA0CCTL0=CCIE
#pragma vector= TIMER0_A0_VECTOR
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: