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

Part Number: EK-TM4C1294XL

Hi,

I am trying to read the length of a pulse I providing to TM4C. I am having a lot of trouble and I am not sure how to proceed, suggestions would be very helpful. I think I might have a couple issues, If I am connected to ground there is no problem and will print out end = 0, start = zero and length = zero as expected. However, when I connect to my PWM (not the one in the code but one generated by another source) I get random values. I am not sure what is going on or how to fix it. The frequency of the signal I will eventually want to measure is anywhere from 3kHz to 5 kHz. Additionally, I think there might be a problem the order of how its reading my pulses - meaning it might be reading falling and then the rising edge, which inst a problem now as I am using a 50% PWM as my test signal but will be a problem in the future - so if anyone has any suggestions. I was trying to figure out a way to have my clock start at a rising edge but I am having trouble trying to figure out how to do that.

I think that I might be using my interrupts incorrectly, I am confused as how exactly to use them, what I attempted to do was tie PD0 and PD1 together with my incoming pulse, then when a rising edge is detected, timer a will call an interrupt which saves that value to start, then when the negative edge is reached timer b will call an interrupt which saves timer value to end.

Any help would be appreciated. - I kept all code which controls peripherals on the board in the code I post because I am not sure if there may be any interference or something along those lines.

#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"


// Global variable declaration for clock
uint32_t g_ui32SysClock;

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


// Function Prototypes
void configureUART (void);
void Pulse_Length (void);
void DummyDelay(uint32_t Count);
void configurePWM(void);
void FallingEdge (void);
void RisingEdge (void);


//***********************************************************************************************************************//
// This section contains UART communication dependencies - this is used as a check for the code written but will most    //
// be commented out of the final code - it is going to remain as a comment and not deleted to allow editor of program to //
// utilize UART to provide checks of values and functions                                                                               //
//***********************************************************************************************************************//

void
configureUART(void)
{
    //
    // Enable GPIO and UART 0
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); 
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Configure UART Pins for UART MODE
    //
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);


    //UARTClockSourceSet(UART0_BASE, UART_CLOCK_SYSTEM);


    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, g_ui32SysClock);
}

//***********************************************************************************************************************//
// This section will set up the timer to measure the length of the pulse into the controller. I will attempt to achieve  //
// by setting PD0 and PD1 as timer pins. These pins will both be connected to the comparator. PD0 will "look for" the    //
// rising-edge while PD1 will "look for" the falling-edge. Each will capture the timer value to start and end variables  //
// the difference between the two will be the pulse length.
//***********************************************************************************************************************//

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));

    //
    //Timer Load Value
    //
    TimerLoadSet(TIMER0_BASE, TIMER_BOTH, g_ui32SysClock);

    //
    //Synchronize both timers
    //
    TimerSynchronize(TIMER0_BASE, TIMER_0A_SYNC | TIMER_0B_SYNC);

    //
    // 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);
    // 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);
    // 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);
}

void RisingEdge (void)
{
    TimerIntClear(TIMER0_BASE, TIMER_CAPA_EVENT);
    start = TimerValueGet(TIMER0_BASE, TIMER_A);
}

void FallingEdge (void)
{
    TimerIntClear(TIMER0_BASE, TIMER_CAPB_EVENT);
    end = TimerValueGet(TIMER0_BASE, TIMER_B);
}

//***********************************************************************************************************************//
//***********************************************************************************************************************//
//***********************************************************************************************************************//

// DummyDelay() will only be used when debugging
void DummyDelay(uint32_t Count)
{
    volatile uint32_t x;

    for (x = 0;x<Count;x++)
    {
        x = x | 0;
    }
}
//***********************************************************************************************************************//
// This section contains the PWM function needed create the PWM function                                                 //

