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.

Timer in edge count mode

Other Parts Discussed in Thread: TM4C123GH6PM, TM4C129ENCPDT

Hello all!

I am trying to implement a GPSDO (GPS Disciplined Oscillator) using a TM4C123G.

I stumbled on the first step: count (OCXO) clock cycles. I configured WTIMER0A as a edge counter up, this part works, but the prescaler is not incremented when the 32 bit counter wraps.

Thanks in advance.

Edesio

References on GPSDO:

  - initial idea: http://www.febo.com/pipermail/time-nuts/2014-February/082820.html

  - initial code for Arduino: http://www.febo.com/pipermail/time-nuts/2014-February/082834.html

  - revised and simplified code: http://www.febo.com/pipermail/time-nuts/2014-April/084118.html

Environment:

  - board: Tive C Series TM4C123G

  - processor: TM4C123GH6PM

  - Code Composer Studio --  Version: 6.0.0.00190

  - External clock at pin PC4: 10 MHz

Debug output:

Ready>
Timer0A: (0, 17060100)
Timer0A: (0, 136109986)
Timer0A: (0, 261279235)
Timer0A: (0, 825797122)
Timer0A: (0, 1589034047)
Timer0A: (0, 1954483747)
Timer0A: (0, 4281694411)
Timer0A: (0, 119617027)

Code:

/*
* main.c
*/

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "driverlib/rom_map.h"
#include "driverlib/gpio.h"
#include "utils/uartstdio.h"

#define USE_PRESCALER .. /* */

static void
PortFunctionInit(void)
{
int bitValue;

//
// Enable Peripheral Clocks
//
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0);
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);

//
// Enable pin PB6 for TIMER0 T0CCP0
//
MAP_GPIOPinConfigure(GPIO_PB6_T0CCP0);
MAP_GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6);

//
// Enable pin PB7 for TIMER0 T0CCP1
//
MAP_GPIOPinConfigure(GPIO_PB7_T0CCP1);
MAP_GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_7);

//
// Enable pin PB5 for TIMER1 T1CCP1
//
MAP_GPIOPinConfigure(GPIO_PB5_T1CCP1);
MAP_GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_5);

//
// Enable pin PB4 for TIMER1 T1CCP0
//
MAP_GPIOPinConfigure(GPIO_PB4_T1CCP0);
MAP_GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_4);

//
// Enable pin PA0 for UART0 U0RX
//
MAP_GPIOPinConfigure(GPIO_PA0_U0RX);
MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0);

//
// Enable pin PA1 for UART0 U0TX
//
MAP_GPIOPinConfigure(GPIO_PA1_U0TX);
MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_1);

//
// Enable pin PC5 for WTIMER0 WT0CCP1
//
MAP_GPIOPinConfigure(GPIO_PC5_WT0CCP1);
MAP_GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_5);

//
// Enable pin PC4 for WTIMER0 WT0CCP0
//
MAP_GPIOPinConfigure(GPIO_PC4_WT0CCP0);
MAP_GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_4);
}

