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.

TM4C123GH6PM: Edge timer not measuring correct time between rising edges

Part Number: TM4C123GH6PM


I am trying to configure wide timer 0 to calculate the frequency of a pulse train (~1 kHz) using the edge timer function.

Configuration code:

    //
    // Set the clocking to run at 50 MHz from the PLL.
    //
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |
                       SYSCTL_OSC_MAIN);

        /*
     * Configure the input pin (PC4) to use CCP
     */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOC)) {}
    GPIOPinConfigure(GPIO_PC4_WT0CCP0);
    GPIOPadConfigSet (GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPD);
    GPIODirModeSet (GPIO_PORTC_BASE, GPIO_PORTC_BASE, GPIO_DIR_MODE_IN);
    GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_4);

    /*
     * Configure the timer A module of wide timer 0 in rising edge timer mode, configure timer B in periodic mode
     */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_WTIMER0)) {}
    TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME | TIMER_CFG_B_PERIODIC);
    TimerControlEvent(WTIMER0_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);

    /*
     * Set the timer prescale and load value
     */
    TimerLoadSet(WTIMER0_BASE, TIMER_BOTH, 250000);

    /*
     * Setup the interrupts for the timer CCP and timer timeout
     */
    TimerIntRegister(WTIMER0_BASE, TIMER_A, TimerW0AInt);
    TimerIntEnable(WTIMER0_BASE, TIMER_CAPA_EVENT);
    IntEnable(INT_WTIMER0A);

    /*
     * Enable master interrupts
     */
    IntMasterEnable();

    /*
     * Enable the timers
     */
    TimerEnable(WTIMER0_BASE, TIMER_BOTH);

ISR:

    /*
     * Get the type of timer interrupt occurred and then clear it
     */
    TimerIntClear(WTIMER0_BASE, TIMER_CAPA_EVENT);

    /*
     * The CCP edge capture has occurred, calculate the period of the signal if another edge
     * has been captured before this
     */
    uint32_t ui32_snapshot = TimerValueGet(WTIMER0_BASE, TIMER_A);
    if (gui8_edge_flag) {

        int32_t i32_delta;

            // Calculate the time delta
            i32_delta = gui32_last_timer_val - ui32_snapshot;

            /*
             * Check for timer overflow: since the timer is in down-count mode the new snapshot
             * value will be greater than the last if overflow occurred (hence the time delta will
             * be negative. If there is overflow add the timer load value to the difference to
             * calculate the true time delta.
             */
            if (i32_delta < 0) {
                i32_delta += TIMER_PERIOD + 1;
            }

        /*
         * Store the time delta to a buffer
         */
        xSemaphoreTakeFromISR(gp_time_delta_semaphore, NULL);
        circbuf_write(&g_freq_buffer, (uint32_t) i32_delta);
        xSemaphoreGiveFromISR(gp_time_delta_semaphore, NULL);

    } else {
        // Set the flag
        gui8_edge_flag = 1;
    }

    // Update the last value of the timer edge
    gui32_last_timer_val = ui32_snapshot;