//***********************************************************************************************************************//
void
configurePWM(void)
{

    uint32_t Ticks;
    Ticks = 50; //Number_Ticks_Freq();
    //
    // Enable the GPIO Peripheral used by PWM (PF0,  PF1)
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

    //
    // Enable PWM0
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);

    //
    //Dividing the clock for PWM use - Will use one for now
    //
    SysCtlPWMClockSet(SYSCTL_PWMDIV_1);

    //
    //Unlocking the pins
    //
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
    HWREG(GPIO_PORTF_BASE + GPIO_O_CR) = 0x01;

    //
    // Configure GPIO pin for PWM
    //
    GPIOPinConfigure(GPIO_PF0_M0PWM0);
    GPIOPinConfigure(GPIO_PF1_M0PWM1);

    GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Configure PWM
    //
    PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);

    //
    //Setting PWM Period
    //
    PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, ceil(Ticks));

    //
    //Setting duty cycle
    //
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0 , ceil(Ticks/2));
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1 , ceil(Ticks/2));

    //
    // Enable PWM
    //
    PWMGenEnable(PWM0_BASE, PWM_GEN_0);
    PWMOutputInvert(PWM0_BASE, PWM_OUT_1_BIT, true);
    PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT, true);

}

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


    configurePWM();
    configureUART();
    IntMasterEnable(); // Enable the processor to respond to interrupts.
    Pulse_Length();

    TimerEnable(TIMER0_BASE, TIMER_BOTH);

    while(1)
    {
        length = end - start;
        UARTprintf("\nSTART = %d\n", start);
        UARTprintf("\nEND = %d\n", end);
        UARTprintf("\nLENGTH = %d\n", length);

    }
}

  • Just to let you know, I am working on your problem. I will give you an updated later today (next 5 hours).
  • Thank you very much, I have been stuck on this for a while
  • OK, here is how I modified your code:

    1. First, you are loading Timer 0 with a maximum count value of 120000000. It is only a 16 bit counter value when split. I changed the load value to 0xFFFF, the maximum for a 16-bit counter.

    2. No need to sychronize the timers, they are started together stay synchronized.

    3. You need to account for when the counter overflows between the rising edge and the falling edge. In that case the value captured for the rising edge is bigger than the value captured for the falling edge. I fixed this by adding 0x10000 to the falling edge value when it is smaller than the rising edge value before calculating the length. Note that this is not sufficient if the pulse length you measure is long enough for the 16-bit counter to overflow a second time.

    4. You need to calculate the length in the falling edge interrupt routine, or at least before the next rising edge occurs. Since you spend more time printing to the terminal than it takes to measure the pulse, the rising edge value captured may be updated before you calculate length in the main routine. I hope this helps you.

    #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"
    
    
    // Global variable declaration for clock
    uint32_t g_ui32SysClock;
    
    // Global variable for Pulse
    uint32_t  start = 0, end = 0, length = 0;
    
    
    // Function Prototypes
    void configureUART (void);
    void Pulse_Length (void);
    void DummyDelay(uint32_t Count);
    void configurePWM(void);
    void FallingEdge (void);
    void RisingEdge (void);
    
    
    //***********************************************************************************************************************//
    // This section contains UART communication dependencies - this is used as a check for the code written but will most    //
    // be commented out of the final code - it is going to remain as a comment and not deleted to allow editor of program to //
    // utilize UART to provide checks of values and functions                                                                               //
    //***********************************************************************************************************************//
    
    void
    configureUART(void)
    {
        //
        // Enable GPIO and UART 0
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    
        //
        // Configure UART Pins for UART MODE
        //
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
        GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
    
        //UARTClockSourceSet(UART0_BASE, UART_CLOCK_SYSTEM);
    
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, g_ui32SysClock);
    }
    
    //***********************************************************************************************************************//
    // This section will set up the timer to measure the length of the pulse into the controller. I will attempt to achieve  //
    // by setting PD0 and PD1 as timer pins. These pins will both be connected to the comparator. PD0 will "look for" the    //
    // rising-edge while PD1 will "look for" the falling-edge. Each will capture the timer value to start and end variables  //
    // the difference between the two will be the pulse length.
    //***********************************************************************************************************************//
    
    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));
    
        //
        //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);
        // 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);
        // 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);
    }
    
    void RisingEdge (void)
    {
        TimerIntClear(TIMER0_BASE, TIMER_CAPA_EVENT);
        start = TimerValueGet(TIMER0_BASE, TIMER_A);
    }
    
    void FallingEdge (void)
    {
        TimerIntClear(TIMER0_BASE, TIMER_CAPB_EVENT);
        end = TimerValueGet(TIMER0_BASE, TIMER_B);
        if(end < start)
        {
            end = end + 0x10000;
        }
        length = end - start;
    }
    
    //***********************************************************************************************************************//
    //***********************************************************************************************************************//
    //***********************************************************************************************************************//
    
    // DummyDelay() will only be used when debugging
    void DummyDelay(uint32_t Count)
    {
        volatile uint32_t x;
    
        for (x = 0;x<Count;x++)
        {
            x = x | 0;
        }
    }
    //***********************************************************************************************************************//
    // This section contains the PWM function needed create the PWM function                                                 //
    
    //***********************************************************************************************************************//
    void
    configurePWM(void)
    {
    
        uint32_t Ticks;
        Ticks = 50000; //Number_Ticks_Freq();
        //
        // Enable the GPIO Peripheral used by PWM (PF0,  PF1)
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    
        //
        // Enable PWM0
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
    
        //
        //Dividing the clock for PWM use - Will use one for now
        //
        SysCtlPWMClockSet(SYSCTL_PWMDIV_1);
    
        //
        //Unlocking the pins
        //
        HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
        HWREG(GPIO_PORTF_BASE + GPIO_O_CR) = 0x01;
    
        //
        // Configure GPIO pin for PWM
        //
        GPIOPinConfigure(GPIO_PF0_M0PWM0);
        GPIOPinConfigure(GPIO_PF1_M0PWM1);
    
        GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Configure PWM
        //
        PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
    
        //
        //Setting PWM Period
        //
        PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, Ticks);
    
        //
        //Setting duty cycle
        //
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0 , (Ticks/4));
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1 , (Ticks/4));
    
        //
        // Enable PWM
        //
        PWMGenEnable(PWM0_BASE, PWM_GEN_0);
        PWMOutputInvert(PWM0_BASE, PWM_OUT_1_BIT, true);
        PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT, true);
    
    }
    
    int
    main(void)
    {
        g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                                     SYSCTL_OSC_MAIN |
                                                     SYSCTL_USE_PLL |
                                                     SYSCTL_CFG_VCO_480), 120000000);
    
    
        configurePWM();
        configureUART();
        IntMasterEnable(); // Enable the processor to respond to interrupts.
        Pulse_Length();
    
        TimerEnable(TIMER0_BASE, TIMER_BOTH);
    
    
        while(1)
        {
            UARTprintf("\nSTART = %d\n", start);
            UARTprintf("\nEND = %d\n", end);
            UARTprintf("\nLENGTH = %d\n", length);
    
        }
    }
    

  • Thank you, I wasn't ever expecting you to actually edit my code, thank you very much.

    I just had a couple extra quick questions:
    Am I thinking this through correctly, but I have 120MHz timer and I am using a 16 bit timer, so my max pulse length I can read will be:
    ( 1/(120*10^6)) *2^16 so about 546.3ms?

    This means that for me to convert my length value to time I would have to multiply that value by ( 1/(120*10^6)) to get my pulse length in nano seconds correct?
  • Hisham Hafez said:
    Am I thinking this through correctly, but I have 120MHz timer and I am using a 16 bit timer, so my max pulse length I can read will be:
    ( 1/(120*10^6)) *2^16 so about 546.3ms?

    Yes.

    Hisham Hafez said:
    This means that for me to convert my length value to time I would have to multiply that value by ( 1/(120*10^6)) to get my pulse length in nano seconds correct?

    Almost, the answer would be in seconds.