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.

I don't understand how the timer interrupts work in cc430f6137

Other Parts Discussed in Thread: CC430F6137

I have the following code:

  __disable_interrupt();
  //stop timer
  TA0CTL &= ~(MC0 | MC1);
  //set timer count register to 0
  TA0R = 0;
  // set aclk source, clear timer
  TA0CTL |= TASSEL0 + TACLR;// + TAIE;
  //divide by 8
  TA0CTL |= ID1 | ID0;

  //set capture/compare register to ...
  TA0CCR0 = 0x4000;
  // Enable cc0 interrupt
  TA0CCTL0 |= CCIE;

  //set capture/compare register to ...
  TA0CCR1 = 0x8000;
  // Enable cc0 interrupt
  TA0CCTL1 |= CCIE;

  //set capture/compare register to ...
  TA0CCR2 = 0xB000;
  // Enable cc0 interrupt
  TA0CCTL2 |= CCIE;

  //start timer
  TA0CTL |= MC1;
  __enable_interrupt();

And the following interrupt handlers that don't work as I expected:

#pragma vector=TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR(void) {
  count++;
  if (TA0CTL & TAIFG == TAIFG) {
    count++;
  }
}

#pragma vector=TIMER0_A1_VECTOR
__interrupt void TIMER0_A1_ISR(void) {
  count++;
  if (TA0CTL & TAIFG == TAIFG) {
    TA0CTL &= ~TAIFG;
  }
  if (TA0CCTL0 & CCIFG == CCIFG) {
    TA0CCTL0 &= ~CCIFG;
  }
  if (TA0CCTL1 & CCIFG == CCIFG) {
    TA0CCTL1 &= ~CCIFG;
  }
  if (TA0CCTL2 & CCIFG == CCIFG) {
    TA0CCTL2 &= ~CCIFG;
  }
}

The interrupt flag for TA0CCTL0 is never set to 1, only TA0CCTL1 / TA0CCTL2 interrupt flags are set when the  value set in TA0CCR1 / TA0CCR2 is reached by the counter. When TA0R reaches 0x4000 the TIMER0_A0_ISR is executed and no interrupt flag is set. Is this how it's supposed to work or am I doing something wrong. I also don't understand when should I use TIMER0_A0_VECTOR and when TIMER0_A1_VECTOR.


To be clear I want the following code to work as it should:

  if (TA0CCTL0 & CCIFG == CCIFG) {
    TA0CCTL0 &= ~CCIFG;
  }
  if (TA0CCTL1 & CCIFG == CCIFG) {
    TA0CCTL1 &= ~CCIFG;
  }
  if (TA0CCTL2 & CCIFG == CCIFG) {
    TA0CCTL2 &= ~CCIFG;
  }

At the moment I can only work with TA0CCTL1 and TA0CCTL2, for TA0CCTL0 the TIMER0_A0_ISR is executed instead without ANY interrupt flag being set.

  • TIMER0_A0_VECTOR is only triggered by TA0CCTL0.CCIFG, whereas TIMER0_A1_VECTOR is triggered by all other interrupt sources in TimerA0 (TA0CTL.TAIFG, TA0CCTL1.CCIFG, TA0CCTL2.CCIFG...).

    As TIMER0_A0_VECTOR has a single source the interrupt acceptance logic automatically clears TA0CCTL0.CCIFG on entry to the ISR.

    For a full explanation of TimerA interrupts on cc430f6137, see section 13.2.6 Timer_A Interrupts of the CC430 Family User's Guide.

  • Thanks, I've read that part of the guide a few times and I wasn't sure how it works. So in order to use both capture/compare and overflow interrupts I have to use the following code right?

    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void TIMER0_A0_ISR(void) {
      //this is executed ONLY when the timer reaches TA0CCR0
      count++;
    }
    
    #pragma vector=TIMER0_A1_VECTOR
    __interrupt void TIMER0_A1_ISR(void) {
      count++;
      //check for capture compare registers
      if (TA0CCTL1 & CCIFG == CCIFG) {
        TA0CCTL1 &= ~CCIFG;
      }
      if (TA0CCTL2 & CCIFG == CCIFG) {
        TA0CCTL2 &= ~CCIFG;
      }
      if (TA0CCTL3 & CCIFG == CCIFG) {
        TA0CCTL3 &= ~CCIFG;
      }
      if (TA0CCTL4 & CCIFG == CCIFG) {
        TA0CCTL4 &= ~CCIFG;
      }
      
      //check for overflow
      if (TA0CTL & TAIFG == TAIFG) {
        TA0CTL &= ~TAIFG;
        count++;
      }
    }

    So, TIMER0_A0_VECTOR is only called for TA0CCR0, and TIMER0_A1_VECTOR is called for the other capture/compare registers and for timer overflow, right?

  • To make the handling of the multiple interrupt source more convenient (and circumvent some quirks with asynchronous interrupt events during access ot the IFG register), there is the IV register.
    Reading TA0IV returns an even value from 0 (no interrupt) to n. The value corresponds to the highest-priority pending interrupt. At the same time it clears the IFG bit. The result of the register can be used in a switch statement, which is much more effective than a chain of Ifs. Be careful that the IV register may only be read once per interrupt. The next read will report the next pending interrupt.
    Also see the __even_in_range intrinsic, which optimizes the switch structure when used as argument in a switch statement.

    Switch(__even_in_range(TA0IV, 14)[
    case 0: return; // no interrupt pending
    case 2: // TA0CCR1.CCIFG pending
      break;
    case 4:...
    ...
    case 14: //TA0CTL.TAIFG pending
    }

    You can put this switch into a while(1) loop. It will exit the ISR when no more interrupts are pending (case 0).

**Attention** This is a public forum