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.

Captured timing incorrect - MSP430FR5969 (launchpad)

Other Parts Discussed in Thread: MSPWARE, MSP430WARE

I did use the Search function but did not find a suitable answer. I apologize in advance if I didn't search deep enough.

I am debugging a modified example project, msp430fr59xx_ta0_capture. It is running and I am getting the capture numbers. What I can't figure out is why are the numbers off? When VLO is used as ACLK source, I am getting 97-98 counts per capture with a 1 MHz SMCLK. Sounds about right; VLO is not exacly 10 kHz so who knows what should VLO/SMCLK really be? To verify the capture timing I configured ACLK to use LFXT, and the SMCLK to 8 MHz for better resolution, like so:

P3DIR |= BIT4;
P3SEL1 |= BIT4; // Output SMCLK so I can scope it

PJSEL0 = BIT4 | BIT5; // For XT1
// Clock System Setup for LFXT
CSCTL0_H = CSKEY >> 8; // Unlock CS registers
CSCTL1 = DCOFSEL_6; // Set DCO to 8 MHz
CSCTL2 = SELA__LFXTCLK | SELS__DCOCLK | SELM__DCOCLK;
CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1; // Set all dividers
CSCTL4 &= ~LFXTOFF;
CSCTL0_H = 0x00; // Lock CS module (use byte mode to upper byte)

The project runs just fine but the captured numbers are 213-214 counts apart. They should be 8000000 / 32768 = 244 counts apart. The difference is like 13%! I started thinking, cycle count for ISR etc but that shouldn't matter, the timer counting is done in hardware, is continuous and independent of the CPU, and should go on in the background, and TA0CCR2 should update at rate 32768Hz counting at 8MHz.

Both clocks were verified using a scope, and they are indeed 32.768kGz and 7.998MHz. What is happening?

Thanks!

