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.

EK-TM4C1294XL: Trouble reading pulse width (continued)

Part Number: EK-TM4C1294XL

Hi,

So I have already received help from Bob Crosby. Up until bob had helped me I was unable to get any constant reading for a known PWM. Now I do get a constant reading for PWMs with known pulse lengths but I have an irregularity - I thought to convert my pulse length into time I would have to divide by (120*10^6) to get my answer in seconds. This isn't the case, I have to use another constant to get my answer in seconds. It is a good amount off 120*10^6 - closer to 2 million.

If I use that constant I can get an accurate (within error range) pulse length reading when comparing it to oscilloscope (this includes PWMs with different pulse lengths) but I was wondering what could be causing my issue and if there is a possibility of this issue becoming a big problem in the future.

Here is my code relevant to the pulse width (I only want to read the pulse once at the moment):

#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/hw_pwm.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/pwm.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include <string.h>
#include <stdio.h>
#include "driverlib/sysctl.h"

// Global variable declaration for clock
uint32_t g_ui32SysClock;

// Global variable for Pulse
uint32_t  start = 0, end = 0, length = 0;

void Pulse_Length (void)
{
    //
    // Enable GPIO and Timer 0
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

    //
    //Configure GPIO Timer Pins in
    //
    GPIOPinConfigure(GPIO_PD0_T0CCP0);
    GPIOPinConfigure(GPIO_PD1_T0CCP1);
    GPIOPinTypeTimer(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Initialize timer A and B to count up in edge time mode
    //
    TimerConfigure(TIMER0_BASE, (TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP | TIMER_CFG_B_CAP_TIME_UP));
    TimerClockSourceSet(TIMER0_BASE, TIMER_CLOCK_SYSTEM);
    //
    //Timer Load Value
    //
    TimerLoadSet(TIMER0_BASE, TIMER_BOTH, 0xFFFFu);

    //
    // Timer A records positive edge time and Timer B records negative edge time
    //
    TimerControlEvent(TIMER0_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);
    TimerControlEvent(TIMER0_BASE, TIMER_B, TIMER_EVENT_NEG_EDGE);



    // Registers a interrupt function to be called when timer as hits positive edge trig int
    IntRegister(INT_TIMER0A, RisingEdge);
    // Set interrupt to the highest priority
    IntPrioritySet(INT_TIMER0A, 0);
    // Makes sure the interrupt is cleared
    TimerIntClear(TIMER0_BASE, TIMER_CAPA_EVENT);
    // Enable the indicated timer interrupt source.
    TimerIntEnable(TIMER0_BASE, TIMER_CAPA_EVENT);
    // The specified interrupt is enabled in the interrupt controller.
    IntEnable(INT_TIMER0A);

    // Registers a interrupt function to be called when timer as hits neg edge trig int
    IntRegister(INT_TIMER0B, FallingEdge);
    // Set interrupt to the highest priority
    IntPrioritySet(INT_TIMER0B, 0);
    // Makes sure the interrupt is cleared
    TimerIntClear(TIMER0_BASE, TIMER_CAPB_EVENT);
    // Enable the indicated timer interrupt source.
    TimerIntEnable(TIMER0_BASE, TIMER_CAPB_EVENT);
    // The specified interrupt is enabled in the interrupt controller.
    //IntEnable(INT_TIMER0B);

    TimerEnable(TIMER0_BASE, TIMER_BOTH); // needs to come after timer is configured
}

void RisingEdge (void)
{
//    if (PULSE_START == PulseState)
//    {
        TimerIntClear(TIMER0_BASE, TIMER_CAPA_EVENT);
        start = TimerValueGet(TIMER0_BASE, TIMER_A);
//        PulseState = PULSE_POS;
        IntDisable(INT_TIMER0A);
        IntEnable(INT_TIMER0B);
//    }
}

void FallingEdge (void)
{
//    if (PULSE_POS == PulseState)
//    {
        TimerIntClear(TIMER0_BASE, TIMER_CAPB_EVENT);
        end = TimerValueGet(TIMER0_BASE, TIMER_B);
        if(end < start)
        {
            end = end + 0x10000;
        }
        length = end - start;
//        PulseState=PULSE_NEG;
//    }
        //IntEnable(INT_TIMER0A); // This is disabled for acctual pulse
        IntDisable(INT_TIMER0B);
}

int
main(void)
{
    g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                                 SYSCTL_OSC_MAIN |
                                                 SYSCTL_USE_PLL |
                                                 SYSCTL_CFG_VCO_480), 120000000);

    FPUEnable();
    FPURoundingModeSet(FPU_ROUND_POS_INF);    
    Pulse_Length();

    IntMasterEnable();
    while(1)
        {
            SysCtlDelay(40000000);
            UARTprintf("Length = %d\n", length);
        }
}

  • How long is the pulse you measure on the scope? If it is longer than 546uS, then the timer has overflowed.
  • Hi Bob,
    You are absolutely correct, I was stupidly testing with a a pulse length greater than 546us. I also know that I acknowledged that this was the limit last time we talked about the situation, which makes this more embarrassing.

    Now for my final application I will be measuring a pulse within that range (between 3kHz and 6kHz), so I am fine here.

    However, I am curious about this, if I were to increase my limit, I can do this by tracking every time I roll over (aka end<start) and adding 0x10000 for each time? (It seems hard to get the code to increment a variable by one and only one each time the roll over occurs).

  • There are several ways to extend the range of your PWM measurement. You can use the prescaler with the half width timers, you can synchronize and use two 32-bit timers, you can use a slower timer clock source such as PIOSC, or you can keep track of the overflows.

    To keep track of the overflows as you suggest in your post above, enable the TimerB timeout as well as the capture event interrupt. Create an overflow counter in a "volatile static" variable. In the timer A interrupt routine, zero your overflow counter and capture your start value. In the timer B interrupt service routine, check for both the timeout and the capture event. If only the timeout flag is set, increment your overflow counter. If only the capture flag is set, compute the elapsed time as the end time plus 0x10000 times the number of overflows. If both the timeout and the capture event flags are set, you must determine if the captured value is before or after the timeout. A simple check to see if the captured value is greater than 0x8000 should suffice. If the capture value is < 0x8000, increment the overflow counter before computing the elapsed time. Otherwise, compute the elapsed time without incrementing the overflow counter.