0
\$\begingroup\$

I'm new to MSP430 and microcontrollers. Right now I'm learning interrupt routines and trying to write a program that changes the LED's state (OFF-ON-BLINK) by every press on P1.1 button. I have used the "switch-case" statement to achieve this and to control the switch statement's variable, I used a port interrupt. And for the blinking operation, I used a timer interrupt. Everything works as it should be. The program starts with the LED is off. One press on the button turns the LED on and if it's pressed once more, it starts to blink. And one more press, then the program should go to the initial position and turn the LED off. But there is a problem with the blink case.

When the LED starts to blink, the program stuck at the infinite loop where the timer get involved to toggle the LED state. Because of that, a press on the button has no effect, the LED continues to blink. Instead of this, the program should return back to Case 0 (see the code below)

Without the interrupts, (by usign polling etc.) my code works flawlessly. So in order to not overcrowd here, I won't post my code completely.

volatile unsigned int mode;

#pragma vector = port_vector_here
__interrupt void anotherNameHere (void) {
    __delay_cycles(1000);
    mode++;
    P1IFG &= ~BIT1;
}

#pragma vector = timer_vector_here
__interrupt void someNameHere (void) {
    P1OUT ^= BIT0;
    TA0CTL &= ~TAIFG;
}

int main(void) {

    /* Port settings, P1.0 to output, P1.1 to input etc.
     * Port Interrupt settings, IES, IRQ etc.
     * Timer settings, TA0CTL, SMCLK clock etc.
     * The configured as it should be, there is no problem.*/
    
    mode = 0;

    while (1) {

        if (mode > 2) {
            mode = 0;
        }

        switch (mode) {
           case 0: P1OUT &= ~BIT0;
                   break;
           case 1: P1OUT |= BIT0;
                   break;

           // Case 2 is the problematic part
           case 2: TA0CTL = TASSEL_2 + MC_1 + TACLR + TAIE;
                   TA0CCR0 = 50000;
                   TA0CTL &= ~TAIFG;
                   while(1) {}
           // Program stucks here (in while loop). When I debug it on CCS, I can see that a press on the button triggers the port interrupt but program returns to this line after completing the ISR. So it can't go out of Case 2.
                   break;
        }
    }
}

As I said in the comment part, the program stucks at the while loop that triggers the timer. As far as I know, I have to use an infinite loop to trigger a timer interrupt but I don't know how to skip over that infinite loop after triggering and executing commands in the Timer ISR.

How can I do this? How can I skip the infinite loop over to return to the initial position?

\$\endgroup\$
9
  • \$\begingroup\$ use mode 2 to set up timer and switch to mode 3 ... is anotherNameHere really a good name for a button press ISR? \$\endgroup\$
    – jsotola
    Commented May 16, 2021 at 2:19
  • \$\begingroup\$ Instead of while (1) {} do while (mode == 2) {} instead. \$\endgroup\$
    – brhans
    Commented May 16, 2021 at 2:53
  • 1
    \$\begingroup\$ First rule of interrupt handlers is keep them short : no delay(n), no long or infinite loops. Instead, make the int handler signal for the main loop to do the slow work (which can then be interrupted) \$\endgroup\$
    – user16324
    Commented May 16, 2021 at 11:47
  • \$\begingroup\$ @brhans yes I tried that but it doesn't work stable with that. For example, when the LED is blinking I press the button and the LED turns off, I press the button again the LED starts to blink instead of just turning on. \$\endgroup\$
    – Efe
    Commented May 16, 2021 at 17:09
  • \$\begingroup\$ @jsotola as I said, this is not the exact code of my program, just the important part. Does the name of an ISR matter for my problem? No. So there is no need to be a trainspotter :) So to get to the main point, what should I write in mode 3 to trigger timer? Unfortunately I don't any other way to trigger an interrupt other than run an infinite loop. That's my main problem too :) \$\endgroup\$
    – Efe
    Commented May 16, 2021 at 17:15

2 Answers 2

1
\$\begingroup\$

Your microcontroller does exactly what you program it to do. You programmed it to have a tight empty infinite loop, and as the name suggests, an interrupt routine just interrupts the execution, before returning where it left off.

So, this works as designed! If you don't want a while-loop without condition, then don't use one. Instead, you could check whether you're still in state 2.

However, this looks like your state 2 should actually be two different states: the TA0-configuring state, and an "idle" waiting state. After you converted that state to two states, you can just eliminate the inner while-loop, and just iterate through your state machine. Pseudo-code:

Isr_a():
   Change_state;

Isr_b():
   Change_state;

Main():
    Endlessly:
        Case state 0:
            Do this;
        Case state 1:
            Do that;
        Case state 2:
             Setup TA0;
             Change state to 3;
        All other cases:
             (Nothing)
```
\$\endgroup\$
2
  • \$\begingroup\$ Sorry but didn't understand what you mean by "change state to 3". What should I do in Case 3? \$\endgroup\$
    – Efe
    Commented May 16, 2021 at 17:05
  • \$\begingroup\$ Nothing! In state 3, you do nothing. Then you go back and check which state you're in. \$\endgroup\$
    – mmmm
    Commented May 16, 2021 at 23:23
0
\$\begingroup\$

If you use interrupts (which you should), use them all the way.

In your situation, you don’t have anything to do unless an event occurs, and whatever you need to do is very simple and very short:

  • if a button is pressed, you change mode, and you potentially switch on or off
  • if a timer event occurs and you are in the blink mode, you toggle the light

So your main function should just set up the interrupts (both of them), and then... do nothing. Not sure exactly how this is done on an MSP430, but there’s usually some kind of SLEEP or IDLE instruction which just idles the processor waiting for interrupts. An interrupt may exit that sleep mode, so you probably need to loop around that. But other than sleeping, you don’t do anything there.

Then, in the button interrupt handler:

  • increment mode
  • if mode is 3, back to 0
  • if new mode is 0, switch off LED
  • if new mode is 1, switch on LED
  • if new mode is 2, you probably won’t do anything

In your timer handler (if the timer interval is your blink interval):

  • if mode is 0 or 1, do nothing
  • if mode is 2, toggle LED

And that should be it!

You may need to denounce the button interrupt, but then again this can be done in the ISR. This would probably require another timer just keeping track of time to know how long you should ignore another press, but that’s it. Do NOT sleep or delay on an interrupt.

Note that things could be different if you had a lot of processing to perform upon one of those events, but that’s definitely not the case here.

\$\endgroup\$
2
  • \$\begingroup\$ So you say, I have to write the commands that I wrote to change the state of the LED in the port and timer interrupt handlers instead of the main function. If I am right, that didn't work. The blink process is just pain in the neck. \$\endgroup\$
    – Efe
    Commented May 16, 2021 at 17:20
  • \$\begingroup\$ @Efe without seeing your code it’s difficult to know why it doesn’t work. But it’s just a very simple state machine, it should be just a few lines if code. \$\endgroup\$
    – jcaron
    Commented May 16, 2021 at 20:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.