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.

MSP430FR2311: New to MSP430 need help understanding Timer B functionality.

Part Number: MSP430FR2311

Hello everyone,

I am new to the MSP430 platform and I working on a project where a double pulse needs to generated with a 200us delay between both rising edges (see picture attached). I am currently trying to use Timer B to generate these signals. currently using standard 1MHz SMCLK for input to timer B.

The code below is what I currently have and I am not understanding how the TBxCCRn register are working. I was wondering if someone could help out? or provide a better explanation that the one that is the datasheet / family user manual? What is confusing me is the values that are being placed into the TB1CCR1 and TB1CCR2 registers

Thank you for all the help in advance.

#include <msp430.h>

int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;  //stops the watchdog timer.


    

    //Trigger Input (P1.0)
    P1DIR &= ~BIT0;
    P1REN |= BIT0; //enable internal pullup/pulldown resistors
    P1OUT |= BIT0; // select pull-up mode for P1.0
    P1IE |= BIT0; //enable interrupts
    P1IES |= BIT0; //high -> low sets flag
    P1IFG &= ~BIT0; //interrupt flag register cleared


    //output on p1.1 (INIT_Q_DRIVE)
    P1DIR |= BIT1; // setting direction on specific pin
    P1OUT &= ~BIT1; // setting the initial output to 0


    PM5CTL0 &= ~LOCKLPM5; //disable GPIO power-on default to activate previously configured settings

    //TB1CCTL1 |= (OUTMOD_7 | CCIE); // Timer B1 Capture/Compare Control Register1 is set to Set/Reset
    TB1CCTL1 |= CCIE; //Timer B1 Capture/Compare Control Register1 enabled for interrupts

    //TB1CCTL2 |= (OUTMOD_7 | CCIE); // Timer B1 Capture/Compare Control Register1 is set to Set/Reset
    TB1CCTL2 |= CCIE; //Timer B1 Capture/Compare Control Register1 enabled for interrupts
    
    TB1CCR1 = 10;
    TB1CCR2 = 20;

    while(1){
        __bis_SR_register(LPM3_bits | GIE); //enter low power mode and enable interrupts
    }
}

// interrupt on I/O port (pin P1.0)
#pragma vector = PORT1_VECTOR
__interrupt void Port_1_ISR(void)
{

    P1IE &= ~BIT0; //disable interrupts so none occur while in this routine
    P1IFG &= ~BIT0; //clear interrupt flag that occured on p1.0

    //below the different delay times need to be set (200, 400, 800, 1000 us)
    //check input pins to see what has been selected. checked based on rotary switch 
    
    TB1CCR0 = 208; //200us delay (this hardcoding is temporary)
    
    //start timer
    TB1CTL = TBSSEL_2 | MC_1 | TBCLR | TBIE; // Starting the timer, SMCLK(1MHz), UP mode, clear TBR, enable interrupt
}

