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.

reading two push buttons simultaneously with interrupts

Other Parts Discussed in Thread: MSP430G2553

Hi,

I am trying to write a code on the launchpad msp430g2553, which would simulate the output of several simple logic gates. 

I have connected 2 push buttons as inputs to pins P1.3 and P1.7 and 4 LEDs as outputs to P1.0, P1.4, P1.5, and P1.6.

2 of the LEDs are representing the "truth" and "false" of the truth table and the other 2 are just to indicate that a push button has been pressed (1 for each). 

At first I did it with polling with 6 diferrent logic gates in switch cases and it is working just fine. 

#include <msp430g2553.h>
#include <stdlib.h>


void main(void) {

   WDTCTL = WDTPW + WDTHOLD;        // Stop watchdog timer

   P1DIR |= 0x71;                   // Set P1.0, P1.4, P1.5, and P1.6 to output direction, 1 is output

   P1OUT &= ~0x71;                  // Set all the LED off

   P1DIR &= ~0x88;                  // Port 1 P1.3 and P1.7 (push buttons) as input, 0 is input

   P1SEL &= ~0x88;                  // Select Port 1 P1.3 and P1.7 (push buttons), 0 selects

 //  P1REN |=  0x88; 				// Enable Port 1 P1.3 (push button) pull-up resistor

   srand (time(NULL));				// Initialise random seed

    long random= rand() % 6 + 1;	// generate random number from 1 to 6


while(1){
      if( (P1IN & 0x08) == 0x08)    // if Push button at P1.3 is pressed

          P1OUT |= 0x40;            // Set LED at 1.6 ON when button down
      else
    	  P1OUT  &= ~0x40; 			// Set LED at 1.6 OFF

      if ((P1IN & 0x80) == 0x80)    // if Push button at P1.7 is pressed

          P1OUT |= 0x01;			// Set LED at 1.0 ON when button down
      else
          P1OUT  &= ~0x01;			// Set LED at 1.6 OFF

       switch(random){

  case 1: 							//logic "AND"
       if ((P1IN & 0x88) == 0x88)   //if both push buttons are pressed
                {
                P1OUT |= 0x10;		// Set "TRUE" LED at 1.4 ON
                P1OUT &= ~0x20;		// Set "FALSE" LED at 1.5 OFF
                    }
               else{
                P1OUT |= 0x20;      // Set "FALSE" LED at 1.5 ON
                P1OUT &= ~0x10;		// Set "TRUE" LED at 1.5 OFF
                   }
                	   break;

  case 2:							 //logic "OR"
	  if (((P1IN & 0x88) == 0x88) || ((P1IN & 0x80) == 0x80) || ((P1IN & 0x08) == 0x08) )
	  {
		  	  	 P1OUT |= 0x10;
		  	  	 P1OUT &= ~0x20;

                }
               else{
            	 P1OUT |= 0x20;
            	 P1OUT &= ~0x10;
                   }
	  	  	  	  	  	break;
  case 3:							 //logic "XNOR"
 	  if (((P1IN & 0x88) == 0x88) || ((P1IN & 0x80) == 0x80) || ((P1IN & 0x08) == 0x08) )                {
 		  	  	 P1OUT |= 0x20;
 		         P1OUT &= ~0x10;
                 }
                else{
	             P1OUT |= 0x10;
	             P1OUT &= ~0x20;
                	}
 	  	  	  	  	  	break;
  case 4:						     //logic "XOR"
	  if  ((P1IN & 0x88) == 0x88)
	  {
		  	     P1OUT |= 0x20;
		  	     P1OUT &= ~0x10;
	  }
	  else  if  ((P1IN & 0x80) == 0x80){

 		  	  	 P1OUT |= 0x10;
 		         P1OUT &= ~0x20;
                 }
	  else if  ((P1IN & 0x08) == 0x08){

 		 	 	 P1OUT |= 0x10;
 		 		 P1OUT &= ~0x20;

 	   }
                else{
	             P1OUT |= 0x20;
	             P1OUT &= ~0x10;
                }
             	         break;
  case 5: 							   //logic "NAND" {
                     if ((P1IN & 0x88) == 0x88)
                     {
                 P1OUT |= 0x20;
                 P1OUT &= ~0x10;
                     }
                    else{
                 P1OUT |= 0x10;        // Set green LED off when button up
                 P1OUT &= ~0x20;
                    }
                 	      break;
  case 6: 							   //logic "XNOR"
	  if  ((P1IN & 0x88) == 0x88)
	  {
		  	  	 P1OUT |= 0x10;
		  	     P1OUT &= ~0x20;
	  }
	  else  if  ((P1IN & 0x80) == 0x80){

 		  	  	 P1OUT |= 0x20;
 		         P1OUT &= ~0x10;
                 }
	  else if  ((P1IN & 0x08) == 0x08)
	  {
		  	  	 P1OUT |= 0x20;
 		 	     P1OUT &= ~0x10;
	  }
                else{
	             P1OUT |= 0x10;
	             P1OUT &= ~0x20;
                }
             	   	      break;

  default:
	  P1OUT &= ~0x71;
    }

  }
}

 

