Jump to content
MSPLife

Timer interrupt

Recommended Posts

Hi everyone,

I want to use both of Timer0_A0 and Timer0_A1 interrupt. I have configured as below:

void TIM_TRIG_ADC_CFG(){
	CCTL0 &= ~CCIE;
	CCTL1 &= ~CCIE;
	TACTL = TASSEL_2 + MC_1; // Set the timer A to SMCLCK, Continuous
	TACCR0 = 50000-1;
	TACCR1 += 25000-1;
	CCTL0 |= CCIE;
	CCTL1 |= CCIE;
	// Clear the timer and enable timer interrupt
	__enable_interrupt();
}

// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A00 (void)
{
	if(!((++timerCount)%16))
	P1OUT ^= LED_RED;
}

// Timer A1 interrupt service routine
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A01 (void)
{
	if(!((++timerCount1)))
	P1OUT ^= LED_GREEN;
}

It's only jump to Timer0A0 ISR, TImer0A1 is not.

Please hlep me to explain and how I can make Timer0A1 ISR working?

Thank in advance!

Share this post


Link to post
Share on other sites

Hi @@MSPLife,

 

Your code as submitted looks to be incomplete and you have not specified what MCU you are using.  The following, derived from something I wrote for the F5529 as a programming exercise recently, may do what you are attempting.  I added ACLK to slow things down but the commented out lines with SMCLK also work.

// Written for the F5529
//
//***** Include *********************************************************************************
#include <msp430.h>

//***** Define **********************************************************************************
#define  RED_LED               BIT0   // P1.0 is the red LED
#define  GREEN_LED             BIT7   // P4.7 is the green LED

//***** Main ************************************************************************************
main()
{
    WDTCTL = WDTPW | WDTHOLD;        // Stop watchdog timer

    P1DIR = RED_LED;                 // Set red LED to output
    P4DIR = GREEN_LED;               // Set green LED to output

    TA0CCR0 = 50000;                 // Timer A0 counts to this number
    //TA0CTL = TASSEL_2 + MC_1;      // Timer A0 using SMCLK, continuous
    TA0CTL = TASSEL_1 + MC_1;        // Use ACLK to slow things down
    TA0CCTL0 = CCIE;                 // Enable the interrupt for Timer A0

    TA1CCR0 = 25000;                 // Timer A1 counts to this number
    //TA1CTL = TASSEL_2 + MC_1;      // Timer A1 using SMCLK, continuous
    TA1CTL = TASSEL_1 + MC_1;        // Use ACLK to slow things down
    TA1CCTL0 = CCIE;                 // Enable the interrupt for Timer A1

    _BIS_SR(GIE);                    // Global activation of interrupts)

    while(1);                        // Wait here for the interrupt
}

//***** TimerA0 ISR ******************************************************************************
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer0_ISR (void)
{
    P1OUT = P1OUT ^ RED_LED;         // Toggle the red LED
}

//***** TimerA1 ISR ******************************************************************************
#pragma vector=TIMER1_A0_VECTOR
__interrupt void Timer1_ISR (void)
{
    P4OUT = P4OUT ^ GREEN_LED;       // Toggle the green LED
}

Share this post


Link to post
Share on other sites

@@MSPLife Have you debugged the operation of the interrupts?

 

I've assumed you are using the G2231, shouldn't really matter thought.

 

My thinking is that since TIMER0_A1_VECTOR is a shared interrupt you generally should be checking the corresponding Interrupt Vector, to determine the exact source of the interrupt.

void TIM_TRIG_ADC_CFG(){
	CCTL0 &= ~CCIE;
	CCTL1 &= ~CCIE;
	TACTL = TASSEL_2 + MC_1; // Set the timer A to SMCLCK, Continuous
	TACCR0 = 50000-1;
	TACCR1 += 25000-1;
	CCTL0 |= CCIE;
	CCTL1 |= CCIE;
	// Clear the timer and enable timer interrupt
	__enable_interrupt();
}

// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A00 (void)
{
	if(!((++timerCount)%16))
	P1OUT ^= LED_RED;
}

// Timer A1 interrupt service routine
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A01 (void)
{
	switch( TAIV )
	{
		case TAIV_NONE:
			break;

		case TAIV_TACCR1:
			if(!((++timerCount1)))
			P1OUT ^= LED_GREEN;
			break;

		case TAIV_TAIFG:
			break;

		default:
			break;
	}
}

