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.
Hello and thanks for looking. I am trying to implement a button press release that is tied to a second PWM pin using the WDT to debounce the button. The result should be: Btn down, pwm on; Btn up pwm off. This system must also capture the timing of the button state changes to get an accurate determination of button up/down time (capture of TA1CCR2 to a variable is not shown in this demo code). In trying to achieve this, I have several questions but the biggest problem with the current set up is that the relationship between the button state and the pwm behavior reverses after multiple button presses. At first, the system works as described but after several button press/release cycles, I the pwm turns on when the button is released, then switches back after more, etc.
I have read good discussions on timing of the raising and clearing of the CCIE flag in this forum, but my application doesn't seem to require highly accurate counts as in those examples. If I can get timing accurate to within a 10 - 40 ms range that would be fine. I see button bounce on a scope that is well within the WDT 'time out' for debouncing, so that appears to be working well.
So, my question is how to implement this relatively simple scenario. I am using the MSP430G2553 Timer1_A3 for capture on pin P2_5 attached to the button and Timer0_A3 on pin P2_6 for the pwm output. Related questions are...
Can I use PxREN to set up internal pullup (datasheet says this is for pin when configured as I/O)? I have an external pullup working now.
Do I need to stop the timer in order to switch the edge that triggers an interrupt (TA1CTL)?
Do I have to reset COV in TA1CCTL2?
Is it permissible to test the state of the pin with PxIN & BTN_KEY? As currently used, this does not seem to work as expected.
Finally, while debugging this I noticed that the debugger reports that the CMx bit of TA1CCTL2 is CM_3 when I am toggling between CM_1 and CM_2 even though I can see that I am correctly toggling bit CCIE. Why might that be?
Thanks for any thoughts and here is my code...
#include <msp430.h> #define BTN_KEY BIT5 //attach key to P2_5 TA1.2 #define BUZZER BIT6 // Buzzer -> P2_6 void PortsInit(void); void InitBuzzer(void); volatile char edge = 0; //used to toggle CCR2 edge detect int main(void) { WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer PortsInit(); P2DIR &= ~BTN_KEY; //Set Btn as input P2SEL = BTN_KEY; //Set Btn for Timer1_A CCI1A capture InitBuzzer(); //Set up Timer1A TA1CTL = MC_2 | ID_3 | TASSEL_2 | TACLR;// | TAIE; // capture on falling edge, CCI1A input, synchronous cap, capture mode, ints enabled TA1CCTL2 = CM_2 | CCIS_1 | SCS | CAP | CCIE; __enable_interrupt(); for ( ; ; ){ // do nothing } return 0; } #pragma vector = WDT_VECTOR __interrupt void WDT_ISR(void){ // use to debounce falling and rising IE1 &= ~WDTIE; // disable WDT interrupt IFG1 &= ~WDTIFG; // clear flag WDTCTL = WDTPW + WDTHOLD; // put WDT back in hold state TA1CCTL2 &= ~CCIFG; // clear the CCRO flag TA1CCTL2 &= ~COV; // reset COV bit if it was set TA1CCTL2 |= CCIE; //enable interrupts } #pragma vector = TIMER1_A1_VECTOR __interrupt void TIMER1_A1_ISR (void){ switch (__even_in_range(TA1IV, 10)){ case 0: break; case TA1IV_TACCR2: //grab value of TA1CCR2 here TA1CCTL2 &= ~CCIE; // disable further CCIE interrupts TA1CTL |= MC_0; // stop timer if((P2IN & BTN_KEY) == 0){ // should be here at beginning of down pulse edge = 1; TA0CTL |= MC_1; // start tone P2SEL |= BUZZER; TA1CCTL2 |= CM_1; // trigger on rising edge }else{ // Should be here after down pulse finished edge = 0; TA0CTL |= MC_0; // end tone P2SEL &= ~BUZZER; TA1CCTL2 |= CM_2; // trigger on falling edge, enable interrupts } TA1CTL |= MC_2; // restart timer IE1 |= WDTIE; // enable watchdog interrupt WDTCTL = WDT_MDLY_32; // start and set WDT (watch dog timer) 32ms interval break; case TA1IV_TAIFG: break; default: for (;;){ //Should not be possible } } } void PortsInit(void){ P1OUT = 0; P1DIR = 0xFF; P2OUT = 0; P2DIR = 0xFF; P3OUT = 0; P3DIR = 0xFF; } void InitBuzzer(void){ P2DIR |= BUZZER; TA0CCR0 = 0x8ec; // period TA0CCR1 = 0x476; // period/2 TA0CTL = TACLR + TASSEL_2 + MC_1; // Timer -> SMCLK, Up Mode, Clear TA0CCTL1 |= OUTMOD_7; // Output mode for TA0.1 Reset/Set }
> TA1CTL |= MC_0; // stop timer
This doesn't stop the timer, since MC_0==0. Try:
> TA1CTL &= ~MC_3; // stop timer
------------------------
> TA1CCTL2 |= CM_1; // trigger on rising edge
Similarly, this doesn't set CM=1, it sets the low bit of CM. (After one round, CM=3 and it stays there.) Try instead:
> TA1CCTL2 = (TA1CCTL2 & ~CM_3) | CM_1; // trigger on rising edge
Alternatively, since (a) you're overall interested in both edges and (b) you're checking the actual state of the pin, you might consider using CM=3 after all.
------------------------
Checking the state of the pin is a good idea. Strategically, though, you should check it after the debouncing is done (in the WDT ISR), not immediately after you see the first transition.
Bounces happen very fast, and there's a non-small chance it will have transitioned twice before you look. Thus it's more reliable to look only later, after it has settled down.
------------------------
For your specific questions:
- If you have an external pullup, you don't need the REN resistor.
- If you're not interested in COV, you can ignore it completely. Checking the pin directly replaces most anything COV would tell you.
- You don't need to stop the timer to switch the edge (CM field).
-
Thank you for the quick and thorough reply. Your correction on setting the CM bits helps. Also, using both edges and checking in the WDT ISR works as needed. I see that it is okay to use the internal pullup resistor in this case and I've removed the external one I had.
I have a remaining question regarding setting the P2SEL bit for the BTN_KEY (for timer capture purposes). After setting P2OUT = 0 and P2DIR = 0xFF in the PortsInit(), I then set up the BTN_KEY with
P2DIR &= ~BTN_KEY; //Set Btn as input P2OUT |= BTN_KEY; // along with REN, set pullup P2REN |= BTN_KEY; //Pullup resistor P2SEL = BTN_KEY; //Set Btn for Timer1_A CCI1A capture
This works fine but if I were to set P2SEL |= BTN_KEY, that sets the P2_6 and P2_7 as well. I've looked at the datasheet for this processor family and see that there is some consideration of interactions between REN and SEL but that seems to be with a different chip. Is there a better way to set just this bit? I feel that this is in the compiler's users guide, so I'll dig in there to look for macros to define bits.
Thanks again and I'll mark this issue as resolved.
Regards,
Nick
That's a good question, which I overlooked. At Reset, P2SEL=0xC0 (bits 6/7 pre-set) to enable the 32kHz oscillator (XIN/XOUT). This is somewhat unusual, but very useful.
These two pins are interrelated. To do PWM on P2.6, (P2SEL.6) you need to also turn off P2SEL.7 and set P2.6 as an output, to shut down the oscillator. [Ref data sheet (SLAS735J) Table 21]. This is what the code you ended up with (using "P2SEL=") is doing, though you only need to do it once.
Those two pins (on the F2/G2 series) are special. For any other pins (on any other series) using "|=" would be the correct action.
That makes sense. I am using an old launchpad boosterpack mk1 which has the buzzer tied to P2_6 (XIN). I should have realized this pin was 'special.'
Thanks again,
Nick
**Attention** This is a public forum