But now I am trying to do it with interrupts and I get problems while pressing both buttons at the same time (simulating 2 inputs of ´1´ and ´1´). for pressing one of the buttons I used this code and it works:

#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
if(P1IFG & BUTTON){
      P1OUT ^= LED1; // P1.6 = toggle
      P1IFG &= ~BUTTON; // P1.3 IFG cleared
      P1IES ^= BUTTON; // toggle the interrupt edge,
            }
But when I want to set the "truth" LED on while pressing both buttons at the same time it does not work
       if ((P1IFG & BUTTON1)&&(P1IFG & BUTTON)){
            P1OUT ^= LED2;
            P1IFG &= ~(BUTTON + BUTTON1);
            P1IES ^= (BUTTON + BUTTON1);
      }

I have stopped coding when I realized that I got it wrong,so the code is not complete, but eventually I want to code it so it functions exactly as the first polling code.

#include <msp430x20x2.h>

#define LED0 BIT0
#define LED1 BIT6
#define LED2 BIT4
#define BUTTON BIT3
#define BUTTON1 BIT7



int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1DIR |= (LED0 + LED1 + LED2);
P1OUT &= ~(LED0 + LED1 + LED2);
P1IE |= (BUTTON + BUTTON1); // P1.3 and P1.7 interrupt enabled
P1IFG &= ~(BUTTON + BUTTON1); // P1.3 and P1.7 IFG cleared
__enable_interrupt(); // enable all interrupts

for(;;)
{}
}


// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{

	if(P1IFG & BUTTON){
	P1OUT ^= LED1; // P1.6 = toggle
	P1IFG &= ~BUTTON; // P1.3 IFG cleared
	P1IES ^= BUTTON; // toggle the interrupt edge,
	// the interrupt vector will be called
	// when P1.3 goes from HitoLow as well as
	// LowtoHigh
		}
	if(P1IFG & BUTTON1){
	P1OUT ^= LED0; // P1.0 = toggle
	P1IFG &= ~BUTTON1; // P1.7 IFG cleared
	P1IES ^= BUTTON1; // toggle the interrupt edge,
	// the interrupt vector will be called
	// when P1.3 goes from HitoLow as well as
	// LowtoHigh
		}
	 if ((P1IFG & BUTTON1)&&(P1IFG & BUTTON)){
		P1OUT ^= LED2;
		P1IFG &= ~(BUTTON + BUTTON1);
		P1IES ^= (BUTTON + BUTTON1);
	}

}

 

I am new to msp430 and to microcontrollers and I understand that there is a problem with how I handle the interrupts (the ISR reads only 1 interrupt at a time??) but can't figure out how to fix it. 

 

any help would be much appreciated

Many thanks,

