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 enter and exit from LPM4 inside comparator_D interruption on MSP430fr5739?

I'm trying to enter in LPM4 inside a Comparator_D interruption but when the systems enters in LPM4_bits, never exits. This is my code:

#include <msp430.h>

unsigned int l=0;

int main(void)

{

WDTCTL = WDTPW + WDTHOLD; // Stop WDT

PJDIR |= BIT0; // P1.0/LED output direction

// compare input

P1SEL0 |= BIT1; // P1.1/CD1

P1SEL1 |= BIT1;

// Setup ComparatorD

CDCTL0 |= CDIPEN + CDIPSEL_1; // Enable V+, input channel CD1

CDCTL2 |= CDRSEL; // VREF is applied to -terminal

CDCTL2 |= CDRS_3+CDREFL_1; // R-ladder off; bandgap ref voltage (1.2V)

// supplied ref amplifier to get Vcref=1.5V (CDREFL_2)

CDCTL3 |= BIT1; // Input Buffer Disable @P1.1/CD1

__delay_cycles(400); // delay for the reference to settle

CDINT &= ~(CDIFG + CDIIFG); // Clear any errant interrupts

CDINT |= CDIE; // Enable CompB Interrupt on rising edge of CDIFG (CDIES=0)

CDCTL1 |= CDON; // Turn On ComparatorD

__bis_SR_register(GIE);

 __no_operation(); // For debug

}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)

#pragma vector=COMP_D_VECTOR

__interrupt void Comp_D_ISR (void)

#elif defined(__GNUC__)

void __attribute__ ((interrupt(COMP_D_VECTOR))) Comp_D_ISR (void)

#else

#error Compiler not supported!

#endif

