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.

MSP430FR2676: Multiple Timers on Timer_B

Part Number: MSP430FR2676

I am trying to set up multiple timers on the MSP430FR2676. I know that it has two timers: A and B. I have been able to run the example code successfully (see below). However, I would like to be able to run 4 separate timers to toggle 4 separate LEDs. I am trying to set that up using Timer B with the separate TB0CCTLn registers. I understand that each separate TB0CCTLn register has a separate count in order to cause an interrupt. I am a little confused on how to recognize when the separate interrupts occur in the interrupt function. Is there a register to read that states which interrupt triggered the event? Also, I know that sending a TB0CTL &= ~MC stops the entire Timer B. However, is there a way to stop an individual TB0CCTLn count so as to stop each individual LED timer via software?

#include <msp430.h>
int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;                     // Stop WDT

    // Configure GPIO
    P1DIR |= BIT0;                                // P1.0 output
    P1OUT |= BIT0;                                // P1.0 high

    // Disable the GPIO power-on default high-impedance mode to activate
    // previously configured port settings
    PM5CTL0 &= ~LOCKLPM5;

    TA0CCTL0 |= CCIE;                             // TACCR0 interrupt enabled
    TA0CCR0 = 50000;
    TA0CTL |= TASSEL__SMCLK | MC__CONTINUOUS;     // SMCLK, continuous mode

    __bis_SR_register(LPM0_bits | GIE);           // Enter LPM0 w/ interrupts
    __no_operation();                             // For debug
}