Shahar

  • You are not going to enter the ISR where both buttons are triggering an interrupt at the same time so your branch condition will never happen during the ISR. This means that even when you press the buttons "at the same time" one always occurs before the other.

    Basically you will enter the interrupt handler for one button and clear the P1IFG for that button and then reenter the interrupt handler for the second button but at this time P1IFG has been cleared for the previous button push. This means that (P1IFG & BUTTON1) is mutually exclusive with (P1IFG & BUTTON) and the branch is never taken.

    P1IFG & BUTTON1, or the other condition, tells you when you have entered the ISR for a BUTTON1, or BUTTON, change but for your purposes you only want to tell the state of both buttons in order to update your truth values and LEDs.

  • Thank you for your response. 

    So it means that I can't read the status of 2 buttons using interrupts?

    Is there a way to do it or I just should stick with the polling solution?

  • You can do this within using interrupts. So your code that looks like below:

    if(P1IFG & BUTTON){

    }

    You are clearing the interrupt and changing the edge condition for the specific button that is pressed. This is a proper way to handle the interrupt but within these if statements you can only tell the state of one button (i.e. either button or button1 is pressed or released)

    In order to do what you want, you need to know the state of BOTH buttons. After you handle the interrupts in the if statements mentioned above you should read the state of the ports. I will preface this with I have not done much programming on the device so I cannot give you the exact code. Both ports that you are using P1.3 and P1.7 should be inputs and you should be able to read their state, either 1 or 0, via some port memory mapped register.

    Your branch statement for when both buttons should be something similar to "if(P1.3 && P1.7)" and can come at the end of the interrupt handler to update your logic values.

  • Shahar Levi said:
    ... So it means that I can't read the status of 2 buttons using interrupts? ...

    Yes, you can read the status of 2 (or more) buttons using interrupts. Your problem is your fingers cannot press 2 (or more) buttons at the same instance in time. The MSP430 is much faster than your fingers. Within micro seconds of the instance one of the two buttons is pressed, your ISR code will read the status. At that time, the other button is not pressed yet despite of the fact that you think you are pressing both of them "at the same time". In reality, one can be milliseconds ahead of the other.

    You may inset a delay of a few milliseconds in your ISR before attempt to read the status. This way, you have a change to fine that they are both pressed. But this is a silly idea.

  • Thanks for your comment Matthew ! 

    I got it working with reading the pins state (PxIN) withing the ISR instead of reading the interrupt flags (PxIFG), as you suggested. 

    in this manner: 

    #pragma vector=PORT1_VECTOR
    __interrupt void Port_1(void)
    {

    switch (random){

    case 1:   // logic gate "AND" 
    if((P1IN & 0x88) == 0x88){ 
          P1OUT |= 0x10; // set "TRUE" LED at P1.4 ON
          P1OUT &= ~0x20; // 
    set "FALSE" LED at P1.5 OFF
                }

    else{

          P1OUT |= 0x20; // set "FALSE" LED at P1.5 ON

          P1OUT &= ~0x10; // set "TRUE" LED at P1.4 OFF

                 }

    break;

    case 2:  //logic gate "OR"

    (...)

     

    next stage will be to Implement it with timer interrupts!

  • Just to make sure you are still clearing the interrupt flag and toggling the edge interrupt condition within the handler, correct? If you don't clear the flag you will constantly enter the ISR which will give you the same performance as polling. Just wanted to make sure as I didn't see it in your code above.

  • I was actually wondering if I'm doing it right. I am clearing the interrupt flag and toggling the edge interrupt in the if statements that check the P1IFG, but not in the if statements which read the P1IN.

    Please have a look in the full code. the first 2 if branches in the ISR are handling the LEDs which indicate the each of the buttons is pressed. there I used interrupt flag clearing and edge toggling. The other branches are in switch cases and each represents different gate logic. There I am reading the P1IN. Is it correct interrupt handling? if not, where should I add the flag clearing? thanks!

     

    #include <msp430x20x2.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define LED0 BIT0
    #define LED1 BIT6
    #define LED2 BIT4
    #define LED3 BIT5
    #define BUTTON BIT3
    #define BUTTON1 BIT7
    
    long random;
    
    int main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    P1DIR |= (LED0 + LED1 + LED2 + LED3);
    P1OUT &= ~(LED0 + LED1 + LED2 + LED3);
    P1IE |= (BUTTON + BUTTON1); // P1.3 and P1.7 interrupt enabled
    P1IFG &= ~(BUTTON + BUTTON1); // P1.3 and P1.7 IFG cleared
    __enable_interrupt(); // enable all interrupts
    
    srand (time(NULL));				// Initialise random seed
    
     random= rand() % 6 + 1;	// generate random number from 1 to 6
    
    for(;;)
    {}
    }
    
    // Port 1 interrupt service routine
    #pragma vector=PORT1_VECTOR
    __interrupt void Port_1(void)
    {
    
    	if(P1IFG & BUTTON){  //handle the LED which indicates the right button
    	P1OUT ^= LED1; // P1.6 = toggle
    	P1IFG &= ~BUTTON; // P1.3 IFG cleared
    	P1IES ^= BUTTON; // toggle the interrupt edge,
    	// the interrupt vector will be called
    	// when P1.3 goes from HitoLow as well as
    	// LowtoHigh
    		}
    	if (P1IFG & BUTTON1){//handle the LED which indicates the left button
    	P1OUT ^= LED0; // P1.0 = toggle
    	P1IFG &= ~BUTTON1; // P1.7 IFG cleared
    	P1IES ^= BUTTON1; // toggle the interrupt edge,
    	// the interrupt vector will be called
    	// when P1.7 goes from HitoLow as well as
    	// LowtoHigh
    		}
    
        switch(random){
    
    case 1: 							//logic "AND"
         if ((P1IN & 0x88) == 0x88)   //if both push buttons are pressed
                  {
                  P1OUT |= 0x10;		// Set "TRUE" LED at 1.4 ON
                  P1OUT &= ~0x20;		// Set "FALSE" LED at 1.5 OFF
                      }
                 else{
                  P1OUT |= 0x20;      // Set "FALSE" LED at 1.5 ON
                  P1OUT &= ~0x10;		// Set "TRUE" LED at 1.4 OFF
                     }
                  	   break;
    
    case 2:							 //logic "OR"
    	  if (((P1IN & 0x88) == 0x88) || ((P1IN & 0x80) == 0x80) || ((P1IN & 0x08) == 0x08) )
    	  {
    		  	  	 P1OUT |= 0x10;
    		  	  	 P1OUT &= ~0x20;
    
                  }
                 else{
              	 P1OUT |= 0x20;
              	 P1OUT &= ~0x10;
                     }
    	  	  	  	  	  	break;
    case 3:							 //logic "XNOR"
    	  if (((P1IN & 0x88) == 0x88) || ((P1IN & 0x80) == 0x80) || ((P1IN & 0x08) == 0x08) )                {
    		  	  	 P1OUT |= 0x20;
    		         P1OUT &= ~0x10;
                   }
                  else{
    	             P1OUT |= 0x10;
    	             P1OUT &= ~0x20;
                  	}
    	  	  	  	  	  	break;
    case 4:						     //logic "XOR"
    	  if  ((P1IN & 0x88) == 0x88)
    	  {
    		  	     P1OUT |= 0x20;
    		  	     P1OUT &= ~0x10;
    	  }
    	  else  if  ((P1IN & 0x80) == 0x80){
    
    		  	  	 P1OUT |= 0x10;
    		         P1OUT &= ~0x20;
                   }
    	  else if  ((P1IN & 0x08) == 0x08){
    
    		 	 	 P1OUT |= 0x10;
    		 		 P1OUT &= ~0x20;
    
    	   }
                  else{
    	             P1OUT |= 0x20;
    	             P1OUT &= ~0x10;
                  }
               	         break;
    case 5: 							   //logic "NAND" {
                       if ((P1IN & 0x88) == 0x88)
                       {
                   P1OUT |= 0x20;
                   P1OUT &= ~0x10;
                       }
                      else{
                   P1OUT |= 0x10;        // Set green LED off when button up
                   P1OUT &= ~0x20;
                      }
                   	      break;
    case 6: 							   //logic "XNOR"
    	  if  ((P1IN & 0x88) == 0x88)
    	  {
    		  	  	 P1OUT |= 0x10;
    		  	     P1OUT &= ~0x20;
    	  }
    	  else  if  ((P1IN & 0x80) == 0x80){
    
    		  	  	 P1OUT |= 0x20;
    		         P1OUT &= ~0x10;
                   }
    	  else if  ((P1IN & 0x08) == 0x08)
    	  {
    		  	  	 P1OUT |= 0x20;
    		 	     P1OUT &= ~0x10;
    	  }
                  else{
    	             P1OUT |= 0x10;
    	             P1OUT &= ~0x20;
                  }
               	   	      break;
    
    default:
    	  P1OUT &= ~0x71;
      }
    
    }
    

    #include <msp430x20x2.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define LED0 BIT0
    #define LED1 BIT6
    #define LED2 BIT4
    #define LED3 BIT5
    #define BUTTON BIT3
    #define BUTTON1 BIT7
    
    long random;
    
    int main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    P1DIR |= (LED0 + LED1 + LED2 + LED3);
    P1OUT &= ~(LED0 + LED1 + LED2 + LED3);
    P1IE |= (BUTTON + BUTTON1); // P1.3 and P1.7 interrupt enabled
    P1IFG &= ~(BUTTON + BUTTON1); // P1.3 and P1.7 IFG cleared
    __enable_interrupt(); // enable all interrupts
    
    srand (time(NULL));				// Initialise random seed
    
     random= rand() % 6 + 1;	// generate random number from 1 to 6
    
    for(;;)
    {}
    }
    
    // Port 1 interrupt service routine
    #pragma vector=PORT1_VECTOR
    __interrupt void Port_1(void)
    {
    
    	if(P1IFG & BUTTON){  //handle the LED which indicates the right button
    	P1OUT ^= LED1; // P1.6 = toggle
    	P1IFG &= ~BUTTON; // P1.3 IFG cleared
    	P1IES ^= BUTTON; // toggle the interrupt edge,
    	// the interrupt vector will be called
    	// when P1.3 goes from HitoLow as well as
    	// LowtoHigh
    		}
    	if (P1IFG & BUTTON1){//handle the LED which indicates the left button
    	P1OUT ^= LED0; // P1.0 = toggle
    	P1IFG &= ~BUTTON1; // P1.7 IFG cleared
    	P1IES ^= BUTTON1; // toggle the interrupt edge,
    	// the interrupt vector will be called
    	// when P1.7 goes from HitoLow as well as
    	// LowtoHigh
    		}
    
        switch(random){
    
    case 1: 							//logic "AND"
         if ((P1IN & 0x88) == 0x88)   //if both push buttons are pressed
                  {
                  P1OUT |= 0x10;		// Set "TRUE" LED at 1.4 ON
                  P1OUT &= ~0x20;		// Set "FALSE" LED at 1.5 OFF
                      }
                 else{
                  P1OUT |= 0x20;      // Set "FALSE" LED at 1.5 ON
                  P1OUT &= ~0x10;		// Set "TRUE" LED at 1.4 OFF
                     }
                  	   break;
    
    case 2:							 //logic "OR"
    	  if (((P1IN & 0x88) == 0x88) || ((P1IN & 0x80) == 0x80) || ((P1IN & 0x08) == 0x08) )
    	  {
    		  	  	 P1OUT |= 0x10;
    		  	  	 P1OUT &= ~0x20;
    
                  }
                 else{
              	 P1OUT |= 0x20;
              	 P1OUT &= ~0x10;
                     }
    	  	  	  	  	  	break;
    case 3:							 //logic "XNOR"
    	  if (((P1IN & 0x88) == 0x88) || ((P1IN & 0x80) == 0x80) || ((P1IN & 0x08) == 0x08) )                {
    		  	  	 P1OUT |= 0x20;
    		         P1OUT &= ~0x10;
                   }
                  else{
    	             P1OUT |= 0x10;
    	             P1OUT &= ~0x20;
                  	}
    	  	  	  	  	  	break;
    case 4:						     //logic "XOR"
    	  if  ((P1IN & 0x88) == 0x88)
    	  {
    		  	     P1OUT |= 0x20;
    		  	     P1OUT &= ~0x10;
    	  }
    	  else  if  ((P1IN & 0x80) == 0x80){
    
    		  	  	 P1OUT |= 0x10;
    		         P1OUT &= ~0x20;
                   }
    	  else if  ((P1IN & 0x08) == 0x08){
    
    		 	 	 P1OUT |= 0x10;
    		 		 P1OUT &= ~0x20;
    
    	   }
                  else{
    	             P1OUT |= 0x20;
    	             P1OUT &= ~0x10;
                  }
               	         break;
    case 5: 							   //logic "NAND" {
                       if ((P1IN & 0x88) == 0x88)
                       {
                   P1OUT |= 0x20;
                   P1OUT &= ~0x10;
                       }
                      else{
                   P1OUT |= 0x10;        // Set green LED off when button up
                   P1OUT &= ~0x20;
                      }
                   	      break;
    case 6: 							   //logic "XNOR"
    	  if  ((P1IN & 0x88) == 0x88)
    	  {
    		  	  	 P1OUT |= 0x10;
    		  	     P1OUT &= ~0x20;
    	  }
    	  else  if  ((P1IN & 0x80) == 0x80){
    
    		  	  	 P1OUT |= 0x20;
    		         P1OUT &= ~0x10;
                   }
    	  else if  ((P1IN & 0x08) == 0x08)
    	  {
    		  	  	 P1OUT |= 0x20;
    		 	     P1OUT &= ~0x10;
    	  }
                  else{
    	             P1OUT |= 0x10;
    	             P1OUT &= ~0x20;
                  }
               	   	      break;
    
    default:
    	  P1OUT &= ~0x71;
      }
    
    }
    
  • This looks fine as far as clearing the flags. You should only be generating an interrupt per each button press or release with this code.

    You only need to clear the flags and switch triggering condition once within the ISR so you don't need to worry about it within your switch statement.

  • Thanks. I appreciate it

  • Matthew Kracht said:
    This looks fine as far as clearing the flags. You should only be generating an interrupt per each button press or release with this code.

    Well, in theory, with an ideal button. Unfortunately, most buttons aren't ideal and bounce.
    So it may happen that after a positive-going edge, the port input shows a low pin, before bouncing up again and triggering another positive edge etc.

    A good expansion to the posted code is to move it into a timer interrupt:

    On a port pin edge interrupt, port pin interrupts are blocked and a timer is started. When the timer interrupt comes, after any bouncing has settled, the port pin status is read and port interrupts are enabled again. Well, for detecting 'simultaneous' pressing of two buttons, things get even more complex, as between the first button's press and the second button's press, many to several hundreds of milliseconds may pass. Yet a single button press shall be detected quickly.
    UI systems usually have a long delay to detect a button press (or double-button press) but react immediately when the button is released.
    So B1low, B1 high is detected as a single-button press, while B1 low, B2 low, B1 (or B2) high is detected as a double-press. Also, B1 low and hold is detected as single button press, but only after some time (to ensure there won't be B2 pressed anymore).
    The required state machine is rather complex and can't be done with a simple if-chain. But the only way to ensure you're not misinterpreting user input. Except if you want to give instructions like 'press and hold button 1, then press button 2' (which could be handled with a simple if chain)

**Attention** This is a public forum