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: Using Timer for delay function

Part Number: TM4C123GH6PM
Other Parts Discussed in Thread: EK-TM4C123GXL

Hello everyone,

I'm using EK-TM4C123GXL launchpad board and trying use Timer0 as a delay function but having a strange problem and I don't quite understand why my code doesn't work. The code is very simple as shown below:

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"


//*****************************************************************************
//
// Delay ms
//
//*****************************************************************************
void Delay_ms(uint16_t ui16ms)
{
    TimerEnable(TIMER0_BASE, TIMER_A);

    while (ui16ms--)
    {
        while (!TimerIntStatus(TIMER0_BASE, TIMER_A))
        {
            TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
        }
    }

    TimerDisable(TIMER0_BASE, TIMER_A);
}

//*****************************************************************************
//
// Configure Timer0A as a 32-bit periodic 
//
//*****************************************************************************
int
main(void)
{
    
	//
	// Config system clock for 80MHz using PLL with external 16MHz oscillator
	//
	SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                   SYSCTL_XTAL_16MHZ);

    //
    // Enable Peripheral Clocks for port PB
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
	
    //
    // Enable pin PB for GPIOOutput
    //
    GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_0);
    GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_1);
    
    //
    // Wait for PB is ready
    //	
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB));	

    //
    // Enable The Timer0 peripheral.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
	
	//
	// Wait for Timer0 is ready
	while(!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0));
	
	//
	// Config Timer0 32 bit periodic
	//
    TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
	//
	// Preload Timer0A for 1ms timeout
	//
    TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet() / 1000);


    //
    // Configure the Timer0A interrupt for timer timeout.
    //
    TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

    //
    // Enable Timer0A.
    //
    TimerEnable(TIMER0_BASE, TIMER_A);

    //
    // Loop forever while the Timer0B runs.
    //
    while(1)
    {
		//
		// Toggle port PB0 every 50ms
		//
		GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, 1);
        Delay_ms(1);
        GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, 0);
        Delay_ms(1);
    }
}

The problem I have is the Timer0 seems to overflow much faster than I expected. I used the scope to look at the port pin PB0 and see it toggle around 1.5us instead of 1ms. The system clock is running at 80MHz. I checked the Timer0 TIMER_TAILR register and the preload value is correct with 0x00013880 which is 80000 in decimal. Would you all please look into my code and tell me where my mistakes are?

Thank you all in advance and have a wonderful Mon.

Best regards,

TLN 

  • Hello TLN,

    I can help with looking at the timer but before I do, is there a particular reason you want to use to use a timer over other options? A lot of delays are implemented with the SysCtlDelay API.

    This would be the equivalent of a 1 ms delay:

    SysCtlDelay(SysCtlClockGet() / (1000 / 3));

    If that works, it would be simpler for you. But if you need the timer for a specific reason then I can help look into that.

    Best Regards,

    Ralph

  • Hello Ralph,

    Thank you for your fast reply.

    The reason I try to use the timer instead of the SysCtlDelay() function in the Tiva library is to have better accuracy of the delay learn how to use the the timer in different ways. Did you see anything wrong with my code?

    I tried your suggestion and the PB0 did not toggle at the 1ms rate but 18ms instead, even though the TIMER_TAILR register had value of 0x0003AA70 = 240240 decimal which corresponding to 3ms.

    Please correct me if I'm wrong here, the SysCtlDelay() takes 3 machine cycles. With 80Mhz system clock, each cycle take 12.5ns, and the correct value to load into SysCtlDelay() for 1ms delay is 26666. But if I use this value then the toggle rate is 2ms. If I use 13333 then the toggle rate is correct. I'm confused now!!!

  • Hello TLN,

    I would fully disable the interrupt for the timer before trying the SysCtlDelay.

    I will try your timer code next chance I get to see how it is running.

    Best Regards,

    Ralph

  • But if I use this value then the toggle rate is 2ms.

    Also for this, you are see 2 ms on, 2 ms off? Or the full period is 2 ms with 1 ms on and 1 ms off?

  • Ralph,

    With SysCtlDelay(26666) I had 2ms on and 2ms off.

    With SysCtlDelay(13333) I had 1ms on and 1ms off.

  • Commented out this line

    //    TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

    Still the same using 

    TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet() / (1000/3));

    Only SysCtlDelay(13333) give correct 1ms on and off regardless of the function TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT) is used or not.

  • The 2nd argument of TimerIntStatus is a bool documented as:

    bMasked is false if the raw interrupt status is required and true if the masked interrupt status is required.

    The following in the Delay_ms() function loop passed TIMER_A as the bMasked parameter to TimerIntStatus() which looks wrong:

            while (!TimerIntStatus(TIMER0_BASE, TIMER_A))
            {
                TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
            }

    However, TIMER_A is defined as 0xff which should cause TimerIntStatus to return the masked interrupt status which is probably what the code wants.

    Since TimerIntStatus returns non-zero if any masked interrupt is pending should the while loop be masking the return from TimerIntStatus to only check the required interrupt TIMER_TIMA_TIMEOUT?

    I.e. if there is any other masked interrupt pending won't the while loop terminate without waiting for the required interrupt.

    Also, I think the timer interrupt should only be cleared once has occurred.

    Try writing the loop as:

            while ((TimerIntStatus(TIMER0_BASE, true) & TIMER_TIMA_TIMEOUT) == 0)
            {
            }
            TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

  • Hello TLN,

    I tested Chester's code and he is spot on. Please follow his suggestion.

    Thanks Chester! Slight smile

    Best,

    Ralph

  • Hello Chester,

    I've tried your code suggestion and it worked beautifully Joy. I'll look into the library and try to understand it more in depth how to use them more properly.

    Thank you very much and much appreciated for your help. 

  • Hello Ralph,

    Yes, I've tried and it worked. Thank you very much for your help! Joy