Jump to content
43oh

Recommended Posts

All,

 

Here is me again struggling with the timer.

 

What I am trying to do is count to CCR0, passing through CCR1, generating interrupts when timer overflows and when it reaches CCR1 value as well.

 

From TI examples I had understood I should set up something like this, adding code to the appropriate "switch" cases:

#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A(void)
{
 switch( TAIV )
 {
   case  2: break;                          // CCR1 not used
   case  4: break;                          // CCR2 not used
   case 10: P1OUT ^= 0x01;                  // overflow
            break;
 }
}

Well, it is getting to the ISR no problem, but at all times TAIV = 0x000A (10). Even then, the code in the "case 10" statement is not executed. Also, it seems like the interrupt is not being called when the counter reaches CCR1.

 

Here is the full code. I have commented out my original code in the ISR (which was supposed to toggle the led in P1.0 when counter reached CCR1, and P1.6 when timer overflows). I tried both hexa and decimal numbers in the "case" statements.

The part not commented is copied straight from TI's example (msp430x20x3_ta_03, see see slac163 and slaa428) without any changes.

 

Am I doing something fundamentally wrong? Is my intent to do something when timer reaches both CCR0 and CCR1 possible at all?

#include <msp430.h>
#include <stdlib.h>

int main( void )
{
  //---------------- //
  // Hardware config //
  //---------------- //
  
  WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
  
  // DCO configuration
  DCOCTL = CALDCO_1MHZ; //Set DCO to 1 MHz
  BCSCTL1 = CALBC1_1MHZ; // Calibrate internal clock for 1 MHz
  
  // Port 1 configuration
  P1DIR |= BIT0 + BIT2 + BIT6;
  P1SEL |= BIT2;
  P1OUT = 0x00;
  
  // Timer A configuration
  //CCTL0 = CCIE; // Enable interrupts
  CCTL1 = CCIE; // Enable interrupts
  TACTL =  TASSEL_2 + // Set clock to internal DCO
           ID_0 +     // No frequency divider
           TAIE +     // Interrupts
           MC_0;      // Stopped to begin with
  CCR0 = 1000-1;  // PWM Period (1 us)
  CCTL1 = OUTMOD_7; // CCR1 reset/set
  CCR1 = 500; // CCR1 PWM duty cycle
  
  //----------- //  
  // Main logic //
  //----------- //
  TACTL  |= MC_1;
  
  _BIS_SR(GIE + LPM0_bits);
}

//#pragma vector = TIMERA0_VECTOR
//__interrupt void Timer_A0 (void)
//{
//  P1OUT ^= BIT6;
//}

#pragma vector = TIMERA1_VECTOR
__interrupt void Timer_A1 (void)
{
  switch( TAIV )
 {
   case  2: break;                          // CCR1 not used
   case  4: break;                          // CCR2 not used
   case 10: P1OUT ^= 0x01;                  // overflow
            break;
 }
//  P1OUT ^= BIT0;
//  switch (TAIV)
//  {
//  case 0x02: // CCR1
//    {
//      P1OUT ^= BIT0;
//    } break;
//  case 0x04: // CCR2
//    {
//      //
//    } break;
//  case 0x0A:  // CCR0
//    {
//      P1OUT ^= BIT6;
//    } break;
//  }
//  return;
}

P.S.: I am using IAR and the G2231 chip if that makes any difference.

Link to post
Share on other sites

It's a very subtle error you made,

during setup you set CCTL1 = CCIE to enable your interrupt,

then a few lines later you set the output mode like CCTL1 = OUTMOD_7

Alas, you're writing to the same register, hence overwriting the CCIE bit you set in it!

Your solution should be to use CCTL1 |= OUTMOD_7 to mask those bits in, instead of clearing all other bits.

Link to post
Share on other sites

Just to close the loop - @@roadrunner84 was spot on about my mistake - after fixing that the ISR was executed for all timer interrupts. However, the TAIV switch statement still didn't work, TAIV kept being reset so its value was always zero and neither of the case statements ever got executed.

 

I then found this article that explains that TAIV, being a read only register, is reset when read; the debugger was therefore resetting it before the switch statement had a chance do to anything useful. That's what I understood at least.

 

The fix was to use intrinsic __even_in_range. Here is the final ISR code for blinking the LP LED's, one every CCR0 overflow, the other every CCR1 overflow.

 

p.s.: here is some information on the intrinsic. The first parameter is the string, the second is the last case value in the range.

#pragma vector = TIMERA1_VECTOR
__interrupt void Timer_A1 (void)
{
  switch (__even_in_range(TAIV,10))
  {
  case 2: // CCR1
    {
      P1OUT |= BIT0;
      P1OUT &= ~BIT6;
    } break;
  case 4: // CCR2
    {
      //
    } break;
  case 10:  // CCR0
    {
      P1OUT &= ~BIT0;
      P1OUT |= BIT6;
    } break;
  }
}
Link to post
Share on other sites

In fact I could also use CCTL1 = CCIE + OUTMOD_7 in one line of code right?

Yes, that'd do the trick as well.

 

When using the __even_in_range intrinsic, make sure no odd (not-even) value or value above the set range could even occur! As you may have read, the __even_in_range intrinsic changes the if() else if() chain into an "add parameter to the program counter" and a jump look up table. Any value out of the set constraints will lead to undefined behaviour, and maybe even hang your process.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...