This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How to code multiple PORT2 interrupts?

Other Parts Discussed in Thread: MSP430F2274

Hello everyone,

I am trying to make a portable blood pressure monitor with MSP430F2274. In addition to measuring blood pressure, the device is supposed to store the blood pressures along with the time the measurement is taken.

On port 2, i have four buttons interfaced on p2.0 to p2.3. all the buttons generate interrupts to wake up the CPU from LPM3.

I have the following problem in running the test code:

on pressing any of the four buttons, the code is entering the PORT2_ISR, but the code is not detecting which button is pressed. I have used a simple switch case statement on P2IFG, but when I tried to debug, i found that P2IFG reset after entering the ISR.

What can be done to detect which of the four buttons is pressed?

I am using IAR embedded workbench platform and am programming in C language.

Your suggestions will be really helpful.

  • I suspect that the debugger resets P2IFG.

    How does you test code look like?

  • Yes, the Simulator resets P2IFG.

    I have posted the PORT2_ISR for convenience..

     

    #pragma vector=PORT2_VECTOR

    __interrupt void Port2_interrupt(void)  

    {

      switch(P2IFG)

      {

      case 0x08:                                // SET button interrupt

      set_time();

      P2IFG &= ~0x01;

      case 0x01:

      case 0x02:

      case 0x04: break;

      }

    }

    this code is supposed to check for which button is pressed on PORT2.
    but none of the cases are getting executed, because P2IFG resets to 0x00 on entry in interrupt.

  • This code won't work.

    If two interrupts are pending, P2IFG will be 0x03 or 0x0a or whatever combination of events is pending.

    You cannot use a bitfield in a switch statement.

    if your MSP has a P2IV register (5x/6x family only), use it. It always returns the nubmer of the highest pending interrupt, and at the same time resets its IFG bit.
    So you can loop through it with a switch and if it reads 0, no more interrupts are pending.
    It also eliminates a nasty baug in which a por tinterupt is lost if it happens the very moment you're reading the IFG register, or that you may clear it yourself by a read-modify-write instruction while you are clearing a different IFG bit.

     

  • Mr. Jens-Michael,

    the mistake in the code is rectified by putting a 'default' statement. hence the code will do nothing if there are two interrupts. Thankyou for showing me the error.

    It is really unlikely that two interrupts will occur, because the interrupts are generated by pushbuttons. even if they occur if some guy is trying to be nasty with the machine, doing nothing seems to be a satisfactory solution.

    As I've already told, i am using a 2x series MSP430, and hence do not have a PAIV register.

    What should I do here?

  • Jimit Raja said:
    As I've already told, i am using a 2x series MSP430, and hence do not have a PAIV register.

    When answering to a post, you don't see the whole thread anymore, and I was a bit in a hurry too, so I didn't check.

    One thing that's, well not 'wrong' but also not entirely right, is , that you ignore the bouncing of the pushbuttons. Manual pushbuttons are nbot ideal. You'll get multiple interrupts for several milliseconds.

    The usual way to do debouncing is to use a timer delay before acting. If a port interrupt is triggered, the 'offending' port interrupt is disabled, and a timer is started. When the timer expires, it looks for the current value of the port bit (with the now settled signal) and determines whether it is a pushed button or not. Then it re-enables the port interrupt.

    Jimit Raja said:
    even if they occur if some guy is trying to be nasty with the machine, doing nothing seems to be a satisfactory solution.

    I agree. For this scenario, it is a valid and working solution.

  • #pragma vector=PORT2_VECTOR
    __interrupt void Port2_interrupt(void) 
    {
      switch(P2IFG)
      {
        case 0x08:     
          set_time();
          Break;
        case 0x01:
          ...
          Break;
        case 0x02:
          ...
          Break;
        case 0x04:
          ...
          break;
      }
      P2IFG = 0;
    }

  • Mr. old_cow_yellow,

    My question is that as soon as i enter the subroutine, P2IFG already becomes zero. i am working with the simulator. and i am not able to detect any button presses because of this.

    thanks for giving the code in a better form, but if P2IFG becomes zero before switch statement occurs, any code that uses P2IFG as a switch variable wont work.

  • Mr. Jens-Michael,

    Jens-Michael Gross said:

    When answering to a post, you don't see the whole thread anymore, and I was a bit in a hurry too, so I didn't check.

    Never mind sir, I understand.

    Jens-Michael Gross said:

    The usual way to do debouncing is to use a timer delay before acting. If a port interrupt is triggered, the 'offending' port interrupt is disabled, and a timer is started. When the timer expires, it looks for the current value of the port bit (with the now settled signal) and determines whether it is a pushed button or not. Then it re-enables the port interrupt.

    Thank you for the suggestion. will surely apply. can you tell me how much delay is required?

    My main question is still a question sir, what can be done in a 2x series MSP430 to detect which port interrupt is generated? P2IFG is not working in my case.

     

     

  • Jimit Raja said:
    .... i am working with the simulator. and i am not able to detect any button presses because of this....

    Do not use simulator. Use the real hardware.

  • old_cow_yellow said:

    .... i am working with the simulator. and i am not able to detect any button presses because of this....

    Do not use simulator. Use the real hardware.

    [/quote]

    Are you sure this will do the trick?

    As far as I know, simulator has always done what real hardware has done actually.. Hence i do not try real hardware until i get satisfactory results on the simulator...

    but in this case, are you sure it is a simulator bug and will be resolved in hardware??

  • Jimit Raja said:
    As far as I know, simulator has always done what real hardware has done actually

    For the CPu core, this may be true. For anything that's related to the hardwar emodules or deals with external signals, this is only partly true. There are a lot of differences. Some of them are device-specific differences (like some internal signals internally connected on one MSP but not on a different one of the same sub-family). This is much too difficult to simulate and keep up-to-date in teh simulator unit.

    So don't use the simulator for anything than algorithm-analysis and perhaps timer usage (but not for timer CCR stuff). Foe everything else, use the real hardware.

  • okay..

    will surely use real hardware and give my feed back..

    Thank you Mr. Jens-Michael and Mr. old_cow_yellow!!

    Your suggestions helped a lot.

  • I forgot to answer this one:

    Jimit Raja said:
    Thank you for the suggestion. will surely apply. can you tell me how much delay is required?

    It depends on the mechanical part that cause teh bouncing. Usually 100ms are a good debouncing time. But you can go for more or less, depending on how fast you want to react on the keypress and how long you expext and want to suppress the possible bouncing.

  • I know its a very old post...But I am faced with the similar problem..Cant seem to think what to do...as the P2IFG switch statement doesnt respond as I want to be.. The program switches between pages when P2.0 is pressed. But for P2.1 and P2.2 nothing happens....?? I tried the routine for incrementing hours and minutes..on P2.0 and seems to work perfectly..However, with all three case statements intact it doesnot work....can u help please?? scroll down to port2 ISR

    #include <msp430fg4618.h>//Port definitions
    #include <intrinsics.h>// For BCD calculation
    #include <stdint.h>
    #include "LCD_defs.h"
    #define LCDMEMS 11 //to access the LCD memory
    uint8_t * const LCDMem= (uint8_t *) &LCDM3;//pointer to LCDMem which is defined in header file(used an array later)
    #define SEG_A BIT0
    #define SEG_B BIT1
    #define SEG_C BIT2
    #define SEG_D BIT3
    #define SEG_E BIT4
    #define SEG_F BIT5
    #define SEG_G BIT6
    #define SEG_H BIT7 // define statement.. Each time they appear in the codewill be replaced by subsequent bits

    const uint8_t LCDHexChar[] = {
    SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_G, //0
    SEG_B | SEG_C , //1
    SEG_A | SEG_B | SEG_D | SEG_G | SEG_F, //2
    SEG_A | SEG_B | SEG_C | SEG_D | SEG_F, //3
    SEG_B | SEG_C | SEG_E | SEG_F, //4
    SEG_A | SEG_C | SEG_D | SEG_E | SEG_F, //5
    SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G, //6
    SEG_A | SEG_B | SEG_C, //7,
    SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F |SEG_G,//8
    SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, //9

    };

    const uint8_t LCDAMChar = SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G;//display characters to indicate AM(A) and PM (p)
    const uint8_t LCDPMChar = SEG_A | SEG_B | SEG_E | SEG_F | SEG_G;
    const uint8_t LCDBlankChar = 0;
    unsigned char page;
    static unsigned short seconds = 0x59; //initial time in BCD 23:59:59 (Global variables for now)otherwise will have the flickering problem
    static unsigned short minutes = 0x59;
    static unsigned short hours = 0x23;
    static unsigned short Aminutes=0x01; //(declaration as local variable will cause problems)
    static unsigned short Ahours=0x00;
    static unsigned short Aseconds = 0x00;

    void InitLCD (void);



    void LCD_sec(void){
    LCDMem[1] = LCDHexChar[seconds & 0x0F]; //Updating all the digits ;segment H is the colon
    LCDMem[2] = LCDHexChar[(seconds >> 4) & 0x0F] | SEG_H ;
    }

    void LCD_Asec(void){
    LCDMem[1] = LCDHexChar[Aseconds & 0x0F]; //Updating all the digits ;segment H is the colon
    LCDMem[2] = LCDHexChar[(Aseconds >> 4) & 0x0F] | SEG_H ;
    }


    void LCD_min(void){
    LCDMem[3] = LCDHexChar[minutes & 0x0F];
    LCDMem[4] = LCDHexChar[(minutes >> 4) & 0x0F] | SEG_H;
    }

    void LCD_Amin(void){
    LCDMem[3] = LCDHexChar[Aminutes & 0x0F];
    LCDMem[4] = LCDHexChar[(Aminutes >> 4) & 0x0F] | SEG_H;
    }

    void LCD_hour(void){

    LCDMem[5] = LCDHexChar[hours & 0x0F];
    LCDMem[6] = LCDHexChar[(hours >> 4) & 0x0F] | SEG_H;
    }

    void LCD_Ahour(void){

    LCDMem[5] = LCDHexChar[Ahours & 0x0F];
    LCDMem[6] = LCDHexChar[(Ahours >> 4) & 0x0F] | SEG_H;
    }



    //will be used later(Prototype declared)

    void main(void) {
    WDTCTL = WDTPW|WDTHOLD; //WATCHDOG TIMER STOP
    InitLCD();
    TACCR0 = 0x8000; //upper limit for timer count
    TACCTL0 = CCIE; //enable compare 0 interrupts
    TACTL = MC_1|TASSEL_1|TACLR; //setting up timer A ,no clock division



    P1DIR&=~BIT0; //To enable these pins to be used as inputs
    P1DIR&=~BIT1;

    //Setting bits to be use an I/O interrupt //

    P1IE=0x03;// interrupt enable on P1.0 and P1.1
    P1OUT = 0x03;//pull up enable on P1.0 and P1.1
    P1IFG&=~0X03;



    P1IES &= ~0x01;
    P1SEL &= ~0x01;
    //P2DIR=0X02;
    //P2DIR&=~BIT0;
    //P2OUT&=~0X02;

    P2SEL &= ~0x07;
    P2DIR &= ~0x07; // P2.0 digital inputs
    P2IFG &=~ 0x07; // Clear P2 flags
    P2IES |= 0x07; // high-to-low transition interrupts
    P2IE |= 0x07;

    P5DIR |= 0x1E; // Ports P5.2, P5.3 and P5.4 as outputs
    P5SEL |= 0x1E;
    // Basic Timer 1 Configuration
    BTCTL = BT_fCLK2_DIV64; // (ACLK/256)/64
    IE2 |= BTIE; // Enable BT interrupt with 0.5 periode

    //P1OUT = 0x02;//clear the interrupt flag
    //P1IE=0x02;

    //P1IES&=~BIT0;


    __enable_interrupt();

    for (;;) {
    __low_power_mode_3(); //Asynchronous clock continues to run
    }




    }
    //INITIALIZING THE LCD DISPLAY
    void InitLCD (void)
    {
    int i;
    for (i = 0; i <LCDMEMS; ++i) { //clear LCD memory
    LCDMem [i] = 0;
    }
    //configuring LCD-A controller
    P5SEL =BIT4|BIT3|BIT2;//shared pins of port 5 are assigned to backplanes (COM)
    LCDAPCTL0 =LCDS4|LCDS8|LCDS12|LCDS16|LCDS20|LCDS24;//Enable LCD segs 4-27 (till 25 used),
    LCDAVCTL0 = 0;// no charge pump used
    LCDACTL = LCDFREQ_128 |LCD4MUX|LCDSON |LCDON;//Aclk/128 ; 4 mux; segments on;LCD_A on


    }

    #pragma vector=BASICTIMER_VECTOR
    __interrupt void basic_timer_ISR(void)
    {
    //P2OUT |=0x02; // LED1 turn on
    //P2OUT ^=0x04; // LED2 toogle

    if (page == 0) // Real time clock
    {
    LCD_sec();
    LCD_min();
    LCD_hour();
    }
    else // calendar
    {
    LCD_Asec();
    LCD_Amin();
    LCD_Ahour();
    }

    if (seconds & 0x01) // toogle clock dots
    {
    P3_DOT_ON;
    P5_DOT_ON;
    }
    else
    {
    P3_DOT_OFF;
    P5_DOT_OFF;
    }
    if(seconds==Aseconds && minutes==Aminutes && hours==Ahours)

    P5OUT=0x02;

    // LED1 turn off
    }






    //ISR FOR TIMER//
    #pragma vector = TIMERA0_VECTOR
    __interrupt void TIMERA0_ISR (void){
    //variables need to be static so that they dont change and short as to match with the intrinsic functions for BCD arithmetic
    //static unsigned short seconds = 0x59; //initial time in BCD 23:59:59
    //static unsigned short minutes = 0x59; (declaration as local variables will cause problems)
    //static unsigned short hours = 0x23;
    //Updating time//
    seconds = __bcd_add_short(seconds, 0x01);//seconds incremented(intrinsic function used)
    if(seconds >= 0x60){
    seconds = 0;

    minutes = __bcd_add_short(minutes,0x01); //minutes incremented
    if(minutes >= 0x60){
    minutes = 0; //new hour started
    hours = __bcd_add_short(hours,0x01);//hours incremented
    if (hours >= 0x24){
    hours = 0; //new day
    }

    }
    }
    LCDMem[1] = LCDHexChar[seconds & 0x0F]; //Updating all the digits ;segment H is the colon
    LCDMem[2] = LCDHexChar[(seconds >> 4) & 0x0F] | SEG_H ;

    LCDMem[3] = LCDHexChar[minutes & 0x0F];
    LCDMem[4] = LCDHexChar[(minutes >> 4) & 0x0F] | SEG_H;

    LCDMem[5] = LCDHexChar[hours & 0x0F];
    LCDMem[6] = LCDHexChar[(hours >> 4) & 0x0F] | SEG_H;
    LCDMem[11] = LCDHexChar[hours & 0x0F];
    }


    ////////////////////////////////////////////TIME ADJUSTMENT ///////////////////////////////////////////////////////////////
    /*
    #pragma vector = PORT1_VECTOR

    __interrupt void Port1_ISR(void)
    //I/O interrupt on port 1
    {
    //static unsigned short minutes=0x59; (declaration as local variable will cause problems)
    //static unsigned short hours=0x23;
    volatile unsigned int i;

    for (i=0x1FFF; i > 0; i--); //DELAY to tackle switch bounce

    if ((P1IN & BIT0)==0){
    hours = __bcd_add_short(hours,0x01); //when button pressed increment hours
    if (hours >= 0x24){
    hours = 0;
    //P1IFG&=0;
    }
    //P1IES|=BIT0; // PIES needs to be 1 (for1-0 edge==button pressed)
    }

    if ((P1IN & BIT1)==0){
    minutes = __bcd_add_short(minutes,0x01);//when button pressed increment minutes
    if(minutes >= 0x60){
    minutes = 0;

    }

    //P1IES|=BIT1;
    }

    LCDMem[3] = LCDHexChar[minutes & 0x0F];
    LCDMem[4] = LCDHexChar[(minutes >> 4) & 0x0F] | SEG_H;

    LCDMem[5] = LCDHexChar[hours & 0x0F];
    LCDMem[6] = LCDHexChar[(hours >> 4) & 0x0F] | SEG_H;

    }
    */
    #pragma vector=PORT2_VECTOR
    __interrupt void PORT2_ISR(void)
    {

    volatile unsigned int i;

    for (i=0x1FFF; i > 0; i--);//to tackle switch bounce

    switch(P2IFG)
    {
    case 0x01:
    if (page == 0) {page = 1; // switch between the two pages...just for going into alarm mode
    }else {page = 0;}

    break;
    case 0x02:

    P2IN=0x02;

    if ( page==0){
    minutes = __bcd_add_short(minutes, 0x01);
    if(minutes>=60){
    minutes=0;
    }
    }else
    {
    Aminutes = __bcd_add_short(Aminutes, 0x01);
    if(Aminutes>=60){
    Aminutes=0;
    }

    }

    break;
    case 0x04:
    P2IN=0x04;
    if ( page==0){
    hours = __bcd_add_short(hours, 0x01);
    if(hours>=23){
    hours=0;
    }
    }else
    {
    Ahours = __bcd_add_short(Ahours, 0x01);
    if(Ahours>=23){
    Aminutes=0;
    }
    }



    break;


    }

    P2IFG&=~0x07;
    }




    /*
    #pragma vector=PORT2_VECTOR
    __interrupt void PORT2_ISR (void){
    if (page == 0) page = 1; // switch between the two pages...just for going into alarm mode
    else page = 0;
    P2IFG&=~0x01;
    }
    */






  • Mr. Agha,

    I am but a newbie and am not so sure of i could answer the question, but

    case 0x02:

    P2IN=0x02;
    and
    case 0x04:
    P2IN=0x04;
    
    
    can you tell me what these statements are for?
    You can also try using switch(P2IN) instead of switch(P2IFG)
    because even if P2IFG switch statement does not function, the P2IN statement will. and during a button press, P2IN and P2IFG will have the same values.
  • Hi Jimit

    Sorry those P2IN statements have to be commented out.!! Even If they are removed the same problem exsists. As i said before If i run the same 

    ISR on port 1 it works perfectly. However,it doesnt work on port 2. The datasheet says that port1 pins have a single interrupt vector

    and port2 pins have different single interrupt vector. I was wondering if it has something do with that??

  • Actually a switch statement is the worst option on an intput register.
    You have 8 independent bits that can happen in any combination. So you'd have to have 256 cases.

    It's better to do:

    x = P2IN; // (or P2IFG)
    if (x&0x01){...}
    if (x&0x02){...}
    if (x&0x04){...}
    ...

    Imagine what happens when bit 0 and bit 1 are both set - then P2In (or P2IFG) has the value 3, which won't be handled by the switch.

    This is why newer MSPs have a vector register that only returns the number of the highest pending interrupt.

**Attention** This is a public forum