-P

  • Thank you to all who took a look at my question.

    I have a couple more data points to help find the answer. I changed the ACLK divider and the missing counts are directly related to the counter trigger frequency, they are not constant (which one would expect if that was the result of ISR clock cycles somehow). Here is what I get for 8 Mhz SMCLK counter base clock:

    ACLK ACLK div Expected Actual Difference
    32768 1 244 214 30 counts
    16384 2 488 428 60 counts
    8192 4 977 857 120 counts

    Thus, the missing counts are 30*DIVA.

    I tried changing the counter base clock (SMCLK) to 8 and to 4 Mhz. The missing count number changes proportionally:

    ACLK ACLK div Expected Actual Difference
    32768 1 122 107 15 counts
    16384 2 244 214 30 counts
    8192 4 488 428 60 counts

    When SMCLK is 1 Mhz, the values change by half again (also, at DIVA=1 sometimes the ISR can't keep up with the counter, and double counts at 54 but that now would be attributable to clock cycles needed to process the ISR, and is Ok):

    ACLK ACLK div Expected Actual Difference
    32768 1 31 27 4 counts
    16384 2 61 54 7 counts
    8192 4 122 107 15 counts

    The time corresponding to the missing counts is the same for all SMCLK speeds and is quite substantial:

    3.77E-06 s
    7.54E-06 s
    1.49E-05 s

    Does anyone have any ideas?

  • It appears that nobody wants to offer an idea.

    I am going to attach the code file. Perhaps someone can point out to me what the problem may be, or download it and see if it can be duplicated on another device. I would be very curious to find out what causes the lost counts I am observing.

    To reiterate: the attached code runs TA1 at 8MHz SMCLK and captures ACLK pulses from 32,768 Hz LFXT divided by 4 into TA1CCR2. The data table for these frequencies looks like this:

    SMCLK TA1 ACLK ACLK div Expected Actual Difference
    8.0E+06 8192 4 977 857 120 counts

    I would expect to capture 977 pulses. When I examine timerA0captureValues in debugger in the TA1IV_TA1CCR2 select statement, I see 857-858 counts. My next step will be to run one of the counters in PWM mode and use its output to trigger capture in another counter, and use the same clock to generate PWM and to provide base clock for the capture counter. The captured counts had better be the same as the PWM width setting...

    Thanks!

    -P

    msp430fr59xx_ta1_IntCapt.c
    /* --COPYRIGHT--,BSD_EX
     * Copyright (c) 2012, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     *******************************************************************************
     *
     *                       MSP430 CODE EXAMPLE DISCLAIMER
     *
     * MSP430 code examples are self-contained low-level programs that typically
     * demonstrate a single peripheral function or device feature in a highly
     * concise manner. For this the code may rely on the device's power-on default
     * register values and settings such as the clock configuration and care must
     * be taken when combining code from several examples to avoid potential side
     * effects. Also see www.ti.com/grace for a GUI- and www.ti.com/msp430ware
     * for an API functional library-approach to peripheral configuration.
     *
     * --/COPYRIGHT--*/
    //***************************************************************************************
    //  P. Thanigai
    //  Texas Instruments, Inc
    //  August 2012
    //  Built with IAR Embedded Workbench V5.30 & Code Composer Studio V5.5
    // This TI example code has been further modified. TI is not respnsible for this code.
    //***************************************************************************************
    #include "msp430.h"
    
    #define NUMBER_TIMER_CAPTURES       10
    
    volatile unsigned int timerA0captureValues[NUMBER_TIMER_CAPTURES];
    unsigned int timerA0capturePointer = 0;
    unsigned int prevValue0 = 0;
    
    int main(void)
    {
      WDTCTL = WDTPW | WDTHOLD;                 // Stop watchdog timer
    
      // Configure GPIO
      P1OUT &= ~BIT0;                           // Clear P1.0 output
      P1DIR |= BIT0;                            // Set P1.0 to output direction
    
      P3DIR |= BIT4;
      P3SEL1 |= BIT4;	// Output SMCLK so it can be scoped
    
      PJSEL0 = BIT4 | BIT5;                     // For XT1
    
      // Disable the GPIO power-on default high-impedance mode to activate previously configured port settings
      PM5CTL0 &= ~LOCKLPM5;
    
      // Clock System Setup for LFXTL
      CSCTL0_H = CSKEY >> 8;		// Unlock CS registers
      CSCTL1 &= DCOFSEL_6;			// Set DCO to 8 MHz
      CSCTL2 = SELA__LFXTCLK | SELS__DCOCLK | SELM__DCOCLK; // ACLK=LFXT, SMCLK=DCO, MCLK=DCO
      CSCTL3 = DIVA__4 | DIVS__1 | DIVM__1;		// Set all dividers
      CSCTL4 &= ~LFXTOFF;			// Turn On LFXT
      CSCTL0_H = 0x00;				// Lock CS module (use byte mode to upper byte)
    
      __delay_cycles(100000);		// Allow clock system to settle
    
    
      // Timer1_A3 CCR2 Setup:
      // Capture rising edge, Use CCI0B=ACLK, Synch capture, capture mode, Enable capture interrupt
      TA1CCTL2 = CM_1 | CCIS_1 | SCS | CAP | CCIE;
    
      // Timer1_A3 CCR0 Setup:
      // Capture rising edge, Use CCI0B=P2.4, Synch capture, capture mode, Enable capture interrupt
    //  TA1CCTL0 = CM_1 | CCIS_1 | SCS | CAP | CCIE;
    
      //Global TA1 settings:
      TA1CTL = TASSEL__SMCLK | MC__CONTINUOUS;  // ACLK as clock source, continuous mode, input divider=8
    
      __enable_interrupt();
    //  __bis_SR_register(LPM0_bits | GIE); // Enter LPM0, watch interrupts
    
      while (1) {
      }
    }
    
    // Timer0_A0 CCR0 Interrupt Handler
    //#pragma vector = TIMER1_A0_VECTOR
    //__interrupt void Timer1_A0_ISR(void)
    //{
    //    while (1) {
    //      P1OUT ^= 0x01;                    // Toggle P1.0 (LED)
    //      __delay_cycles(1000000);
    //    }
    //}
    
    // Timer0_A3 CC1-4, TA Interrupt Handler
    #pragma vector = TIMER1_A1_VECTOR
    __interrupt void Timer1_A1_ISR(void)
    {
      switch (__even_in_range(TA1IV, TA1IV_TAIFG)) {
        case TA1IV_TA1CCR1:
            while (1) {
              P1OUT ^= 0x01;                    // Toggle P1.0 (LED)
              __delay_cycles(2000000);
            }
    //      break;
        case TA1IV_TA1CCR2:
          timerA0captureValues[timerA0capturePointer++] = TA1CCR2 - prevValue0;
          prevValue0 = TA1CCR2;
          if (timerA0capturePointer >= NUMBER_TIMER_CAPTURES) {
            while (1) {
              P1OUT ^= 0x01;                    // Toggle P1.0 (LED)
              __delay_cycles(1000000);
            }
          }
          break;
        case TA1IV_TA1IFG:
            while (1) {
              P1OUT ^= 0x01;                    // Toggle P1.0 (LED)
              __delay_cycles(3000000);
            }
    //      break;
        default:
          break;
      }
    }
    

  • PaulDaisy said:
    It appears that nobody wants to offer an idea.

    It’s not they don’t want but these cases can be complex and time consuming, and most of these timing issues are user programming faults and not MCU. Posting your full code helps to reduce investigation time.

    And as I already told you my times is currently also limited, therefore I didn’t look for now to your initialization code, I hope you have followed the example.

    But one thing my eye catches is the use of TA1CCR2. As I already explain you FRAM isn’t fast and you need to optimize his resources. Now you using only one interrupt and is the order not very critical but TA1CCR0 has higher priority than TA1CCR2 and TB0CCR0 the highest. Doing time critical operations make use of this!

    In your ISR in each ‘case’ you use an endless ‘while’ in my opinion you will never come out here unless you put a break inside the ‘while’ but on the other hand why ‘while’?

    But then when you exit the while in case = TA1IV_TA1CCR1 you continue with case = TA1IV_TA1CCR2 unless you uncomment the break.

  • Hi Leo,

    My "nobody wants to offer an idea" was not meant to sound presumptuous as if someone - including you Leo - is obligated to do so. I appreciate any and all advice, and if there is none I will try to plug away on my own. If I find a solution I will post it for others' benefit.

    The code is directly from the TI example. The only thing different is the capture clock source, instead of VLO it uses LFXT.

    My understanding is, FRAM speed should not matter here. It would only matter if I tried to process interrupts faster than captures occur. The capture is done in hardware - the counter stores the value in TA1CCR2 at its own speed, up to 16MHz, and moves on happily. Whenever that value is retrieved by ISR is not relevant since it does not change anymore. That is the beauty of Capture - as long as the next capture does not happen before the prior one is retrieved, it can be read at any time.

    But even if it is missed, in this application the next Capture should record the same value - it records time between rising edges of the same clock.

    On the last point - the ISR would never exit the TA1IV_TA1CCR1, it is an infinite loop trap. Same for TA1IV_TA1IFG. Neither of these events should ever occur in this application, so these traps would tell me if anything unexpected happens. The only one that does occur is TA1IV_TA1CCR2, and that one will store NUMBER_TIMER_CAPTURES in an array, then get trapped in the infinite loop, blinking the LED, at which point one can pause the debugger and inspect the values in timerA0captureValues.

    In fact if you un-comment the Breaks in the two other Selects, the compiler will issue a warning that the Break statement is not reachable.

  • Ok, understand. I wouldn’t use this approach so it looks a little bit strange to me, but everybody has its own –different- way.

    Calculating your results it looks like you are using the VLO instead of LXFT, this can happen when the LFXT doesn’t start, ACLK defaults then to VLO.

    Checking your clock setup I notice that you are not Testing & Clearing the oscillator fault flags. Add this but before you are locking CS.

  • Surely not the VLO, that's typically 9.4kHz.

    The FR59xx user's guide says that ACLK defaults to LFMODCLK if LFXT doesn't start. That's sourced from MODOSC/128, and with a typical MODOSC frequency of 4.8MHz that gives 37.5kHz. That does indeed match with the timing results seen by Paul, though I'm not sure how this can be happening given that he verified the clocks as 32.768kGHz and 7.998MHz. Was the LFXT measurement taken direct from the crystal rather than by routing ACLK to an external pin?

  • Paul,

    I agree with you. What you observed is very puzzling. I suspect that you may have discovered a hardware bug. One possibility is, SMCLK some how is not working right when you use the Timer this way.

    PaulDaisy said:
    ... The only one that does occur is TA1IV_TA1CCR2, and that one will store NUMBER_TIMER_CAPTURES in an array, then get trapped in the infinite loop, blinking the LED, at which point one can pause the debugger and inspect the values in timerA0captureValues....

    In this case, you could replace the infinite loop of blinking LED by simply reset the array pointer to 0. Doing so will enable you to observe the SMCLK (and ACLK too, if you set it up ahead of time) on the scope while Timer is doing this. When ACLK is set up at 32.8 kHz, is it still 32.8 now? When SMCLK is set up at 1.00 MHz, is it still 1.00 now? Does it stop for few cycles every 30 or 31 cycle?

    Another possibility is, the Timer missed a few count. I have to think about how to verify that.

    --OCY

  • Robert, I think you nailed it. I measured the 32.768 on the crystal. The launchpad I have does not expose either ACLK pass through pin (PJ.2 or P2.0) on the header. I was surprised to see that the crystal is putting out only mV level of signal but it is steady and otherwise fine.
    I bet I am not driving the crystal properly, and the ACLK defaults to LFMODCLK.
    Thank you again, this does look like the smoking gun. I will verify it and post the result.
  • Robert, you nailed it. I measured the 32.768 on the crystal. The launchpad does not expose either ACLK pin (PJ.2 or P2.0) on the header and I didn't try to measure it on the chip. The crystal put out a nice 200 mV sine wave, so I got content with that, not considering that it may not get through the clock system, which then reverted to fail-safe mode.

    I configured P2.0 to output ACLK and confirmed that it, indeed, was 37.3kHz. One needs a magnifying glass to even figure out where pin 1 is on this package, let alone hit the pin without shorting out others :(

    The reason for the problem was not clearing crystal fault. The xtal obviously started eventually but since I didn't tell the CPU to wait out the start-up, it must have issued a fault and reverted to LFMODCLK. I added the fault check:

    do
    {
    CSCTL5 &= ~LFXTOFFG; // Clear XT1 fault flag
    SFRIFG1 &= ~OFIFG;
    } while (SFRIFG1&OFIFG); // Test oscillator fault flag

    and the capture is now working as expected. I am getting the correct counts from the ACLK. Beer is on me Robert, when you are in CO :)

    Thanks very much to all who posted! I greatly appreciate your taking the time to help me understand the issue.

    -P

  • Sorry, it’s so common for the MSP this VLO but FRAM has a bit more, also the User’s Manual is probably in error and writes VLOCLK in table 3-6. In this post my VLO must be read as MODCLK. All other, calculation and solution, is pointing directly to the actual problem.

  • It can happen that the XT oscillator ‘hicks’ and the fault flag will be set again and ACLK will move away from XT and will be sourced again from MODCLK, therefore add a oscillator fault ISR to handle this and correct ACLK to the proper source, otherwise you will be back soon on this forum with a new ACLK problem.
  • Thanks Leo, I already made a mental note to do so, and put it in my ISR so that when I reuse the code it will hopefully be apparent what it is supposed to do. While at it, I also added the same for the HFXT that I intend to use in the actual application.
    I rarely repeat the same mistake more than twice. However, my personal NVM write cycles are probably approaching the MTBF count so don't be surprised if I do exactly what you are saying :)
    Best regards,
    -P
  • As you all said, this is a pretty common thing to get wrong. I highly recommend using MSPware Driver Library to program the clocks. Their functions help minimize these types of errors. (In fact, I use DriverLib for just about everything - but it should really be a must for a few items, such as clocks and writing to flash.)

    FWIW,
    Scott
  • I have looked into MSPware library. I did not see a good reference manual for the functions and all examples that are built into the CCS are not using the library. I wouldn't mind learning although using c examples relates directly to the user guides, while the driver library does not.

    I had bad experiences with driver libraries in the past when the drivers for PC-104 AD board I used had bugs, and I had to figure them out before I dropped the drivers and just wrote it myself, so I am a little hesitant. Fortunately those drivers had source code so I was able to actually see that they were wrong before I compiled them. Not so with TI DriverLib - you have to trust them. If something fails to work in c code there is a chance to track it. If a driver lib fails, it is harder for me to pin it to the failing point, if it is inside a library.

    How does the code size compare in driver lib vs coding direct? I noticed that when I used Grace to initialize some smple PWM the code size was almost twice as large that when using c code. Unless the Grace code did other things that I didn't ask for. I am not an efficiency freak but that seems excessive.

  • For the same reason/experience I avoid using libraries and have made a lot my own.
  • PaulDaisy said:

    Fortunately those drivers had source code so I was able to actually see that they were wrong before I compiled them. Not so with TI DriverLib - you have to trust them. If something fails to work in c code there is a chance to track it. If a driver lib fails, it is harder for me to pin it to the failing point, if it is inside a library.

    DriverLib source code is available from TI on this page: http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430_Driver_Library/latest/index_FDS.html. It's included in the "BSD licensed driver library only" file msp430_driverlib_1_97_00_19.zip. It may also be included in the version bundled into CCS and MSPWare if you know where to look.

  • I suppose my apprehension towards the libraries is not justified with TI. I have not had bad experience with these, so probably shouldn't compare them to another manufacturer's not so good ones. If a TI employee is using them I would think they are good enough for me.

    Could someone say how does the code size compare between straight code and library code? For something simple like PWM or ADC, or comparator, or FRAM - large and complex code is too hard to compare since it can be written in a variety of ways.

    Is there a MSPLib guide somewhere with a good summary of functions? It would be nice if they were by peripheral or function to get to learn them quicker.

    Thanks to all who responded.

    -P

  • In general, using library functions instead of individual instructions leads to lots of function calls with all their overhead. Also, library code usually needs to be generic, so it does more than it needs in your specific situation. The driver library, as well as MSP430ware, are good for beginners, but less suitable for advanced users or projects where code size or execution speed are critical. But since they are available as source code, they give good starting points for building own code - once you know what the library functions do, you can do it on your own, tailored to your application.

**Attention** This is a public forum