This is because the TAIV is only cleared when TAIV is read, so there is a potential that the code does not signal that the interrupt was handled... This means your CPU gets stuck continuously servicing the interrupt. (I don't actually think this happens with MSP, but it could...)

 

An except from SLAU144 [12.2.6.2 - TAIV, Interrupt Vector Generator]

Any access, read or write, of the TAIV register automatically resets the highest pending interrupt 
flag. If another interrupt flag is set, another interrupt is immediately generated after servicing 
the initial interrupt.

Also note, that this statement

if(!((++timerCount1)))
	P1OUT ^= LED_GREEN;

Will toggle the GREEN LED if timerCount1 == 0. Since you are incrementing it, it will not toggle again until timerCount1 overflows, and equals 0 again.

So if you are relying on the LED to check if your interrupt is firing then this won't help.

Share this post


Link to post
Share on other sites

Dear Fmiburn

I want to use : TIMER0_A1_VECTOR not timer A1

 

Hi @@MSPLife,

 

Your code as submitted looks to be incomplete and you have not specified what MCU you are using.  The following, derived from something I wrote for the F5529 as a programming exercise recently, may do what you are attempting.  I added ACLK to slow things down but the commented out lines with SMCLK also work.

// Written for the F5529
//
//***** Include *********************************************************************************
#include <msp430.h>

//***** Define **********************************************************************************
#define  RED_LED               BIT0   // P1.0 is the red LED
#define  GREEN_LED             BIT7   // P4.7 is the green LED

//***** Main ************************************************************************************
main()
{
    WDTCTL = WDTPW | WDTHOLD;        // Stop watchdog timer

    P1DIR = RED_LED;                 // Set red LED to output
    P4DIR = GREEN_LED;               // Set green LED to output

    TA0CCR0 = 50000;                 // Timer A0 counts to this number
    //TA0CTL = TASSEL_2 + MC_1;      // Timer A0 using SMCLK, continuous
    TA0CTL = TASSEL_1 + MC_1;        // Use ACLK to slow things down
    TA0CCTL0 = CCIE;                 // Enable the interrupt for Timer A0

    TA1CCR0 = 25000;                 // Timer A1 counts to this number
    //TA1CTL = TASSEL_2 + MC_1;      // Timer A1 using SMCLK, continuous
    TA1CTL = TASSEL_1 + MC_1;        // Use ACLK to slow things down
    TA1CCTL0 = CCIE;                 // Enable the interrupt for Timer A1

    _BIS_SR(GIE);                    // Global activation of interrupts)

    while(1);                        // Wait here for the interrupt
}

//***** TimerA0 ISR ******************************************************************************
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer0_ISR (void)
{
    P1OUT = P1OUT ^ RED_LED;         // Toggle the red LED
}

//***** TimerA1 ISR ******************************************************************************
#pragma vector=TIMER1_A0_VECTOR
__interrupt void Timer1_ISR (void)
{
    P4OUT = P4OUT ^ GREEN_LED;       // Toggle the green LED
}

Share this post


Link to post
Share on other sites

Dear greeeg,

I put a breakpoint in TIMER0_A1_VECTOR, but it doeasn jump to that.

 

@@MSPLife Have you debugged the operation of the interrupts?

 

I've assumed you are using the G2231, shouldn't really matter thought.

 

My thinking is that since TIMER0_A1_VECTOR is a shared interrupt you generally should be checking the corresponding Interrupt Vector, to determine the exact source of the interrupt.

void TIM_TRIG_ADC_CFG(){
	CCTL0 &= ~CCIE;
	CCTL1 &= ~CCIE;
	TACTL = TASSEL_2 + MC_1; // Set the timer A to SMCLCK, Continuous
	TACCR0 = 50000-1;
	TACCR1 += 25000-1;
	CCTL0 |= CCIE;
	CCTL1 |= CCIE;
	// Clear the timer and enable timer interrupt
	__enable_interrupt();
}

// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A00 (void)
{
	if(!((++timerCount)%16))
	P1OUT ^= LED_RED;
}

// Timer A1 interrupt service routine
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A01 (void)
{
	switch( TAIV )
	{
		case TAIV_NONE:
			break;

		case TAIV_TACCR1:
			if(!((++timerCount1)))
			P1OUT ^= LED_GREEN;
			break;

		case TAIV_TAIFG:
			break;

		default:
			break;
	}
}

This is because the TAIV is only cleared when TAIV is read, so there is a potential that the code does not signal that the interrupt was handled... This means your CPU gets stuck continuously servicing the interrupt. (I don't actually think this happens with MSP, but it could...)

 

An except from SLAU144 [12.2.6.2 - TAIV, Interrupt Vector Generator]

Any access, read or write, of the TAIV register automatically resets the highest pending interrupt 
flag. If another interrupt flag is set, another interrupt is immediately generated after servicing 
the initial interrupt.

Also note, that this statement

if(!((++timerCount1)))
	P1OUT ^= LED_GREEN;

Will toggle the GREEN LED if timerCount1 == 0. Since you are incrementing it, it will not toggle again until timerCount1 overflows, and equals 0 again.

So if you are relying on the LED to check if your interrupt is firing then this won't help.

Share this post


Link to post
Share on other sites

@@MSPLife

 

The only other thing I can think off is regarding your initialization of CCR's

TACCR0 = 50000-1;
TACCR1 += 25000-1;

Suppose TACCR1 is not initialized to 0 on reset. Or is TIM_TRIG_ADC_CFG() is called multiple times.

If TACCR1 > TACCR0 Since you reset the TAR when it reaches CCR0, then it will never reach CCR1.

 

I would suggest changing these lines to

TACCR0 = 50000-1;
TACCR1 = 25000-1;

This might not produce the desired result. Both the interrupts will fire at the same rate, just offset by half a cycle. This is how your original code operated.

A0 IFG     _______^________^_______

A1 IFG     ___^_______^________^___

Share this post


Link to post
Share on other sites

 

Dear Fmiburn

I want to use : TIMER0_A1_VECTOR not timer A1

Oops :)

 

@@greeeg

 

Implementing your suggestions on my code seems to work...

// Written for the F5529
//
//***** Include *********************************************************************************
#include <msp430.h>

//***** Define **********************************************************************************
#define  RED_LED               BIT0   // P1.0 is the red LED
#define  GREEN_LED             BIT7   // P4.7 is the green LED

//***** Main ************************************************************************************
main()
{
    WDTCTL = WDTPW | WDTHOLD;        // Stop watchdog timer

    P1DIR = RED_LED;                 // Set red LED to output
    P4DIR = GREEN_LED;               // Set green LED to output

    TA0CCR0 = 50000;                 // Timer A0 counts to this number
    TA0CCR1 = 25000;                 // Timer A1 counts to this number
    TA0CTL = TASSEL_2 + MC_1;        // Timer A0 using SMCLK, continuous
    TA0CCTL0 = CCIE;                 // Enable the interrupt for Timer0_A0
    TA0CCTL1 = CCIE;                 // Enable the interrupt for Timer0_A1

    _BIS_SR(GIE);                    // Global activation of interrupts)

    while(1);                        // Wait here for the interrupt
}

//***** TimerA0 ISR ******************************************************************************
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer0_ISR (void)
{
    P1OUT = P1OUT ^ RED_LED;         // Toggle the red LED
}

//***** TimerA1 ISR ******************************************************************************
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer1_ISR (void)
{
    switch(__even_in_range( TA0IV, 10)) {
    case 0x00: 
    	break;                        // None
    case 0x02:                        // CCR1 IFG
    	P4OUT = P4OUT ^ GREEN_LED;    // Toggle the green LED
    default: 
    	break;
    }
}

Snippet from a TI course....

post-45284-0-56256400-1450426914_thumb.jpg

Share this post


Link to post
Share on other sites

Dear @@greeeg, @@Fmilburn,

1.My program worked, but I want to understand, why do we need to check TA0IV in a specific ISR?

If I want to use TImer0A2, I need to check TAIV in Timer0A2 ISR as well?

2. Now my program is below:

void TIM_TRIG_ADC_CFG(){
	CCTL0 &= ~CCIE;
	CCTL1 &= ~CCIE;
	TACTL = TASSEL_2 + MC_1; // Set the timer A to SMCLCK, Continuous
	TACCR0 = 50000-1;
	TACCR1 = 25000-1;
	CCTL0 |= CCIE;
	CCTL1 |= CCIE;
	// Clear the timer and enable timer interrupt
	__enable_interrupt();
}

#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A00 (void)
{
	if(!((++timerCount)%32)){
		Toggle(1,0); //Toggle RED LED
	}
}
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A01 (void)
{
	switch(__even_in_range( TA0IV, 10)) {
		case 0x00:
			break;                        // None
		case 0x02:                        // CCR1 IFG
			if(!((++timerCount1)%32))
				Toggle(1,6); // Toggle GREEN LED
		default:
			break;
	}

}

The LEDs blink with same period while my set up make GREEN LED blink with a shorter period (faster).

 

Please help to explain.

Thank so much!

Share this post


Link to post
Share on other sites

Dear @@greeeg, @@Fmilburn,

1.My program worked, but I want to understand, why we need to check TA0IV in the specific ISR?

If I want to use TImer0A2, I need to check TAIV in Timer0A2 ISR as well?

 

...

 

The LEDs blink with same period while my set up make GREEN LED blink with a shorter period (faster).

 

Please help to explain.

Thank so much!

 

Can you tell me which processor you are using, it will make this explanation more specific.

 

Why we need to check TA0IV.

First, T0A2 interrupt does not exist. This is the purpose of TA0IV (Timer A0 Interrupt Vector)

 

Interrupts require a fair amount of hardware (priority encoders, etc). To keep this manageable, the MSP430 are conservative with their interrupts. Lets look at the interrupts inside a MSP430G2955

post-274-0-79792300-1450491555_thumb.png

 

There is only 15 unique interrupt vectors! Note that the CPU in the G2955 supports 32 interrupt vectors.

 

Let's look at it's timer. Timer0_A3. This means that this timer contains 3 capture/compare modules. So that means 3 interrupts right? Actually 4, the timer itself has an overflow interrupt. But this timer is only assigned 2 unique interrupt vectors.

Timer0_A0 handles

  • TACCR0 - CCIFG

Timer0_A1 handles

  • TACCR1 - CCIFG
  • TACCR2 - CCIFG
  • TAIFG (overflow)

To differentiate we must check TA0IV to determine the exact source within Timer0_A1

 

Why not have all 4 separately? This might work on this particular IC, however larger MSP's include Timers with upto 7/8 CCR's There is not enough interrupt vectors to handle all those sources.

 

Note it's easy to get confused by the vector names. You can check the header file for your MSP430 to see the available vector names.

post-274-0-32881100-1450492395_thumb.png

 

 

"If I want to use TImer0A2, I need to check TAIV in Timer0A2 ISR as well?"

This will be part of the Timer0_A1 ISR. but yes, you need to check the TAIV.

#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A01 (void)
{
  switch(__even_in_range( TA0IV, 10)) {
    case 0x00:                      // None
      break;                        

    case 0x02:                      // CCR1 IFG
      if(!((++timerCount1)%32))
        Toggle(1,6); // Toggle GREEN LED
      break;

    case 0x04:                      // CCR2 IFG
      if(!((++timerCount2)%32))
        Toggle(1,0); // Toggle RED LED
      break;

    default:
      break;
  }
}

You can find in the family guide, that a value of 2 in TAIV represents CCR1, a value of 4 represents CCR2.

post-274-0-42645700-1450492865_thumb.png

Share this post


Link to post
Share on other sites

The LEDs blink with same period while my set up make GREEN LED blink with a shorter period (faster).

 

  • Your timer currently count to CCR0.
  • CCR0 is set to 49,999
  • When your timer reaches 49,999 an interrupt occurs. and the next timer value is 0.
  • CCR1 is set to 24,999
  • When the timer value reaches 24,999 it will cause an interrupt.

 

Hence you're leds blink at the same rate. It takes the timer the same amount of time to count

from 0 to 49,999  = 50,000 ticks

and 25,000 to 24,999 = 50,000 ticks.

 

 

 

How do you fix this. I would recommend the following. (but there are multiple ways.)

 

Set TA0 to run in continuous mode, instead of up mode. (Your code comments mistakenly say continuous, but you've been setting it to up mode)

post-274-0-01232200-1450493313_thumb.png

 

  • When CCR0 ISR fires, you need to add your new period to CCR0
  • When CCR1 ISR fires, you need to add your new period to CCR1
void TIM_TRIG_ADC_CFG(){
  CCTL0 &= ~CCIE;
  CCTL1 &= ~CCIE;
  TACTL = TASSEL_2 + MC_2; // Set the timer A to SMCLCK, Continuous
  TACCR0 = 50000-1;
  TACCR1 = 25000-1;
  CCTL0 |= CCIE;
  CCTL1 |= CCIE;
  // Clear the timer and enable timer interrupt
  __enable_interrupt();
}

#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A00 (void)
{
  CCR0 += 50000;                  // Return in 50,000 cycles

  if(!((++timerCount)%32)){
    Toggle(1,0);                  //Toggle RED LED
  }
}
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A01 (void)
{
  switch(__even_in_range( TA0IV, 10)) {
  case 0x00:                      // None
    break;                        

  case 0x02:                      // CCR1 IFG
    CCR1 += 25000;                // Return in 25,000 cycles

    if(!((++timerCount1)%32))
      Toggle(1,6);                // Toggle GREEN LED
  break;
 
  default:
    break;
  }

}

Now most of the time adding large value like this is dangerous, because of integer overflow. But it's okay here, because the timer counter itself is overflowing in exactly the same way.

Share this post


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...