Jump to content
43oh

three buttons on port1 interrupt


Recommended Posts

I have three buttons on port 1 (1.5,1.6,1.7) for a three button  menu on an LCD do I need to create a single interrupt for each one (three interrupts) or can I use the same interrupt (if any button is pushed, figure out which one is pushed and do whatever) ?

 

I have used code and saw it working for a single button but I am confused when using three

 

Thanks 

 

Bill

Link to post
Share on other sites

Setup

P1DIR &= ~(BIT5 + BIT6 + BIT7)
P1IES = BIT5 + BIT6 + BIT7; //high -> low
P1OUT |= BIT5 + BIT6 + BIT7;
P1REN = BIT5 + BIT6 + BIT7;
P1IFG = 0;
P1IE = BIT5 + BIT6 + BIT7;

Enable global interrupts after setup

_bis_SR_register(GIE);

ISR

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

	if (P1IFG & BIT5) {
		//handle P1.5 switch
		P1IFG &= ~BIT5;
	}

	if (P1IFG & BIT6) {
		//handle P1.6 switch
		P1IFG &= ~BIT6;
	}

	if (P1IFG & BIT7) {
		//handle P1.7 switch
		P1IFG &= ~BIT7;
	}
}
Link to post
Share on other sites

Hello, Guys!

 

 So, I'm starting too with Mcu's and I already saw many solutions for beginners, but always have someone thing for understand.. For example in my code below, after of the led turn on in  isr, the led remain ON, unless which I put one loop for test the condition of the button:
 
 #include "MSP430G2553.h"
int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
  P1DIR |= BIT0 ;                             // P1.0 output, else input
  P1REN |= BIT3;
  P1IES |= BIT3;                            // P1.3  Hi/lo edge
  P1IFG &= ~BIT3;                           // P1.3  IFG cleared
  P1IE |= BIT3;                             // P1.3  interrupt enabled
  
  
 
  _BIS_SR(LPM4_bits + GIE);                 // Enter LPM4 w/interrupt
}
 
// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1_ISR(void)
{
  while(1)                // Loop Infinito
  {     
    if((P1IN & BIT3)==0)        // If S1 ==0
      P1OUT |= BIT0;        //Turn On Led 1 (Red)                                              // With this solution, Work 
    else 
      P1OUT &= ~BIT0;        //Turn Off Led 1 (Red)                                       Nevertheless if I change (inside of the interrupt) for just:   P1OUT |= BIT0;
    P1IFG &= ~BIT3;             // P1.3 IFG Cleared
  }                                                                                                                  and after   P1IFG &= ~BIT3;    The led turn on but, not turn off more..
}
 
Can you help me, please?
And Sorry by "invade" your topic..
 
 PS.: I hope which you have understood my ask, I'm Brazilian and my english is  the worst. Thank you very much! Hug!
Link to post
Share on other sites

@@EdiFirst, what are you trying to accomplish here?

Are you trying to toggle LED with S1?

I would like turn on the led when squeeze the  button by interrupt and,,leveraging topic I has been see which the Launchpad don't have   Basic Time  and don't have RTC.

How I can put more Push Buttons (ok have something like above, but still missing something ), with more leds each one with particular time(for turn on or turn off)?

 

 Thank you very much!

Link to post
Share on other sites

Do you want the LED to be on as long as you hold the button, or to toggle on each button press?

The button can only trigger an interrupt on one edge: from low to high or from high to low. So it won't be triggered on a button release normally.

A tricky way to solve this is to toggle P1IES bit 3 every time the interrupt is entered. But if the pin state already changed before that (a phenomenon called spurious interrupts) you will not trigger until the button release after the next button press. So this will only work if you check the state of the pin after toggling the P1IES bit.

Oh, and by the way, infinite loops or delays during an interrupt is very VERY bad practice, though I see why you did it here. In your case it will work the same if you'd move the whole infinite loop to the end of your main() function, since you won't do anything interrupty after it enters the loop; it's just a flat program then.

Link to post
Share on other sites

Do you want the LED to be on as long as you hold the button, or to toggle on each button press?

The button can only trigger an interrupt on one edge: from low to high or from high to low. So it won't be triggered on a button release normally.

