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.

MSP430G2553 Comparator Interrupt Handler error - Interrupt method gets executed TWICE for every one interrupt

Other Parts Discussed in Thread: MSP430G2553

I am using the Launchpad kit with the MSP430G2553 on CCS5.

I am using the Comparator_A+ block to monitor when an analog signal crosses a threshold. After much trouble, I realized that the comparator is acting strangely.

First, my setup.

I use GRACE to set up everything. My main clock is 1MHz, the auxiliary is at 12KHz; I don't have the external timing crystal soldered on the Launchpad, and the clocks are all internal. I set up all the unused GPIO pins to outputs in GRACE and pull them down to 0 in my main.c.

For the comparator specifically, I set it up completely with GRACE: pin P1.0 is the + input to the comparator and 0.5xVCC is the - input. The inputs are not shorted nor inverted. I output the comparator signal directly to PIN1.7, so I can see the signal on an oscilloscope while the chip is running. I also enabled the CAF to clean up some of the noise. I also enabled comparator interrupts, and named the interrupt method comparatorInterruptHandler() to handle the interrupts; the interrupt triggers on a rising edge. (I do not change the CAIES=0 bit after the initial GRACE setup, because I can see from the Comparator Interrupt System that toggling CAIES could falsely trigger a comparator interrupt.)

I have breakpoints inside the implemented comparatorInterruptHandler() method to monitor what it does, and it does something very strange, as I will describe. To keep track of the strangeness, I have defined a global variable in my main.c to keep count of the number of times the interrupt is handled:

     volatile long comparatorInterruptCounter=0; //keeps count of number of times comparator interrupt handler method runs

My only other modification to main.c is to add an infinite loop at the end of the main function so the chip doesn't shut down:

     while(1){

          //loop until comparator interrupt

     }

Here is my full comparatorInterruptHandler:

     void comparatorInterruptHandler(void){

          CACTL1 &= ~(CAON + CAIE + CAIFG); //turn off comparator, the comparator interrupt, and clear the interrupt flag

          comparatorInterruptCounter +=1;          //increment counter

          //__delay_cycles(10000);                     //only way this interrupt works correctly

          CACTL1 |= (CAON + CAIE);                  //turn comparator back on, reenable interrupt

     } //there is a breakpoint here, so I can monitor the value of comparatorInterruptCounter and of the CACTL1 register bits

Here is the strange behavior I observed:

1) a secondary problem is that the interrupt triggers whether the input signal is sweeping up or down, even though my CAIES bit is set only for up. I am willing to dismiss this as due to large noise on the input signal because I am using a potentiometer to sweep an input voltage by hand. Although I don't like this behavior, I can at least understand that noise might cause it, since the comparator doesn't seem to have any hysteresis.

If anyone knows a solution to this, I am interested, but it is not my main concern.

2) the MAIN problem is that the interrupt triggers TWICE on a positive voltage sweep. When the program is running inside the infinite while loop, I sweep the P1.0 input from 0V toward positive, and at the right time the comparator interrupt triggers correctly and enters the comparatorInterruptHandler method. After the first line, I can see in the CACTL1 register that the CAON, CAIE, and CAIFG bits are correctly cleared. Stepping through, I can also see the comparatorInterruptCounter counting up +1. However, when I am ready to step into "CACTL1 |= (CAON + CAIE);" the CAIFG bit is reset to "1" again, even though I just cleared it.

The comparatorInterruptHandler method exits, but then starts over again, I assume because the CAIFG is back at 1 for some reason. So the method executes again, the comparatorInterruptCounter counts up +1 again, but this second time around the CAIFG bit correctly remains at "0" after I clear it, and the method correctly exits all the way to the main while(1) loop. Fortunately, this behavior, although wrong, seems entirely deterministic, as it executes the method 2X, every time, and never for example executes it 3 or 4 times, nor does it ever just execute it just once.

In conclusion, every time the comparator sees a positive edge, it triggers the interrupt handler, which runs TWICE instead of just ONCE. I have not been able to figure out why. Please help.

3) The strangest part is that I should not even have to worry about setting the CAIFG bit to 0 because it's supposed to be automatically at 0 after the interrupt is handled. I am worried that whatever system should be setting it to 0 is maybe setting it to 1 instead? Something is clearly wrong.

Further, having looked though the logic blocks for the Comparator_A+ Interrupt System, it looks like the only way to Reset the interrupt bit(other than the global POR signal) is to send an IRACC (Interrupt Request Accepted) to the flip flop that manages the comparator interrupt requests.

Is there a way for ME to send an IRACC manually after I'm done with the comparator, since the current system fails at the moment? I assume IRACC should be sent automatically after the comparatorInterruptHandler() method completes.

4) Because I know that toggling the CAON bit can cause a false interrupt, I even added a "CACLT1 &= ~CAIFG;" line AFTER the last line that turns the comparator back on, but when the method reaches the final breakpoint, the CAIFG value has been magically reset to "1" again

CACTL1 |= (CAON + CAIE);                  //turn comparator back on, reenable interrupt

CACTL &= ~CAIFG;                             //reclear the CAIFG just to be sure

} //breakpoint here, the CAIFG is back to "1" even though I just set it to "0" twice.