// Timer A0 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) Timer_A (void)
#else
#error Compiler not supported!
#endif
{
    P1OUT ^= BIT0;
    TA0CCR0 = 50000;                             // Add Offset to TACCR0
}

  • To add to this, is there a better way to do what I am trying to do besides put each timer on a separate TB0CCTLn register?

  • I suspect that this isn't what you want. The timer is configured in continuous mode which means it counts to its maximum value and then goes back to zero. (UP mode would count to the value in CCR0 before reset.) You can of course get an interrupt on each CCRx but the rate at which they happen will be identical. With phase dependent on the CCRx values.

    The CCR0 interrupt has its own interrupt vector while the others are shared and the IV register can be read to tell you which one fired. Described in detail in the user guide.

    Oh, the data sheet shows that this device has five timers, Four TimerA and one TimerB.

  • Not quite sure on architecture of what you trying to do.

    Each Timer device (e.g Timer A0, Timer A1, Timer B0) has its own counter that sets the period of the PWM cycle, now as that counter cycles (either up to 0xffff, or to some value specified by CCR0, depending on MC bits in TAxCTL - you have continuous, so it cycles up to 0xffff). Now there are many compare registers looking at the PWM counter CCR1-n; each of those can trigger when the PWM counter passes that point.

    So the compare registers, are often used as the Duty Cycle in any PWM output, so they toggle for what percentage of the pwm cycle duration, that each of those outputs are active for. So setting CCR1 to 0xff - and it would typically be active from 0 to 0xff, and off from 0x100 to 0xffff; so the led attached would only be one for 1/256 of the total time - and so fairly dim.

    Different compare register can have different CCRn values, and so be one for different percentages of the time.

    So question is what are you trying to achieve, do all your LED flash at the same period, but with different durations? If so then may be able to do that with a single Timer. If the LEDs are totally asynchronous (e.g. one flashing every 5s and another every 7s) - then you probably need multiple Timers for that.

    Does that help? Not sure I've really understood what you are trying to do.

  • And just a though looking at the code. Have you though of doing port mapping; so have the Timer explicitly toggling a IO port, and then if you can't attach you LEDs to those pins, then port mapping to where the LED is connected? Means you don't need the code to toggle the LED light, instead the timer device does this.

  • Hi David,

    I have attached my latest code that I have so far to hopefully give more clarity to what I am trying to do. In essence, you did describe it well in your comment of what I am trying to do. I am trying to have four timers that are controlled separately for four separate LEDs. I am fine if they are controlled on the same clock, but I want them to be able to be set at different times. For example, I want the software to be able to start the LED1 timer to turn LED1 on when a certain button is pressed and leave it on for a certain amount of seconds before turning it off (could be set anywhere from a few ms to 20 seconds). While the timer for LED1 is running, another button could be pressed to turn on LED2 for a different amount of time before it turns off. The same would be happening with LEDs 3 and 4. It sounds like this may not be the best approach to do. If that is the case, I could create a custom timer code myself that counts independently. However, I wanted to try and utilize the timer modules the MSP430 already had.

    //*****************************************************************************
    // Definitions
    //*****************************************************************************
    
    //*****************************************************************************
    //
    //! Timer B is used for the LEDs.
    //! Register 0 is LED 1.
    //! Register 1 is LED 2.
    //! Register 2 is LEDs 3.
    //! Register 3 is LEDs 4
    //! \def TIMER_B_RESET defines a shortcut to set the timer B reset bit.
    //! \def TIMER_B_CTL_SET defines a shortcut to set the timer B clock to the
    //!      SMCLK divided by 2 (1MHz), the timer B mode to up count, and set the
    //!      max count to 16-bit (65535).
    //! \def TIMER_B_CTL_STOP defines a shortcut to stop the timer B clocks.
    //! \def TIMER_B_LED_1_IQR_ENABLE defines a shortcut to set the timer B
    //!      register 0 interrupt enable bit.
    //! \def TIMER_B_LED_1_IQR_CLEAR defines a shortcut to clear the timer B
    //!      register 0 interrupt bit.
    //! \def TIMER_B_LED_1_COUNT defines a shortcut to the timer B count
    //!      register 0.
    //! \def TIMER_B_LED_2_IQR_ENABLE defines a shortcut to set the timer B
    //!      register 1 interrupt enable bit.
    //! \def TIMER_B_LED_2_IQR_CLEAR defines a shortcut to clear the timer B
    //!      register 1 interrupt bit.
    //! \def TIMER_B_LED_2_COUNT defines a shortcut to the timer B count
    //!      register 1.
    //! \def TIMER_B_LED_3_IQR_ENABLE defines a shortcut to set the timer B
    //!      register 2 interrupt enable bit.
    //! \def TIMER_B_LED_3_IQR_CLEAR defines a shortcut to clear the timer B
    //!      register 2 interrupt bit.
    //! \def TIMER_B_LED_3_COUNT defines a shortcut to the timer B count
    //!      register 2.
    //! \def TIMER_B_LED_4_IQR_ENABLE defines a shortcut to set the timer B
    //!      register 3 interrupt enable bit.
    //! \def TIMER_B_LED_4_IQR_CLEAR defines a shortcut to clear the timer B
    //!      register 3 interrupt bit.
    //! \def TIMER_B_LED_4_COUNT defines a shortcut to the timer B count
    //!      register 3.
    //
    //*****************************************************************************
    #define TIMER_B_RESET                                         (TB0CTL |= TBCLR)
    #define TIMER_B_CTL_SET   (TB0CTL |= TBSSEL__SMCLK | ID__2 | MC__UP | CNTL__16)
    #define TIMER_B_CTL_STOP                                        (TB0CTL &= ~MC)
    #define TIMER_B_LED_1_IQR_ENABLE                             (TB0CCTL0 |= CCIE)
    #define TIMER_B_LED_1_IQR_CLEAR                            (TB0CCTL0 &= ~CCIFG)
    #define TIMER_B_LED_1_COUNT                                           (TB0CCR0)
    #define TIMER_B_LED_2_IQR_ENABLE                             (TB0CCTL1 |= CCIE)
    #define TIMER_B_LED_2_IQR_CLEAR                            (TB0CCTL1 &= ~CCIFG)
    #define TIMER_B_LED_2_COUNT                                           (TB0CCR1)
    #define TIMER_B_LED_3_IQR_ENABLE                             (TB0CCTL2 |= CCIE)
    #define TIMER_B_LED_3_IQR_CLEAR                            (TB0CCTL2 &= ~CCIFG)
    #define TIMER_B_LED_3_COUNT                                           (TB0CCR2)
    #define TIMER_B_LED_4_IQR_ENABLE                             (TB0CCTL3 |= CCIE)
    #define TIMER_B_LED_4_IQR_CLEAR                            (TB0CCTL3 &= ~CCIFG)
    #define TIMER_B_LED_4_COUNT                                           (TB0CCR3)
    
    //*****************************************************************************
    // Function Implementations
    //*****************************************************************************
    
    void Timer_startLED1Timer(uint16_t usDuration)
    {
        // Initialize timer B, if applicable
        Timer_startTimerB();
    
        // Enable the LED timer 1 interrupt, set the count, and clear the interrupt bit
        TIMER_B_LED_1_IQR_ENABLE;
        TIMER_B_LED_1_COUNT = usDuration;
        TIMER_B_LED_1_IQR_CLEAR;
    }
    
    void Timer_startLED2Timer(uint16_t usDuration)
    {
        // Initialize timer B, if applicable
        Timer_startTimerB();
    
        // Enable the LED 2 timer interrupt, set the count, and clear the interrupt bit
        TIMER_B_LED_2_IQR_ENABLE;
        TIMER_B_LED_2_COUNT = usDuration;
        TIMER_B_LED_2_IQR_CLEAR;
    }
    
    void Timer_startLED3Timer(uint16_t usDuration)
    {
        // Initialize timer B, if applicable
        Timer_startTimerB();
    
        // Enable the LED 3 timer interrupt, set the count, and clear the interrupt bit
        TIMER_B_LED_3_IQR_ENABLE;
        TIMER_B_LED_3_COUNT = usDuration;
        TIMER_B_LED_3_IQR_CLEAR;
    }
    
    void Timer_startLED4Timer(uint16_t usDuration)
    {
        // Initialize timer B, if applicable
        Timer_startTimerB();
    
        // Enable the LED 4 timer interrupt, set the count, and clear the interrupt bit
        TIMER_B_LED_4_IQR_ENABLE;
        TIMER_B_LED_4_COUNT = usDuration;
        TIMER_B_LED_4_IQR_CLEAR;
    }
    
    void Timer_stopLEDTimers(void)
    {
        TIMER_B_CTL_STOP;
        TIMER_B_RESET;
    }
    
    void Timer_startTimerB(void)
    {
        // Initialize the timer B peripheral, if the timer is not already running
        // TODO: Add way to check the status of the timer
    	if (1)
    	{
            Timer_stopLEDTimers();
            TIMER_B_CTL_SET;
        }
    }
    
    // Interrupt for the LED 1 timer
    #pragma vector = TIMER0_B0_VECTOR
    __interrupt void Timer_led1ISR()
    {
    	// TODO: Toggle LED 1 and stop timer, if applicable
    }
    
    // Interrupt for other LED timers
    #pragma vector = TIMER0_B1_VECTOR
    __interrupt void Timer_led2To4ISR()
    {
    	// TODO: Check which interrupt occurred, toggle appropriate LED, and stop timer, if applicable
    }
    

  • Haven't check the code yet - but what you describe doesn't sound like usual Timer logic. The Timer devices tend to do something repetitive,  like flash an LED on and off forever.

    So how I'd be tempted to code it, is I'd set one interrupt to monitor but presses. Think you may even be able to get a button press to initiate an interrupt, so all you need to do is write code to capture that interrupt. Then depending on the button press I say its sets an alarm, haven't looked into doing that on msp430, but i'd expect it to be functionally that the RTC has.

    So when a button is pressed, code first switches on the relevant LED. Then it sets an alarm event for when the LED has to switch off, probably the msp430 can only do one alarm. So you'll need a table of alarm events, and when you add something to the table, you scan the table for the earliest event, and then make sure the alarm is set for the earliest event.

    When the alarm goes off, guess that will be captured by another interrupt, and routine. That routine, then scans the table of alarm events, see's which event has happened, and switches off the relevant LED. Then scans the table for the next event to happen, and sets the alarm for that event.

    So I'd suggest reading the RTC part of the manual, and how on the digital IO section (so button press) how you can use that to set an interrupt.

    Does that make sense?

  • Hi David,

    That makes perfect sense. After looking at all of your comments earlier, that did seem to me to be the best approach to handle this application. I will move forward with that.

**Attention** This is a public forum