{

l++;

CDCTL1 ^= CDIES; // Toggles interrupt edge

CDINT &= ~(CDIFG + CDIIFG);

if (l%2==0)

{

CDINT|=CDIE;

__bis_SR_register(LPM4_bits);

}

__bic_SR_register_on_exit(LPM4_bits);

PJOUT ^= 0x01; // Toggle P1.0

P3DIR |= BIT5;

P3SEL1 |= BIT5;

P3SEL0 |= BIT5;

//CDCTL1 ^= CDIES; // Toggles interrupt edge

CDINT &= ~CDIFG; // Clear Interrupt flag

}

  • Inside an interrupt handler, GIE is automatically cleared to prevent the function from being interrupted by another or the same interrupt.

    When you go into LPM inside an interrupt handler, the cleared GIE prevents any interrupts that could get the CPU out of LPM from being executed.

    When you want to go to sleep, you should do so after the interrupt handler has returned. This is what __bis_SR_register_on_exit() is for.

    However, the typical pattern is that the main loop goes to sleep when it has nothing to do, and that interrupt handlers wake up the main loop with __bic_SR_register_on_exit() when they need to.

    BTW: You forgot the main loop; execution falls out of the end of main().

  • Thanks for your answer. In that case, is possible to enter and exit from LPM4 inside the comparator? I'm trying to do it with the following code but is not working:

    #include <msp430.h>
    unsigned int l=0;
    int main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT
    PJDIR |= BIT0; // P1.0/LED output direction
    // compare input
    P1SEL0 |= BIT1; // P1.1/CD1
    P1SEL1 |= BIT1;

    // Setup ComparatorB
    CDCTL0 |= CDIPEN + CDIPSEL_1; // Enable V+, input channel CD1
    CDCTL2 |= CDRSEL; // VREF is applied to -terminal
    CDCTL2 |= CDRS_3+CDREFL_1; // R-ladder off; bandgap ref voltage (1.2V)
    // supplied ref amplifier to get Vcref=1.5V (CDREFL_2)
    CDCTL3 |= BIT1; // Input Buffer Disable @P1.1/CD1

    __delay_cycles(400); // delay for the reference to settle

    CDINT &= ~(CDIFG + CDIIFG); // Clear any errant interrupts
    CDINT |= CDIE; // Enable CompB Interrupt on rising edge of CDIFG (CDIES=0)
    CDCTL1 |= CDON; // Turn On ComparatorB
    __bis_SR_register(GIE);
    while(1)
    {
    if (l==100)
    l=0;
    l++;
    }
    __no_operation(); // For debug
    }

    // Comp_d ISR - LED Toggle
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=COMP_D_VECTOR
    __interrupt void Comp_D_ISR (void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(COMP_D_VECTOR))) Comp_D_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    //l++;
    CDCTL1 ^= CDIES; // Toggles interrupt edge
    // __bis_SR_register(GIE);
    if (l%2==0)
    {
    __bis_SR_register(LPM4_bits);

    }
    //__bic_SR_register_on_exit(LPM4_bits);
    PJOUT ^= 0x01; // Toggle P1.0
    P3DIR |= BIT5;
    P3SEL1 |= BIT5;
    P3SEL0 |= BIT5;
    CDINT &= ~(CDIFG + CDIIFG);
    CDINT &= ~CDIFG; // Clear Interrupt flag
    }
  • Hi Alberto,

    Clemens was trying to tell you that you should not use __bis_SR_register(LPM4_bits) inside of your ISR - that is what is causing your problem. Instead, you would use __bis_SR_register_on_exit(LPM4_bits + GIE) so that the LPM4 is only entered after you exit the ISR, so that interrupts are enabled. You also don't want to delay/wait inside of an ISR because ISRs are blocking, preventing your other code from executing.

    Can you explain more the purpose of your code - why are you trying to go to LPM4 from inside the ISR? To me, it looks like you are trying to capture some rising + falling edge of a pulse, and maybe the LPM4 is just a delay to save power until you receive the second edge? If this is the case, it would be better to exit the ISR during the pulse, after you've changed the edge selection. You can use an if-statement with a software flag to keep track of which edge you are waiting for, or check CDIES - and use this to select how to handle the rest of the comp ISR based on this.

    Have you see our MSP430 coding techniques app note: www.ti.com/.../slaa294 This includes chapter 2 Top-Level Code Flow which has a discussion of a typical practices for ISRs and interrupt-driven coding. It might be helpful.

    Regards,
    Katie
  • Hi, thanks for your answer.  What I'm trying to do is when the Vcomp is smaller than Vref0, system should go to sleep and when Vcomp is bigger than Vref1, system should exit from LPM4. I'm programming the interruption as follows just to test what you told me before, but I don't know why the system is kept inside the interruption routine, I mean, when the system exits from LPM4, instead of going to the main loop, it goes to line "l2++" and starts again executing all inside the interruption :

    #pragma vector=COMP_D_VECTOR

    __interrupt void COMP_D_ISR(void)

    {

    l2++;

    //CDCTL1 ^= CDIES; // Toggles interrupt edge

    m=__get_SR_register();

    //CDINT &= ~(CDIFG + CDIIFG);

    PJOUT ^= 0x01; // Toggle P1.0

    P3DIR |= BIT5;

    P3SEL1 |= BIT5;

    P3SEL0 |= BIT5;

    __bis_SR_register_on_exit(LPM4_bits);

    }

  • Hi, thanks for your answer.  What I'm trying to do is when the Vcomp is smaller than Vref0, system should go to sleep and when Vcomp is bigger than Vref1, system should exit from LPM4. I'm programming the interruption as follows just to test what you told me before, but I don't know why the system is kept inside the interruption routine, I mean, when the system exits from LPM4, instead of going to the main loop, it goes to line "l2++" and starts again executing all inside the interruption :

    #pragma vector=COMP_D_VECTOR

    __interrupt void COMP_D_ISR(void)

    {

    l2++;

    //CDCTL1 ^= CDIES; // Toggles interrupt edge

    m=__get_SR_register();

    //CDINT &= ~(CDIFG + CDIIFG);

    PJOUT ^= 0x01; // Toggle P1.0

    P3DIR |= BIT5;

    P3SEL1 |= BIT5;

    P3SEL0 |= BIT5;

    __bis_SR_register_on_exit(LPM4_bits);

    }

  • Alberto,

    You are not clearing the interrupt flag,  so I think you are immediately re-entering the ISR because the flag is still set.

    Does your code need to be:

    • running in main normally until the input drops below the reference,
    • at that point go to LPM,
    • and then stay in LPM until the comparator input rises back above the reference -
    • at this point go back to running in main again?

    Is that the code flow you are looking for?

    This pseudocode shows the kind of code flow I am thinking of if that is the case:

    main()
    {
        init code
        set comparator to interrupt if Vcomp < vref0 
        variable interrupt_edge = 0; //software flag, 0 = falling edge, 1 = rising edge
        enable interrupts
        while(1)
        {
            other code
        }
    }
    
    comp_ISR()
    {
        if(interrupt_edge == 0) //falling edge
        {
            //falling edge handling
            change CDIES to interrupt on rising edge next time
            interrupt_edge = 1; //software flag to indicate we are waiting for rising edge next time
            __bis_SR_register_on_exit(LPM4_bits + GIE); //after exiting ISR, go to LPM4 and wait for the next comp interrupt, rising edge
        }
        else
        {
            //rising edge handling
            change CDIES to interrupt on falling edge next time
            interrupt_edge = 0; //software flag to indicate we are waiting for falling edge next time
            __bic_SR_register_on_exit(LPM4_bits); //wake part up and go back to main when exiting ISR this time
        }
        clear comparator interrupt flag
    }

    Please note - __bic_SR_register_on_exit clears the specified bits - it causes you to wake and return to normal code (if no pending interrupts) on ISR exit. Whereas __bis_SR_register_on_exit sets the specified bits - it causes you to go to sleep mode (if no pending interrupts) on ISR exit.

    Does this help show what we are talking about?

    Regards,

    Katie

  • HI, Thanks for your answer.

    Yes, what you wrote is what I want to implement. I followed your pseudocode but when the system enters in LPM4, never exits. I added my code:

    #include <msp430.h>

    unsigned int int_edge=0;

    unsigned int l=0;

    int main(void)

    {

    WDTCTL = WDTPW + WDTHOLD; // Stop WDT

    int_edge=0;

    // compare input

    P1SEL0 |= BIT1; // P1.1/CD1

    P1SEL1 |= BIT1;

    // Compare output

    P3DIR |= BIT5;

    P3SEL1 |= BIT5;

    P3SEL0 |= BIT5;

    // Setup Comparatord

    CDCTL0 |= CDIPEN + CDIPSEL_1; // Enable V+, input channel CD0

    CDCTL2 |= CDRSEL; // VRef is applied to -terminal

    //CDCTL2 |= CDRS_1+CDREF13; // VREF1 is Vcc*1/4

    //CDCTL2 |= CDREF04+CDREF03; // VREF0 is Vcc*3/4

    CDCTL2 |= CDRS_3+CDREFL_1; // R-ladder off; bandgap ref voltage (1.5V)

    /*CDCTL2|=CDRS_1;

    CDCTL2 |= CDREF14+CDREF13; // VREF1 is Vcc*3/4

    CDCTL2 |= CDREF03+CDREF02;; // VREF0 is Vcc*1/4*/

    CDCTL3 |= BIT1; // Input Buffer Disable @P1.1/Cd1

    CDCTL1|=CDIES;

    __delay_cycles(400); // delay for the reference to settle

    CDINT |= CDIE; // Enable CompB Interrupt on rising edge of CDIFG (CDIES=0)

    CDINT &= ~(CDIFG + CDIIFG); // Clear any errant interrupts

    CDCTL1 |= CDON; // Turn On Comparatord

    __bis_SR_register(GIE);

    while(1)

    {

    if (l==40)

    l=0;

    l++;

    }

    }

    #pragma vector=COMP_D_VECTOR

    __interrupt void COMP_D_ISR(void)

    {

    // Compare output

    if (int_edge==0)

    {

    //falling edge handling

    CDCTL1 ^= CDIES; // Toggles interrupt edge

    int_edge=1;

    __bis_SR_register_on_exit(LPM4_bits + GIE); //after exiting ISR, go to LPM4 and wait for the next comp interrupt, rising edge

    }

    else

    {

    //rising edge handling

    CDCTL1 ^= CDIES; // Toggles interrupt edge.

    int_edge=0;

    __bic_SR_register_on_exit(LPM4_bits); //wake part up and go back to main when exiting ISR this time

    }

    CDINT &= ~(CDIE);

    }

  • I only took a quick look, but this line is not good:
    CDINT &= ~(CDIE);
    That will disable further interrupts!
    You need to clear the interrupt flag, not the interrupt enable, like this:
    CDINT &= ~CDIFG;
  • Hi,

    Yeah, it was my error. Thank you very much for your help. It seems it's working now. :)
  • So glad to hear this got you up and running! :-)

**Attention** This is a public forum