Final note: if, in debug mode, I step through the comparatorInterruptHandler method line by line, I CANNOT replicate this error. Nor does it happen on a downward transition.

However, if I only have a breakpoint at the END of the method, so all he instructions happen in real time, the error happens every time on a positive transition, and by the time the program returns to the infinite while(1) loop, the counter had gone up +2, showing the interrupt handler method executed twice. So I thought this might be a timing issue because the comparator is inherently asynchronous. That's why I added a "__delay_cycles(10000)" line before the end to test this out.

Unfortunately, the error only disappears if I delay more than 10,000 cycles, which is entirely unacceptable; I would have been ok with waiting a few cycles to let the signal propagate, but I CANNOT wait 10,000 cycles!

  • I think you already found the answer but didn't notice:

    Dan Stiurca said:
    the interrupt triggers whether the input signal is sweeping up or down, even though my CAIES bit is set only for up. I am willing to dismiss this as due to large noise on the input signal because I am using a potentiometer to sweep an input voltage by hand.

    Using a mechanical potentiometer usually cause large noise (unless you use one of the very expensive plastic resistor, oil-filled pots for high-end audio devices). The wiper sratches the surface and even jumps.

    As a result, you'll see the voltage jumping up and down while you move the wiper. Short spikes only, but enough to trigger the comparator..

    The same happens when you do your sweep: you get an interrupt for the rising edge. ut while the CPU is starting the ISR (and clears the IFG bit), voltage jumps down and up again and triggers another interrupt (IF gbit gets set again). And possibly some more time (which you won't notivce because the IFG bit is already set). Now you manually clear IFG in your ISR, but there is another jump and IFG is set another time (or more). Eventually, teh spikes leave the trigger range and you don#t see any more interrupts.

    When you single-stepping through the ISR, the spikes have settled between two single steps. However, you may still see IFG set insidfe teh ISR as there were some spikes after the interrutp was triggered (and IFG was cleared) and before the breakpoint was hit.

    If you don't want to make a delay ( abzusy-waiting delay is always a bad idea, btu a death sin when done inside an ISR) you can to two things:

    Software workaround is to disable comparator interrupt in the comparator ISR and start a timer. When teh tiemr expires, the tiemr ISR clears any pending comparator interrups and enables the comparator interrutp again.

    Hardware workaround: applay an R/C filter (low-pass) to the input. This cancels high-frequency noise and the ocmparator will then only trigger once. However, the filter constant enlarges comparator reaction time. It also limits the maximum event frequency, but a delay - busy-waiting or timer - would do the same.

  • Thank you for your reply in regards to the comparator input noise.

    Because I know my eventual real signal will be periodic, I plan to have the comparator completely off after it triggers an interrupt, then wake it back up with a timer after I know the input signal has lowered enough that it wouldn't trigger the comparator. So I am definitely planning on doing that, and I know __delay_cycles() is a terrible thing to do--I was just using it here because I wanted to make sure my error is in the comparator, not in the timer module.

    However, that does not fix the main issue:

    After the compartorInterruptHandler() method is complete, the CAIFG bit remains at "1" and is not automatically cleared. This triggers the second comparator interrupt event that causes problems for my project. Since my post, I have simplified my test code to the bare minimum, to show this problem:

    void main(void){

          CSL_init();

         while(1){

              if(CACTL1 & CAIFG){//the comparator interrupt flag has not been reset to "0" automatically

                   P1OUT |= BIT6;   //Breakpoint 1 here

              }

               else{ //the comparator interrupt flag is at "0" as it should be

                     P1OUT &= ~BIT6;//Breakpoint 2 here

                }

         }

    }

    void comparatorInterruptHandler(void){

         //disable comparator and comparator interrupts

         CACTL1 &= ~(CAON + CAIE);

    }

    //at the end of this method, the comparator should be clearing the CAIFG automatically

    Every time I run this code and trigger an interrupt, the program breaks at Breakpoint 1, indicating CAIFG ==1. I can also see in the Registers View that CAIFG=1 during the breakpoint, but it was "0" before I triggered the interrupt. Why does it not clear CAIFG to "0" after the interrupt is handled, as the MSP430 manual explicitly says it should?

    Furthermore, if I use CACLT1 &= ~(CAON + CAIE + CAIFG));

    to clear the interrupt flag manually, why does the CAIFG return to "1" by the time it gets to the breakpoints? There is something setting CAIFG at "1" even after the comparator module has been turned off.

  • Dan Stiurca said:
    After the compartorInterruptHandler() method is complete, the CAIFG bit remains at "1" and is not automatically cleared.

    As I said, it is the same reason: Once the itnerrupt is accepted and the processor reads the interrupt vector address, the IFG bit is cleared by hardware. But immediately after, another interrupt and another one is triggered by the noise, so the IFG bit is raised again and your ISR code sees it set. Not because it wasn't cleared, btu because since triggering the curent interupt, another raising edge was detected. This is actually an interrupt overrun indicator. If you generate clean edges, you won't see this happen. Of course your real-world applicaiton might not provide clean edges, so you'll have to deal with it in software. But it isn't a bug, it is an expected behaviour. Or rahter: it is a behaviour that had to be expected. You just didn't :)

**Attention** This is a public forum