I am using a timer load value of 250000, which by my calculations should allow a minimum input frequency of 50MHz / 250000 = 200 Hz. The circbuf_write just adds a value to an array acting as a circular buffer and updates a running sum of the buffer contents (I have had this working with other code). I am using a test square wave input signal (50% DC, 500 Hz, 0-3.5 V) to cause the interrupts. I am reading the value of i32_delta using the debugger and it is fluctuating pretty dramatically: I'm not sure if this is due to the Tiva missing the edges or something else that I am missing. 

  • Harry Mander said:
    I am reading the value of i32_delta using the debugger and it is fluctuating pretty dramatically

    Very nice job of explaining your issue - clearly written narrative & code presentation - well done.     And - this proves your (very first) post here - it must be that you've had 'experience' elsewhere - yet still terrific!

    May I suggest 'KISS' - as the fastest, easiest & most effective, 'Slayer of such Dragons?'

    'KISS' directs that you direct your (initial) focus as follows:

    • confirm (or re-confirm) that your input pulse signal has proper (i.e. w/in MCU spec) 'rising/falling' edges - and is free of 'ringing'
    • has this signal had its 'ground lead' recently measured & confirmed to 'well connect'  to your MCU board's ground?
    • does your signal's 3V5 level exceed that of VDD?     Have you (really) looked at the signal's edges - with the scope set to 'µS/division?'    (to note 'edge transients' which (even) further - exceed VDD)
    • as a consequence of 'Points 2 & 3 (above)' my group most always employs the MCU itself - as the 'Test Signal Generator!'     (eliminates ground failure, illegal signal levels and 'bench intrusion')
    • you note the 'i32_delta' as fluctuating - yet (any) 'delta' - proves a 'KISS' violation.   (as it masks the 'true source' of the delta)    It would be 'more limiting'  (thus insightful) to 'Log & review' the 'TOA' (time of arrival) of each input signal - seeking (only) a consistently (observed) 'delta' - between successive signal edges.     You would simply, greatly increase your Timer's Load Value, and log each captured time to a (sufficiently deep) buffer - for 'after the fact' observation.
    • reviewing buffer content - rather than relying upon the debugger alone - avoids (any) disturbance or variables - which the debugger may introduce
    • it would be best if Timer Pin (PC_4) enjoyed 64 bit (both A/B combined) capability - if (instead) it is 32 bit only - your 250K Load - proves 'beyond a split timer's capability...'

    As illustrated - 'KISS' specifies (very) tight (constituent part) - individual function focus AND measurement - in the gradual AND systematic 'verification' of each piece/process - and only 'integrating/joining' them - (again one at a time) - after each has been successfully test/verified.      (note that vendor has (some) aversion to 'KISS' - instead issuing (tortured, multi-word (even sentence), descriptions) which NEVER prove as: memorable, directing and effective of (dare it be noted) 'KISS!'

  • a few things:

    1. for input capture to work, the timer needs to be free running and roll over at its max value, 0xffff for 16-bit or 0xffffffff for 32-bit, for the math to be simple. setting a top at anything other than 0xffff / 0xffffffff needs more math.

    2. make sure you understand the difference between "load value" and "prescaler";

    3. in the input capture isr, you will need to read the captured value, not the timer counter value.

    hope it helps.
  • Thanks for your great debugging advice. It turns out the timer was working properly and I think the issue was due to a lack of heap space (thanks to FreeRTOS) messing with the circular buffer code (which was created dynamically). Increasing the heap space solved the issue.
  • If I'm not mistaken TimerValueGet does give the captured value. From the datasheet: "When the selected input event is detected, the current timer counter value is captured in the GPTMTnR and GPTMTnPS register and is available to be read by the microcontroller". The return statement for TimerValueGet is:

    return((ulTimer == TIMER_A) ? HWREG(ulBase + TIMER_O_TAR) :
    HWREG(ulBase + TIMER_O_TBR));

    So it appears to be returning the value of the GPTMTAR register.

  • Danny F said:
    3. in the input capture isr, you will need to read the captured value, not the timer counter value.

    Would you believe - that in the 'special case' of (either) 'Edge-Count or Edge-Time' - that  'timer counter' value IS (in fact) captured?    Thus - poster's method is  'perfectly correct!'

    Should 'ammo' (documentation) be required - over-whelming 'evidence' arrives:

    11.3.2.4 Input Edge-Time Mode
    When the selected input event is detected, the current timer counter value is captured in the GPTMTnR and GPTMTnPS register ...

    In this mode, the GPTMTnR and GPTMTnPS registers hold the time at which the selected input event occurred while
    the GPTMTnV and GPTMTnPV registers hold the free-running timer value and the free-running prescaler value.

    Each time a (signal) edge event is detected, the current count value is loaded into the GPTMTnR and
    GPTMTnPS registers, and is held there until another (signal) edge is detected ...

    Register 19: GPTM Timer B (GPTMTBR), offset 0x04C
    This register shows the current value of the Timer B counter in all cases  except for Input Edge Count and Time modes.   In the Input Edge Count mode, this register contains the number of edges that have occurred.   In the Input Edge Time mode, this register contains the time at which the last edge event took place.

    Thus - while your suggestion (or hope) was (generally) correct - it 'missed' the 'special conditions' -  imposed by 'Edge-Time Mode.'      And 'No such 'miss' - was suffered by our poster!

    It may be noted that the best  'antidote to hope' ...  proves a  'properly focused - read/review' - of the MCU Manual...

  • Thank you for your kind & generous words - always appreciated.     (especially - as/if  'earned')

    Your method indeed - despite (another's) protest - proved sound & succeeding.      And your 'defense of your method' - would 'Convince any jury' - very well done!     (again)