//*****************************************************************************
//
// Configure the UART and its pins. This must be called before UARTprintf().
//
//*****************************************************************************
static void
ConfigureUART(void)
{
//
// Enable the GPIO Peripheral used by the UART.
//
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

//
// Enable UART0
//
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

//
// Configure GPIO Pins for UART mode.
//
ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

//
// Use the internal 16MHz oscillator as the UART clock source.
//
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

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

int main(void) {
unsigned int val, pre;

FPULazyStackingEnable();

/* set clock to 80 MHz */
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

PortFunctionInit();

ConfigureUART();

#if defined(USE_PRESCALER)
TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_COUNT_UP);
#else
TimerConfigure(WTIMER0_BASE, TIMER_CFG_A_CAP_COUNT_UP);
#endif

TimerControlEvent(WTIMER0_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);

TimerEnable(WTIMER0_BASE, TIMER_A);

UARTprintf("Ready> \n");

while (1)
{
val = TimerValueGet(WTIMER0_BASE, TIMER_A);
pre = TimerPrescaleGet(WTIMER0_BASE, TIMER_A);
UARTprintf("Timer0A: (%u, %u)\n", pre, (val & 0xFFFFFFFF));
}

return 0;
}

  • Hello Edesio

    Thanks for the code post and very clear instructions on the devices (I wish more users post it the way you have). I will look into it and get back.

    Question: Is the USE_PRESCALER defined?

    Regards

    Amit

  • Hello Edesio,

    Please ignore the question. I found it in the code.

    So Analysis

    I ran the code and yes, as you mentioned the Prescaler count never incremented. Reason

    1. The TimerPrescaleGet gets the value of the TAPR register which is the current Prescaler value loaded and not the free running prescaler. The Free running pre scaler is in the TAPV register

    2. For the Edge Count mode you are using, the Match value is 0x0 for Prescaler. So when in Up Count mode the Main counter elapses, the Match for prescaler also happens and the counter rolls over. Instead of you program the Prescalar Match to a non-zero value then you would see the TAPV incrementing as well.

    Regards

    Amit

  • Humble outsider enjoyed & commends poster's carefully constructed, detailed (may I say, "template-like") post.  Well done.

    Note that the edge-count mode imposes a maximum frequency limit upon your application.  (Fmax = 1/4 System Clock)

    The prescaler in edge-count mode, "acts as a timer extension" and (potentially) holds the most significant bits of the count.  Amit did a nice job of identifying a likely, "gotcha" - the (incorrect) setting of, "Prescalar Match" which voids the prescaler.

    In your particular application I'd suggest setting this (more precisely) to a (proper) high, 16 bit value - so that inadvertent/unwanted prescale roll-over/clears do not result...  And - while the wide timer may count up or down - the "up" direction seems so much simpler and intuitive... (prescaler thus contains the number of 32 bit roll-overs - i.e. msb)

    One last comment - as good as your post was - multiple, other timers appeared w/in your code listing.  This forces extra care/attention upon your reviewers - not ideal here - nor in the workplace - where reviewer ease/efficiency is valued...  Superior method may be to, "quarantine" all other timer code - so that, "just the timer under examination" is highlighted for ease of analysis...  (i.e. method borrowed/stolen from surgical, "draping" - show just the area of interest to insure tight focus...)

  • Hi!

    I set the clock frequency to the maximum (80 MHz) to be on the safe side for a 10 MHz OCXO input clock.

    I will load the prescaler match register to 0xFFFF to avoid this roll over problem.

    The code from PortFunctionInit was generated by the PinMux utility and I left it as is. Also I did previous tests using "narrow" timers to check this prescaler "detail" and failed to remove this part.

    Sorry.

    Edesio

  • Hello Amit!

    Thanks for your insight. It worked like a charm!

    I cut, pasted and changed TimerPrescaleMatchGet into this:

    #include "inc/hw_timer.h"

    static uint32_t
    TimerPrescaleValue(uint32_t ui32Base, uint32_t ui32Timer)
    {
    //
    // Check the arguments.
    //
    ASSERT(_TimerBaseValid(ui32Base));
    ASSERT((ui32Timer == TIMER_A) || (ui32Timer == TIMER_B) ||
    (ui32Timer == TIMER_BOTH));

    //
    // Return the appropriate prescale value.
    //
    return((ui32Timer == TIMER_A) ? HWREG(ui32Base + TIMER_O_TAPV) :
    HWREG(ui32Base + TIMER_O_TBPV));
    }

    Changed the main loop to:

    while (1)
    {
    val = TimerValueGet(WTIMER0_BASE, TIMER_A);
    // pre = TimerPrescaleGet(WTIMER0_BASE, TIMER_A);
    pre = TimerPrescaleValue(WTIMER0_BASE, TIMER_A);
    UARTprintf("Timer0A: (%u, %u)\n", pre, (val & 0xFFFFFFFF));
    }

    And got:

    Ready> 

    Timer0A: (0, 23199203)
    Timer0A: (0, 131308671)
    Timer0A: (0, 908645345)
    Timer0A: (0, 1870071501)
    Timer0A: (0, 3190067475)
    Timer0A: (0, 3295295085)
    Timer0A: (0, 3717733072)
    Timer0A: (0, 4102671586)
    Timer0A: (1, 31213275)
    Timer0A: (1, 3544148393)
    Timer0A: (1, 4084836312)
    Timer0A: (2, 165877558)

    Thanks again. Now back to the hardware part :-)

    Edesio

    P.S.: Should I post the whole revised code?

  • Hello Edesio

    Yes, issues when migrating code from narrow timer to wide timer has it's subtleties. Good that we figured it fast to ungate the development.

    Regards

    Amit

  • Hello, I need to count frequency with TM4C129ENCPDT. I tried the above code but I am running in some trouble.


    First, when I do

    #define USE_PRESCALER

    and

    TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_COUNT_UP | TIMER_CFG_B_CAP_COUNT_UP);

    then the whole processor runs about 2x faster! I use TI-RTOS and any Task_sleep() lasts only about half of the specified period. Which is very weird. I use 25MHz crystal and 120MHz cpu frequency. How can Timer configuration affect the system frequency?

    The other problem is with the configuration of timers themselves. I need to use pins T0CCP0, T0CCP1, T2CCP0 and T2CCP1. At each input I have a wave with fixed positive pulse duration of 18us. Maximum expected frequency is about 6kHz. I need to measure those four frequencies.

    For some reason, the last bit in timer A Mode register

    (HWREG(TIMER0_BASE + TIMER_O_TAMR) 
    (HWREG(TIMER2_BASE + TIMER_O_TAMR) 
    

    of T0CCP0 and T2CCP0, which are timer A in both cases, is set to zero, no matter what I do. Per datasheet that means they are configured to 0x2 Periodic TImer mode. I need it to be 0x3 Capture mode.

    The datasheet says at page 1097:

    GPTM Timer A Mode
    The TAMR values are defined as follows:


    Value Description
    0x0 Reserved
    0x1 One-Shot Timer mode
    0x2 Periodic Timer mode
    0x3 Capture mode


    The Timer mode is based on the timer configuration defined by bits 2:0
    in the GPTMCFG register.

    But GPTMCFG is set correctly!

    The B timers are seemingly configured correctly, but they do not count, value is always zero.

    What am I doing wrong?

    The init sequence:

    void EK_TM4C129EXL_initTIMER(void)
    {
    	//#define USE_PRESCALER
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); //IMP1, IMP2
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2); //IMP3, IMP4
    
    	//IMP1
    	GPIOPinConfigure(GPIO_PL5_T0CCP1);
    	GPIOPinTypeTimer(GPIO_PORTL_BASE, GPIO_PIN_5);
    
    	//IMP2
    	GPIOPinConfigure(GPIO_PL4_T0CCP0);
    	GPIOPinTypeTimer(GPIO_PORTL_BASE, GPIO_PIN_4);
    
    	//IMP3
    	GPIOPinConfigure(GPIO_PM0_T2CCP0);
    	GPIOPinTypeTimer(GPIO_PORTM_BASE, GPIO_PIN_0);
    
    	//IMP4
    	GPIOPinConfigure(GPIO_PM1_T2CCP1);
    	GPIOPinTypeTimer(GPIO_PORTM_BASE, GPIO_PIN_1);
    
    	//enable pull-downs
    	/*
    	GPIOPadConfigSet(GPIO_PORTL_BASE, GPIO_PIN_5, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPD);
    	GPIOPadConfigSet(GPIO_PORTL_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPD);
    	GPIOPadConfigSet(GPIO_PORTM_BASE, GPIO_PIN_0, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPD);
    	GPIOPadConfigSet(GPIO_PORTM_BASE, GPIO_PIN_1, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPD);
    	 */
    	//IMP1, IMP2
    	#if defined(USE_PRESCALER)
    	TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_COUNT_UP | TIMER_CFG_B_CAP_COUNT_UP);
    	#else
    	TimerConfigure(TIMER0_BASE, TIMER_CFG_A_CAP_COUNT_UP);
    	TimerConfigure(TIMER0_BASE, TIMER_CFG_B_CAP_COUNT_UP);
    	#endif
    
    	TimerControlEvent(TIMER0_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);
    	TimerControlEvent(TIMER0_BASE, TIMER_B, TIMER_EVENT_POS_EDGE);
    /*
        TimerLoadSet(TIMER0_BASE, TIMER_A, 0xFFFE);
        TimerMatchSet(TIMER0_BASE, TIMER_A, 0x00);
        TimerLoadSet(TIMER0_BASE, TIMER_B, 0xFFFE);
        TimerMatchSet(TIMER0_BASE, TIMER_B, 0x00);
    */
    /*
        TimerPrescaleMatchSet(TIMER0_BASE, TIMER_A, 0xFFFF-1);
    	TimerPrescaleMatchSet(TIMER0_BASE, TIMER_B, 0xFFFF-1);
    */
    	TimerEnable(TIMER0_BASE, TIMER_BOTH);
    
    	//IMP3, IMP4
    	#if defined(USE_PRESCALER)
    	TimerConfigure(TIMER2_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_COUNT_UP | TIMER_CFG_B_CAP_COUNT_UP);
    	#else
    	TimerConfigure(TIMER2_BASE, TIMER_CFG_A_CAP_COUNT_UP);
    	TimerConfigure(TIMER2_BASE, TIMER_CFG_B_CAP_COUNT_UP);
    	#endif
    
    	TimerControlEvent(TIMER2_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);
    	TimerControlEvent(TIMER2_BASE, TIMER_B, TIMER_EVENT_POS_EDGE);
    
    /*
        TimerLoadSet(TIMER2_BASE, TIMER_A, 0xFFFE);
        TimerMatchSet(TIMER2_BASE, TIMER_A, 0x00);
        TimerLoadSet(TIMER2_BASE, TIMER_B, 0xFFFE);
        TimerMatchSet(TIMER2_BASE, TIMER_B, 0x00);
    */
    /*
    	TimerPrescaleMatchSet(TIMER2_BASE, TIMER_A, 0xFFFF-1);
    	TimerPrescaleMatchSet(TIMER2_BASE, TIMER_B, 0xFFFF-1);
    */
    	TimerEnable(TIMER2_BASE, TIMER_BOTH);
    
    }

  • Maybe you are using a timer that the RTOS uses?

    Robert
  • Well, that is possible ... but how can I figure out if it is the case?
  • The resource use should be documented somewhere, if only in the porting layer.

    Robert