A tricky way to solve this is to toggle P1IES bit 3 every time the interrupt is entered. But if the pin state already changed before that (a phenomenon called spurious interrupts) you will not trigger until the button release after the next button press. So this will only work if you check the state of the pin after toggling the P1IES bit.

Oh, and by the way, infinite loops or delays during an interrupt is very VERY bad practice, though I see why you did it here. In your case it will work the same if you'd move the whole infinite loop to the end of your main() function, since you won't do anything interrupty after it enters the loop; it's just a flat program then.

Thank you, Roadrunner for your attention! 

So on really I want which the Led turn on on when I hold the button. because if I put  on the line of the interrupt the P1OUT ^= BIT0; The led stay toggle,
The despite the infinite loops, I taked off the infinite loop then program stoped of work, the led turn on , but don't turn off more.
As I tried to explain up there, I would like to extend thats exercises for more Push buttons and more leds, and I'd like to use time for each action, for example, like a RTC( but as I say the MSP430G2553 don't have Basic Timer Or RTC), when press 1 button he to count Minute, other for Seconds...this is just for me learn to use Timer A..
Link to post
Share on other sites

It looks to me you don't have a real grasp of what you're doing. I suggest you to read a book on C programming and the MSP430x2xx user guide (google slau144) to get a better insight in the workings of the processor.

So what you'd like to do is (as an example) have 4 LEDs and 3 buttons, then the buttons each turn on an LED when depressed and turn it back off when released. The 4th LED should go on after a set period depending on the nuber of presses on the buttons. Is that about the setup you'd like? Let's assume the buttons are for 1 second, 10 seconds and 60 seconds (ie: 1 minute).

 

First off, we need to set up the button and timer interrupts. This is done in the main() function. Note that we cannot go lower than LPM3 because then the timer would be suspended too.

#include "msp430.h"
// "define" will replace every occurrence of the first word by the rest of the line, so P1DIR |= LED1; will become P1DIR |= BIT0;
#define LED1 BIT0
#define LED2 BIT1
#define LED3 BIT2
#define LEDT BIT7
#define BUTTON1 BIT3
#define BUTTON2 BIT4
#define BUTTON3 BIT5
// LEDS is the combination of all LEDs, BUTTONS is the combination of all buttons
#define LEDS (LED1 | LED2 | LED3 | LEDT)
#define BUTTONS (BUTTON1 | BUTTON2 | BUTTON3)

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
  P1DIR |= LEDS;                            // LED pins output, others unaffected
  P1REN |= BUTTONS;
  P1IES |= BUTTONS;                         // Buttons Hi/lo edge
  P1IFG &= ~BUTTONS;                        // Buttons IFG cleared
  P1IE |= BUTTONS;                          // Buttons interrupt enabled

  TACTL = TASSEL_2 | MC_1 | TACLR | TAIE;   // TimerA0 set to up mode on the ACLK (we assume a 32kiHz crystal is attached to pins P2.6 and P2.7)
  TACCR0 = 32768 - 1;                       // TimerA0 will overflow (and generate an interrupt) every 32768 ACLK ticks; 32768 ticks / 32768 Hz = 1 second

  _BIS_SR(LPM3_bits + GIE);                 // Enter LPM3 w/interrupt
}

Alright, so we set up the buttons to generate interrupts, the timer to generate interrupts and switched to low power mode. If you do not have the crystal attached the code would be a little different; you can either use the DCO clock, but that restricts power down even more, since DCO is shut off in LPM3. Or you use VLO, this requires an extra line of code to set the clock circuit to use VLO instead of LFXT1 as source for ACLK. In addition, VLO runs at a lower speed, so the value of 32768 needs to be changed. VLO is very power friendly, but not very stable; the frequency drifts with temperature.

 

Next step will be to set up the button interrupt, we need to figure out which button was pressed and turn the respective LED on, plus add a value to a timer variable. Oh, we need to declare that variable first.

volatile unsigned short timeout_counter;

Okay, that's the variable. Volatile indicates that the compiler may not deduct the value of the variable via any shortcut; it must read the actual value each time. Unsigned short means it's a non-negative 16 bit value.

Now for the button interrupt:

