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.

MSP430FR2673: Reading from TAxR while it's Running in Continous Mode

Part Number: MSP430FR2673

Hello Reader,

I am currently working on a project where we are managing a lot of timing events in the forground.  We have a verison of the firmware that runs on a PC to simulate it and it's reliant on getting a timestamp.  I'm porting this over to our MSP.  I'd like to be able to just setup a timer to count up continously and then just read TAxR in the forground but there is this note in the family guide that makes me hesitant.

Here is a quote from the Family Guide Slau445i (https://www.ti.com/lit/ug/slau445i/slau445i.pdf):

"13.2.1 16-Bit Timer Counter The 16-bit timer/counter register, TAxR, increments or decrements (depending on mode of operation) with each rising edge of the clock signal. TAxR can be read or written with software. Additionally, the timer can generate an interrupt when it overflows.

NOTE: Accessing TAxR Care must be taken when accessing TAxR. If TAxR is accessed (read or write) by the CPU while the timer is running, the value read from TAxR or the value written to TAxR could be unpredictable. To avoid this uncertainty, the timer should be stopped by writing the MC bits to zero before accessing TAxR. For read, alternatively TAxR can be read multiple times while the timer is running, and a majority vote taken in software to determine the correct reading."

I don't want to stop the timer when I read from it so I suppose that leaves me with reading the timer multiple times and taking a majority vote.  But what does that mean?  Do I read multiple times and save the values into an array and then choose the most frequent value out of the array?  Alternatively, could I just use a capture register some how and then read from that?

Thank you for your time and any feedback you may have.

Regards,
Zachary Anderson

  • The hazard is that the timer might be in the process of incrementing as you read it. With a carry propagating through the bits. Read until you get two sequential reads that match.

     I suspect that the timer counter uses a simple ripple carry implementation. It would be nice to know just how long that takes as that would tell you how long the value could be changing.

  • Hello David,

    Thank you for your reply.  I am hoping to use the MCLK for both the CPU and the Timer.  In this case, the Timer would change every time I read it.  I could slow down the timer clock but I'd prefer not to.  Our PC simulation is using this resolution and I'd like to keep it close.

    I have setup a capture register to get the time - I'll be testing shortly and report back.

    U32 get_time(void)
    {
    U32 u32TimeInMicroSeconds = 0;

    // Capture Timer A0 into Capture Register TA0CCR0
    // By creating a rising edge and then reseting
    //
    TA0CCTL0 &= (U32) ~CCIFG; // Clear Capture Flag
    TA0CCTL0 = (U16) CCIS__VCC | (TA0CCTL0 & ~((U16) CCIS)); // Set to VCC
    __no_operation(); // Give it a cycle to trigger a capture
    TA0CCTL0 = (U16) CCIS__GND | (TA0CCTL0 & ~((U16) CCIS)); // Set to GND

    // Wait for Capture/Compare Interrupt Flag (CCIFG) to set
    // before reading the capture register
    //
    while(!(TA0CCTL0 & CCIFG)); // Wait for Capture/Compare Flag
    TA0CCTL0 &= (U32) ~CCIFG; // Clear Capture Flag

    // Copy Capture Register & Overflow Counter into
    // TimeInMicroSeconds
    //
    u32TimeInMicroSeconds = (U32) TA0CCR0; // Copy capture register
    u32TimeInMicroSeconds |= (U32) g_u16TimeInMicroSecondsOverflowCnt << 16;

    return u32TimeInMicroSeconds;
    }



    Regards,
    Zachary Anderson

  • Hi,

    You method seems workable. Please share the results of this solution. Thanks!

    Best regards,

    Cash Hao

  • Hello Everyone,

    Reporting back.  Setting the input of the capture register to ground and then to vcc does capture a rising edge.  I can see the values updating in the capture register with timer on the SMCLK=MCLK.

    Next is to measure how long it takes to get the time with this method and verify that I'm able to read time accurately.  I'm going to save the values into an array every 100 cycles or so - I expect that the value read from the timer will be seperated by about 100 cycles + the cycles it takes to trigger the capture.  I'll report back what I find.

    Regards,
    Zachary Anderson

  • Here are my results.  56uS to read the timer with MCLK at 1.016MHz.
    Any ideas on how to speed it up are appreciated.

    Not perfect.  Occasionally the overflow counter gets incremented just after I've captured the register which leads to an incorrect time.
    And the regular intervals I'm getting by flipping a pin are 448uS instead of my expected 416uS.

    //*****************************************************************************
    //
    // GetTimerCount - Gets the current count on the timer.
    //
    // parameters: none
    //
    // returns - U32 The current count of the timer.
    //
    // Notes
    // ----
    // This timer will roll over after 1h11min.
    // This function takes 56.14uS to complete
    //
    //*****************************************************************************

    U32 GetTimerCount(void)
    {
    U32 u32TimerCount = 0;

    // Capture Timer A0 into Capture Register TA0CCR0
    // By creating a rising edge and then reseting
    //
    //TA0CCTL0 &= (U16) ~CCIFG; // Clear Capture Flag
    TA0CCTL0 = (U16) CCIS__VCC | (TA0CCTL0 & ~((U16) CCIS)); // Set to VCC
    __no_operation(); // Give it a few no ops to trigger a capture
    __no_operation();
    __no_operation();
    TA0CCTL0 = (U16) CCIS__GND | (TA0CCTL0 & ~((U16) CCIS)); // Set to GND

    // Wait for Capture/Compare Interrupt Flag (CCIFG) to set
    // before reading the capture register
    //
    while(!(TA0CCTL0 & CCIFG)); // Wait for Capture/Compare Flag
    TA0CCTL0 &= (U16) ~CCIFG; // Clear Capture Flag

    // Copy Capture Register & Overflow Counter into
    // TimerCount
    //
    TA0CTL &= (U16) ~TAIE; // Disable overflow interrupt
    u32TimerCount = (U32) TA0CCR0; // Copy capture register
    u32TimerCount |= (U32) (((U32) g_u16TimerOverflowCount) << 16);
    TA0CTL |= TAIE; // Enable overflow interrupt

    return u32TimerCount;
    }
  • A 1MHz MCLK? That is really slow. And if the timer is running at that speed, it makes your problem harder as well.

    A timer with such high resolution rarely is useful. And the time required to read it makes that resolution even less useful.

  • Hello everyone,

    I just checking in.  I figured out how to make sure that I'm not reading in the middle of an overflow.  I also found that adding a NOP after disabling interrupts was necessary, otherwise the Microcontroller would go into some kind of fault state.  The time I'm reading now has a consistent offset of about 20-40uS from 400uS to 12000uS.

    //*****************************************************************************
    //
    // GetTimerCount - Gets the current count on the timer.
    //
    // parameters: none
    //
    // returns - U32 The current count of the timer.
    //
    // Notes
    // ----
    // This timer will roll over after 1h11min.
    // This function takes 56.14uS to complete
    // Following a Timer disable, a no_op is required. This was found through
    // exhaustive testing. Without it, g_u16TimerOverflow causes a lock up when
    // being read. See slau445i pg 189 DINT
    // It was found that it's common for uC to require a no op after disabling
    // an interrupt. See the following link:
    //
    //*****************************************************************************

    U32 GetTimerCount(void)
    {
    U32 u32TimerCount = 0;
    U16 u16TimerOverflowCount;

    capture:
    TA0CTL &= (U16) ~TAIE; // Disable overflow interrupt
    __no_operation(); // Necessary to ensure disabled
    u16TimerOverflowCount = g_u16TimerOverflowCount;
    TA0CTL |= TAIE; // Enable overflow interrupt

    // Capture Timer A0 into Capture Register TA0CCR0
    // By creating a rising edge and then reseting
    //
    TA0CCTL0 &= (U16) ~CCIFG; // Clear Capture Flag
    TA0CCTL0 ^= CCIS_1; // Toggle to VCC / GND

    // Wait for Capture/Compare Interrupt Flag (CCIFG) to set
    // before reading the capture register
    //
    while(!(TA0CCTL0 & CCIFG)); // Wait for Capture/Compare Flag
    u32TimerCount = (U32) TA0CCR0; // Copy capture register

    TA0CTL &= (U16) ~TAIE; // Disable overflow interrupt
    __no_operation(); // Necessary to ensure disabled
    if(u16TimerOverflowCount != g_u16TimerOverflowCount) goto capture;
    u32TimerCount |= (U32) (((U32) g_u16TimerOverflowCount) << 16);
    TA0CTL |= TAIE; // Enable overflow interrupt

    return u32TimerCount;
    }

**Attention** This is a public forum