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.

MSP430F5529: How to exit from an interrupt's loop?

Part Number: MSP430F5529

Hi everyone. I'm new to MSP430 and microcontrollers. And right now I'm trying to write a program that changes the LED's state (OFF-ON-BLINK) by every press on P1.1 button. I have used "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. But there is a problem with the blink case.

The program starts with LED off. Then I press the button and LED turns on. Another press on the button and the LED starts to blink. After the blink case, when the button is pressed one more time, the program must return to the initial state, which turns the LED off. And one more press turns the LED on, one more starts to blink and so on.

But when the LED starts to blink, the program stuck at the infinite loop point where the timer get involved to toggle the LED state and a pressing button has no effect, the LED continues to blink.

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 = timer_vector_here
__interrupt void someNameHere (void) {
    P1OUT ^= BIT0;
    TA0CTL &= ~TAIFG;
}

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

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.*/
    
    __enable_interrupt();
    
    mode = 0;
    
    while (1) {
        
        if (mode > 2) {
            mode = 0;
        }
        
        switch (mode) {
            case 0: P1OUT &= ~BIT0;
                    break;
            case 1: P1OUT |= BIT0;
                    break;
            case 2: TA0CTL = TASSEL_2 + MC_1 + TACLR + TAIE;
                    TA0CCR0 = 50000;
                    TA0CTL &= ~TAIFG;
                    for(;;);
                    break;
        }
    }
}

What I wrote on the other parts is not important. What important thing is, what written in the case 2 and timer ISR. So to sum up, my question is, how can I exit that timer interrupt when I press the button to turn the LED off and continue to program?