#pragma vector=PORT1_VECTOR
__interrupt void Port_1_ISR(void)
{
  if (P1IFG & BUTTON1) // Was BUTTON1 interrupt triggered?
  {
    if (P1IES & BUTTON1) // Was it a press action event?
    {
      timeout_counter += 1; // Increment counter by 1 second
    }
    P1OUT ^= LED1; // Toggle LED
    P1IES ^= BUTTON1; // Toggle the button interrupt
    P1IFG &= ~BUTTON1; // clear the interrupt
  }
  // repeat for 2 and 3, with increment with 10 and 60 seconds
}

Something like this, buttons are always tricky because of their glitchy behaviour. If you're using stable and non-bouncing switches, the above code works just fine. Otherwise you'd need to look into switch debouncing.

 

Next: the timer interrupt:

#pragma vector=TIMER_A0_VECTOR
__interrupt void Timer_A0_ISR(void)
{
  if (timeout_counter > 0)
  {
    P1OUT |= LEDT;
    timeout_counter -= 1; // only decrement the counter if it is still non-zero
  }
  else
  {
    P1OUT &= ~LEDT;
  }
}

And that's that. It should work something like this. If you paste all these code snippets together it should compile.

 

Note that I did not code the handlers for button 2 and 3, in these kind of cases I tend to move the whole thing to a function and call that with different parameters. Like this:

bool handle_button(const unsigned char button, const unsigned char led)
{
  bool return_value = false;

  if (P1IFG & button) // Was BUTTON1 interrupt triggered?
  {
    if (P1IES & button) // Was it a press action event?
    {
      return_value = true;
    }
    P1OUT ^= led; // Toggle LED
    P1IES ^= button; // Toggle the button interrupt
    P1IFG &= ~button; // clear the interrupt
  }
  return return_value;
}

#pragma vector=PORT1_VECTOR
__interrupt void Port_1_ISR(void)
{
  if (handle_button(BUTTON1, LED1)) timeout_counter += 1;
  if (handle_button(BUTTON2, LED2)) timeout_counter += 10;
  if (handle_button(BUTTON3, LED3)) timeout_counter += 60;
}

You see I moved the whole thing to a separate function, except for the timeout_counter increment. Instead I let the function return true if the button was pressed. The interrupt handler becomes really small and readable this way.

Link to post
Share on other sites

It looks to me you don't have a real grasp of what you're doing. I suggest you to read a book on C programming and the MSP430x2xx user guide (google slau144) to get a better insight in the workings of the processor.

So what you'd like to do is (as an example) have 4 LEDs and 3 buttons, then the buttons each turn on an LED when depressed and turn it back off when released. The 4th LED should go on after a set period depending on the nuber of presses on the buttons. Is that about the setup you'd like? Let's assume the buttons are for 1 second, 10 seconds and 60 seconds (ie: 1 minute).

 

First off, we need to set up the button and timer interrupts. This is done in the main() function. Note that we cannot go lower than LPM3 because then the timer would be suspended too.

#include "msp430.h"
// "define" will replace every occurrence of the first word by the rest of the line, so P1DIR |= LED1; will become P1DIR |= BIT0;
#define LED1 BIT0
#define LED2 BIT1
#define LED3 BIT2
#define LEDT BIT7
#define BUTTON1 BIT3
#define BUTTON2 BIT4
#define BUTTON3 BIT5
// LEDS is the combination of all LEDs, BUTTONS is the combination of all buttons
#define LEDS (LED1 | LED2 | LED3 | LEDT)
#define BUTTONS (BUTTON1 | BUTTON2 | BUTTON3)

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
  P1DIR |= LEDS;                            // LED pins output, others unaffected
  P1REN |= BUTTONS;
  P1IES |= BUTTONS;                         // Buttons Hi/lo edge
  P1IFG &= ~BUTTONS;                        // Buttons IFG cleared
  P1IE |= BUTTONS;                          // Buttons interrupt enabled

  TACTL = TASSEL_2 | MC_1 | TACLR | TAIE;   // TimerA0 set to up mode on the ACLK (we assume a 32kiHz crystal is attached to pins P2.6 and P2.7)
  TACCR0 = 32768 - 1;                       // TimerA0 will overflow (and generate an interrupt) every 32768 ACLK ticks; 32768 ticks / 32768 Hz = 1 second

  _BIS_SR(LPM3_bits + GIE);                 // Enter LPM3 w/interrupt
}

