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!