P.S. I didn't use and not want to use LPM or something. So please don't give me examples that includes LPM. Thank you so much in advance Slight smile

  • While in each state, this program takes some action repeatedly. I think you actually want it to take that action once, when it enters that state, Cases 0 and 1 don't care since the particular actions are the same whether done once or repeatedly (idempotent), but case 2 should only be done once, when state 2 is entered.

    I suggest you check for transitions:

    1) Declare a variable "unsigned oldstate;" 

    2) As the first line in your while(1) loop: "if (state == oldstate) continue;" to skip a non-transition.

    3) As the last line in your while(1) loop: " oldstate = state;"  to commit to the new state.

    4) In state 0, add: "TA0CTL = 0; // Stop timer".

    ------------------------

    Unsolicited:

    a) These:

    >P1OUT &= BIT0;

    >P1OUT |= ~BIT0;

    don't do what you want. Try:

    >P1OUT &= ~BIT0;  // P1.0 LED off

    > P1OUT |=  BIT0;   // P1.0 LED on

    b) In your PORTn_VECTOR function, don't forget to clear the appropriate PnIFG bit or the ISR will keep triggering.

    c) If you're using a physical button, you'll probably need de-bouncing. A crude but fairly effective method would be to add as the first line in the ISR:

    >     __delay_cycles(1000); // 1ms to let the button stop bouncing

    [Edit: I forgot to mention stopping the timer.]

  • Hello Bruce, thanks for your help.

    Let me be more clear. I want each case to be run once before I press the button except Case 2. I want the process in case 2 to take forever until I press the button. So when the program runs Case 2 and if I never press the button one more time after that, the LED should blink forever.

    Unfortunately, I didn't understand the steps you gave for transitions. Can you please explain them much more?

    -----------------------

    I wrote unsolicited "a" and "b" in my code as you said, I just forgot to mention them. But I have a question about "c". Can I make the delay with the same timer that I use for Case 2?

    Regards,

    Efe

  • It appears I switched terminology ("mode"->"state") partway through. How about:

    ) Declare a variable "unsigned oldmode;" .

    2) As the first line in your while(1) loop, insert: "if (mode == oldmode) continue;" to skip a non-transition.

    3) As the last line in your while(1) loop, insert: " oldmode = mode;"  to commit to the new state.

    4) In 'case 0:', add: "TA0CTL = 0; // Stop timer".

    Based on your pseudo-code, I think these changes will get you going. 

    ------------------

    There are more sophisticated de-bouncing methods. Using TA0 might require a different strategy since I suspect you don't intend to run TA0 all the time.

    Since I can't guess what this program will develop into, I offered a very simple method to get you going. (google "ganssle debounce" if you want to see some other options.) You can also ignore switch bounce for now, but you'll eventually encounter it.

  • Hello again Bruce. I made the changes you suggested. Now the main function of my code looks like this:

    int main(void) {
        
        unsigned oldmode;
    
        /* Port settings, P1.0 to output, P1.1 to input etc.
         * Port Interrupt settings, IES, IRQ etc.
         * Timer settings, TA0CTL, SMCLK clock etc.*/
        
        __enable_interrupt();
        
        mode = 0;
        
        while (1) {
        
            if (mode == oldmode) {
                continue;
            }
            
            if (mode > 2) {
                mode = 0;
            }
            
            switch (mode) {
                case 0: P1OUT &= ~BIT0;
                        TA0CTL = 0;
                        break;
                case 1: P1OUT |= BIT0;
                        break;
                case 2: TA0CTL = TASSEL_2 + MC_1 + TACLR + TAIE;
                        TA0CCR0 = 50000;
                        TA0CTL &= ~TAIFG;
                        for(;;);
                        break;
            }
            oldmode = mode;
        }
    }

    But the problem continues to occur. I can't go out of case 2 and continue to Case 0. LED doesn't stop blinking. When I debug the code and go step-by-step, I see that the program stuck at "for(;;);" part. It should end the timer as soon as I press the button and turns the LED off.

    Regards,

    Efe

  • You should remove the "for(;;)". That was the goal of the other changes.

  • But when I remove that, the timer doesn't trigger. So the LED doesn't blink. 

  • It worked when I did it. (I did have to increase the debounce delay to 50000 to get consistent behavior.)

    I'm not sure what we're doing differently. Maybe something in the code you're not showing?

  • Okay, it's working right now. I don't know what I changed but somehow it started to work, a bit buggy though. Sometimes it goes back from Case 2 to Case 1, sometimes it jumps from Case 0 to Case 2. I don't understand what causes this problem but I'm working on to solve it. Now, I have a few more questions. I'll be so glad if you answer them too.

    First of all, what is the type of oldmode variable? We just wrote unsigned oldmode but unsigned of what? Char, int, long? Or does it change according to the value it's equalized?

    And question 2 is: when I debug the program and go step-by-step, I see that, at startup, if I don't press any button, the program is waiting in if (mode == oldmode) line. But as soon as I press the button, the Port Interrupt triggers and changes the mode variable, so Case 1 becomes active. Why does mode=oldmode when the button is pressed?

    Last but not least, what is the logic of this program. I mean, what triggers the Timer Interrupt? Why we equalized oldmode to mode in the last line of the while loop?

    Regards,

    Efe

  • The extra transitions result from switch bounce. Due to the physical properties of the button, when you push the button once, your program may see 2 (or 10 or 50) contacts, so it goes through that many mode transitions. As I mentioned above, Jack Ganssle wrote an excellent treatise on the topic.

    "unsigned" means "unsigned int".

    Initially your program is in mode 0, the previous mode was mode 0, and the initial conditions are consistent with mode 0 (specifically, the LED and timer are off). Conceptually, you could say that it transitioned to mode 0 just before the program started.

    The timer interrupt results from your setting TAIE when you started the timer. It will trigger repeatedly, every 50000 timer ticks (about 50ms), until you stop the timer when you transition to mode 0.

    You set oldmode=mode to indicate that the mode transition has been processed, so it won't be processed again ("edge sensitive").

    I encourage you to step through this program in your head to see what it is doing. These are idioms you will be seeing often.

  • Thank you so much. I learned so much from you.

    The timer interrupt results from your setting TAIE when you started the timer.

    What if I setup the timer in the main part and write TA0CTL = TAIE; in the Case 2? I mean, yes I can try on my MSP430 and see what happens but what I am asking is, the logic. Short of that, can I set I timer in the main part and use it everywhere?

    Regards,

    Efe

  • Yes you could run the timer all the time, and just turnTAIE on and off (but use "TA0CTL |= TAIE" and "TA0CTL &= ~TAIE"). When you set TAIE, TAIFG will most likely be set (stale) so the first blink cycle will be short. In this program, a human won't be able to see a difference, but in a different application it might matter.

**Attention** This is a public forum