Alright, so we set up the buttons to generate interrupts, the timer to generate interrupts and switched to low power mode. If you do not have the crystal attached the code would be a little different; you can either use the DCO clock, but that restricts power down even more, since DCO is shut off in LPM3. Or you use VLO, this requires an extra line of code to set the clock circuit to use VLO instead of LFXT1 as source for ACLK. In addition, VLO runs at a lower speed, so the value of 32768 needs to be changed. VLO is very power friendly, but not very stable; the frequency drifts with temperature.

 

Next step will be to set up the button interrupt, we need to figure out which button was pressed and turn the respective LED on, plus add a value to a timer variable. Oh, we need to declare that variable first.

volatile unsigned short timeout_counter;

Okay, that's the variable. Volatile indicates that the compiler may not deduct the value of the variable via any shortcut; it must read the actual value each time. Unsigned short means it's a non-negative 16 bit value.

Now for the button interrupt:

#pragma vector=PORT1_VECTOR
__interrupt void Port_1_ISR(void)
{
  if (P1IFG & BUTTON1) // Was BUTTON1 interrupt triggered?
  {
    if (P1IES & BUTTON1) // Was it a press action event?
    {
      timeout_counter += 1; // Increment counter by 1 second
    }
    P1OUT ^= LED1; // Toggle LED
    P1IES ^= BUTTON1; // Toggle the button interrupt
    P1IFG &= ~BUTTON1; // clear the interrupt
  }
  // repeat for 2 and 3, with increment with 10 and 60 seconds
}

Something like this, buttons are always tricky because of their glitchy behaviour. If you're using stable and non-bouncing switches, the above code works just fine. Otherwise you'd need to look into switch debouncing.

 

Next: the timer interrupt:

#pragma vector=TIMER_A0_VECTOR
__interrupt void Timer_A0_ISR(void)
{
  if (timeout_counter > 0)
  {
    P1OUT |= LEDT;
    timeout_counter -= 1; // only decrement the counter if it is still non-zero
  }
  else
  {
    P1OUT &= ~LEDT;
  }
}

And that's that. It should work something like this. If you paste all these code snippets together it should compile.

 

Note that I did not code the handlers for button 2 and 3, in these kind of cases I tend to move the whole thing to a function and call that with different parameters. Like this:

bool handle_button(const unsigned char button, const unsigned char led)
{
  bool return_value = false;

  if (P1IFG & button) // Was BUTTON1 interrupt triggered?
  {
    if (P1IES & button) // Was it a press action event?
    {
      return_value = true;
    }
    P1OUT ^= led; // Toggle LED
    P1IES ^= button; // Toggle the button interrupt
    P1IFG &= ~button; // clear the interrupt
  }
  return return_value;
}

#pragma vector=PORT1_VECTOR
__interrupt void Port_1_ISR(void)
{
  if (handle_button(BUTTON1, LED1)) timeout_counter += 1;
  if (handle_button(BUTTON2, LED2)) timeout_counter += 10;
  if (handle_button(BUTTON3, LED3)) timeout_counter += 60;
}

You see I moved the whole thing to a separate function, except for the timeout_counter increment. Instead I let the function return true if the button was pressed. The interrupt handler becomes really small and readable this way.

Hello, Roadrruner!

 

I don't have words, for thank..You didn't talk about, you made a tutorial, kk.. ; )!
I am very grateful, hence that surely  I'll evolve.

Sorry by mess.. The despite of language and documentation I'm learning, but the User's Guide  help, nevertheless the codes are in Assembly(and I don't want to learn this language for now), if you judge necessary I'll post a video of running here..

Link to post
Share on other sites

Hi Bill,

 

good to hear you gained some insight from the code I posted. The User's Guide is the only manual I had when learning MSP430. It is very nicely built and contains all information to start understanding the MSP430.

A personal note: I can program assembly for several controllers, but nowadays I don't use it anymore. Assembly sheds some light on which kind of instructions are lightweight or heavyweight, but that's all I use of it.

For example: multiplication is quite heavy, since there is neither a multiplier, nor a barrel shifter available (as can be discovered from the assembly description). On the other hand, byte swapping, bit testing and pointer dereference are quite lightweight (as the SWPB, BIT and @Rn operations exist in assembly). As you can see, I use the assembly to decide how to optimise my C code, but I don't write the assembly directly.

As Rob pointed out Assembly does have uses when processing time is really critical, but it turns out it barely ever is in my applications.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...