//timer b interrupt service routine when timer b causes an interrupt this is called
#pragma vector = TIMER1_B1_VECTOR
__interrupt void TIMER1_B1_ISR(void)
{
     switch(__even_in_range(TB1IV,TB1IV_TBIFG))
    {
        case TB1IV_NONE:
            break; // No interrupt

        case TB1IV_TBCCR1:
            P1OUT ^= BIT1; //Output...rising edge of both waves
            break;
            
        case TB1IV_TBCCR2:
            P1OUT ^= BIT1; //Output...falling edge of both waves
            break;
            
        case TB1IV_TBIFG:
            P1IE |= BIT0; //interrupt enabled
            P1IFG &=~BIT0; //clear interrupt flag... when timer stops
            TB1CTL = TBSSEL_2 | MC_0 | TBCLR | TBIE; //Stopping timer
            break;
            
        default:
            break;
    }
}

  • Hi Matt,

    With SMCLK of 1 MHz & TB1CCR1 = 10 & TB1CCR2 = 20, you were expecting a pulse width of 10 us while you are seeing a pulse width of about 35 us. Is that the query? I think with the default DCO clock of 2 MHz, 10 us is 20 cycles. I think the interrupt handling in the code above would be taking more than 20 cycles. I think this is the reason, why the values used in TB1CCR1 & TB1CCR2 do not match the pulse width. 

    You can increase the clock to 16 MHz and that should allow you to control the pulse width better using the values programmed in TB1CCR1 & TB1CCR2.

    Srinivas

  • Yes that was one of my questions because I thought that’s what the TB1CCR1 & TB1CCR2 would control is the pulse width but in my testing it was never the case. I would change those values and pulse width would stay the same. But now it makes more sense because I didn’t even think about the cycles that the ISR consumed. Also is there a better way to handle this that you would recommend? Sorry last question. Why is the TB1IV_TBIFG flag only thrown once? I’m not to sure how the code I provided is generating that double pulse. Thank you for all your help
  • The double pulse (and the single TAIFG) results from the interrupt (TAIV) priority, and TAIFG is the lowest. The TAIFG, CCR1, and CCR2 interrupts are all happening within 10us of each other, so by the first read of the TAIV both the (older) TAIFG and the (newer) CCR1 interrupts are both present and CCR1 goes first. By that time, another 10us has passed and the CCR2 goes first. That sequence generates your second pulse. Only after that does the TAIFG get presented to turn off the timer. 

    This accomplishes your double pulse, though it might be more reliable to just count pulses -- CCR2 always ends the pulse, so it could count to 2 and turn off the timer (no TAIFG at all).

    -------

    Do you have a choice about the pin you use for your output (INIT_Q_DRIVE)? If you could use e.g. P2.0 (TB1.1) you could use OUTMOD=7 (Reset/Set) and set your pulse width in CCR1 and pulse period in CCR0. (CCR2 is unused.) The hardware will generate the pulses exactly, without worrying about how long your software needs. [Edit: You also need to set the PSEL bits according to data sheet (SLASE58E) Table 6-44]

    That would be most of the answer. (1) You still need to stop the pulse train after the second pulse. For that I suggest using the CCR1 CCIE and count to 2. At the moment of the interrupt, the signal will be low, so you can just stop the timer. (2) The initial pulse requires a small dance, since it's the top of the count that triggers it. For this, load TB1R with (CCR0-1) before starting the timer (no TBCLR). There will be one tick of latency, but that won't be noticeable in all the other things you're doing.

    [Edit: Fixed typo.]

  • Unfortunately I am not able to change the output pin because all the rest are being used for other functionality on the board. I read something in the datasheet about the OUTMOD bits and i thought about going that route but then realized that I cant output on the pin I would need to. 

    I do like your idea with counting the pulses and then stopping the timer when reaching the second pulse, my current solution is I cranked the clocked speed up to 16MHz so that interrupts are not all happening at the same time. The results from using a 16MHz clock are much more predictable. 

    So now my ISR looks something like this. where I am using a volatile int to count to 2 before stopping the timer similar solutions to yours but probably mine is less efficient. 

    Thank you for your reply it did help me understand what was actually going on. Now my next venture is making the msp430 work with an external crystal oscillator!

    #pragma vector = TIMER1_B1_VECTOR
    __interrupt void TIMER1_B1_ISR(void)
    {
         switch(__even_in_range(TB1IV,TB1IV_TBIFG))
        {
            case TB1IV_NONE:
                break; // No interrupt
    
            case TB1IV_TBCCR1:
                P1OUT ^= BIT1; //Output...rising edge of both waves
                break;
                
            case TB1IV_TBCCR2:
                P1OUT ^= BIT1; //Output...falling edge of both waves
                break;
                
            case TB1IV_TBIFG:
              if(i == 0){
                i = 1;
              }
              else{
                i = 0;
                P1IE |= BIT0; //interrupt enabled
                P1IFG &=~BIT0; //clear interrupt flag... when timer stops
                TB1CTL = TBSSEL_2 | MC_0 | TBCLR | TBIE; //Stopping timer
              }
                break;
                
            default:
                break;
        }
    }

**Attention** This is a public forum