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.

TM4C1230E6PM: TivaWare Timer for Interrupts

Part Number: TM4C1230E6PM

Tool/software:

Hi,

Im trying to make a timer which periodically triggers an interrupt on a TM4C1230E6PM.

Here is the minimal example I came up with:

// --- Includes
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_memmap.h"
#include "driverlib/timer.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"

// --- Typedefs
typedef void (*pFctHandler)(void);

typedef const struct
{
  uint8_t   TIMER_NR;           
  uint32_t  SYSCTL_TIMER;       
  uint32_t  TIMER_BASE;         
  uint32_t  TIMER_CFG;          
  uint32_t TIMER_A_OR_B;        
  uint32_t TIMER_INTERVAL_US;   
  uint32_t INT_FLAGS;           
  pFctHandler callbackFct;      
} timerHW_t;

// --- Local Function Prototypes
void test_tick(void);

// --- Variables
timerHW_t tmr =
{
        .TIMER_NR = 0,
        .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
        .TIMER_BASE = TIMER2_BASE,
        .TIMER_CFG = TIMER_CFG_PERIODIC,
        .TIMER_A_OR_B = TIMER_BOTH,
        .TIMER_INTERVAL_US = 1000, // not implemented
        .callbackFct = test_tick, 
        .INT_FLAGS = TIMER_TIMA_TIMEOUT,
};


int main(void)
{
  // Set Clock
  SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); //80MHz SysClk

  // Enable Interrupts
  IntMasterEnable();

  // Enable the peripheral
  SysCtlPeripheralEnable(tmr.SYSCTL_TIMER);

  // Wait for the module to be ready.
  while(!SysCtlPeripheralReady(tmr.SYSCTL_TIMER))
  {
  }

  // configure Timer 
  TimerConfigure(tmr.TIMER_BASE, tmr.TIMER_CFG);
  TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, SysCtlClockGet());

  // Register the interrupt handler
  TimerIntRegister(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, tmr.callbackFct);

  // Start the timer
  TimerEnable(tmr.TIMER_BASE, tmr.TIMER_A_OR_B);
  TimerIntEnable(tmr.TIMER_BASE, tmr.INT_FLAGS);

  while(1)
  {
    // do nothing
  ;
  }
}

void test_tick(void)
{
  TimerIntClear(tmr.TIMER_BASE, tmr.INT_FLAGS);
  
  // Why is this line needed?
 TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, SysCtlClockGet());
}

I have the following problems with this:

  • Why is the line 81 needed? Should the Timer with TIMER_CFG_PERIODIC not reset itself when load 0 is reached? Without this line I'm basically stuck in test_tick()
  • With Line 81 active, I noticed that the Clock Counts between line 78 is inconsistent. Its often around the expected 80 000 000 (80 000 192) but deviates in between it shows [0 (???), 70 000 000, 68 000 000, 79 000 000]. Shouldn't the timer be very exact in how many Clock cycles happen between calls?

Thanks for any help in advance!

  • Hi,

      Your configuration is inconsistent. See below comments.  

     .TIMER_NR = 0,
    .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
    .TIMER_BASE = TIMER2_BASE,
    .TIMER_CFG = TIMER_CFG_PERIODIC, // This suggests you are concatenating two 16-bit timers into one 32-bit timer 
    .TIMER_A_OR_B = TIMER_BOTH,           // This suggests you are trying to use both the 16-bit timer_A and timer_B, not combined as one 32-bit timer
    .TIMER_INTERVAL_US = 1000, // not implemented
    .callbackFct = test_tick,
    .INT_FLAGS = TIMER_TIMA_TIMEOUT,  // This suggests you want to generate interrupt for timer_A only and only uses timer_A as a 16-bit timer.

    If you want to use timer_A only as a 16-bit timer, you can use the below configuration.

    .TIMER_NR = 0,
    .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
    .TIMER_BASE = TIMER2_BASE,
    .TIMER_CFG = TIMER_CFG_A_PERIODIC | TIMER_CFG_SPLIT_PAIR,
    .TIMER_A_OR_B = TIMER_A,
    .TIMER_INTERVAL_US = 1000, // not implemented
    .callbackFct = test_tick,
    .INT_FLAGS = TIMER_TIMA_TIMEOUT,

    Refer to the Driver User's Guide for details. 

    • Why is the line 81 needed? Should the Timer with TIMER_CFG_PERIODIC not reset itself when load 0 is reached? Without this line I'm basically stuck in test_tick()

    In your configuration you try to enable interrupt for both Timer_A and Timer_B by using TIMER_BOTH without configuration timer_B. I will suggest you first decide which timer (timer_A or timer_B or both timers) in your application and enable/configure the wanted timers accordingly. 

  • Hi Charles,

    Thanks a lot for the reply. This calarifies a lot! I misunderstood TIMER_BOTH to referr to the combined full width timer, where it seems to referr to "both half-width timers"

    If I want to concatenate the two 16-bit timers, would this be the correct configuration?

    .TIMER_NR = 0,
    .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
    .TIMER_BASE = TIMER2_BASE,
    .TIMER_CFG = TIMER_CFG_PERIODIC,
    .TIMER_A_OR_B = TIMER_A,          
    .TIMER_INTERVAL_US = 1000, // not implemented
    .callbackFct = test_tick,
    .INT_FLAGS = TIMER_TIMA_TIMEOUT, 

  • I tried your suggestion with this code:

    // --- Includes
    #include <stdio.h>
    #include <stdbool.h>
    #include <stdint.h>
    
    //from global
    #include "inc/hw_memmap.h"
    #include "driverlib/timer.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/interrupt.h"
    
    // --- Typedefs
    typedef void (*pFctHandler)(void);
    
    typedef const struct
    {
      uint8_t   TIMER_NR;           
      uint32_t  SYSCTL_TIMER;       
      uint32_t  TIMER_BASE;         
      uint32_t  TIMER_CFG;          
      uint32_t TIMER_A_OR_B;        
      uint32_t TIMER_INTERVAL_US;   
      uint32_t INT_FLAGS;           
      pFctHandler callbackFct;      
    } timerHW_t;
    
    // --- Local Function Prototypes
    void test_tick(void);
    
    // --- Variables
    timerHW_t tmr =
    {
            .TIMER_NR = 0,
            .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
            .TIMER_BASE = TIMER2_BASE,
            .TIMER_CFG = TIMER_CFG_A_PERIODIC | TIMER_CFG_SPLIT_PAIR,
            .TIMER_A_OR_B = TIMER_A,
            .TIMER_INTERVAL_US = 1000,
            .callbackFct = test_tick, 
            .INT_FLAGS = TIMER_TIMA_TIMEOUT,
    };
    
    
    int main(void)
    {
      // Set Clock
      SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); //80MHz SysClk
    
      // Enable Interrupts
      IntMasterEnable();
    
      // Enable the peripheral
      SysCtlPeripheralEnable(tmr.SYSCTL_TIMER);
    
      // Wait for the module to be ready.
      while(!SysCtlPeripheralReady(tmr.SYSCTL_TIMER))
      {
      }
    
      // configure Timer 
      TimerConfigure(tmr.TIMER_BASE, tmr.TIMER_CFG);
      TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, 10000);
    
      // Register the interrupt handler
      TimerIntRegister(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, tmr.callbackFct);
    
      // Start the timer
      TimerEnable(tmr.TIMER_BASE, tmr.TIMER_A_OR_B);
      TimerIntEnable(tmr.TIMER_BASE, tmr.INT_FLAGS);
    
      while(1)
      {
        // do nothing
      ;
      }
    }
    
    void test_tick(void)
    {
      TimerIntClear(tmr.TIMER_BASE, tmr.INT_FLAGS);
    }
    

    But I still get very inconsistent Clock Cycle Counts with a "Count Event - Clock Cycles" on Line 80. I would expect it to be close to 10000 every time?

  • I think I need to use TimerLoadSet after each TimerIntClear. If I do it like this:

    // --- Includes
    #include <stdio.h>
    #include <stdbool.h>
    #include <stdint.h>
    
    //from global
    #include "inc/hw_memmap.h"
    #include "driverlib/timer.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/interrupt.h"
    
    
    // --- Typedefs
    typedef void (*pFctHandler)(void);
    
    typedef struct
    {
      uint8_t   TIMER_NR;             //!< A value between 0 and 11 (Timer0 = 0, ..., Timer5 = 5, WTIMER0 = 6, ..., WTIMER5 = 11)
      uint32_t  SYSCTL_TIMER;         //!< system-control of the peripheral
      uint32_t  TIMER_BASE;           //!< TIMER Base
      uint32_t  TIMER_CFG;           //!< TIMER Base
      uint32_t TIMER_A_OR_B;         //!< TIMER_A or TIMER_B or TIMER_BOTH
      uint32_t TIMER_INTERVAL_US;       //!< Timer interval in microseconds
      uint32_t TIMER_INTERVAL_CLOCKCYCLES;        // will be calculated in hal_timer_init
      uint32_t INT_FLAGS;           //!< Interrupt flags
      pFctHandler callbackFct;        //!< Pointer to the interrupt handler
    } timerHW_t;
    
    // --- Local Function Prototypes
    void test_tick(void);
    
    // --- Variables
    timerHW_t tmr =
    {
            .TIMER_NR = 0,
            .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
            .TIMER_BASE = TIMER2_BASE,
            .TIMER_CFG = TIMER_CFG_PERIODIC,
            .TIMER_A_OR_B = TIMER_A,
            .TIMER_INTERVAL_US = 1000,
            .callbackFct = test_tick, 
            .INT_FLAGS = TIMER_TIMA_TIMEOUT,
    };
    
    timerHW_t* ptimerHW = &tmr;
    
    
    int main(void)
    {
      // Set Clock
      SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); //80MHz SysClk
    
      // Enable Interrupts
      IntMasterEnable();
    
      // Enable the peripheral
      SysCtlPeripheralEnable(ptimerHW->SYSCTL_TIMER);
    
      // Wait for the Timer0 module to be ready.
      while(!SysCtlPeripheralReady(ptimerHW->SYSCTL_TIMER))
      {
      }
    
      // configure Timer timing
      TimerConfigure(ptimerHW->TIMER_BASE, ptimerHW->TIMER_CFG);
      ptimerHW->TIMER_INTERVAL_CLOCKCYCLES = (ptimerHW->TIMER_INTERVAL_US * (SysCtlClockGet() / (1000*1000))); 
      TimerLoadSet(ptimerHW->TIMER_BASE, ptimerHW->TIMER_A_OR_B, ptimerHW->TIMER_INTERVAL_CLOCKCYCLES); 
    
      // Register the interrupt handler
      TimerIntRegister(ptimerHW->TIMER_BASE, ptimerHW->TIMER_A_OR_B, ptimerHW->callbackFct);
    
      // Enable the Timer
      TimerEnable(tmr.TIMER_BASE, tmr.TIMER_A_OR_B);
      TimerIntEnable(tmr.TIMER_BASE, tmr.INT_FLAGS);
    
      while(1)
      {
        // do nothing
      ;
      }
    }
    
    void test_tick(void)
    {
      TimerIntClear(tmr.TIMER_BASE, tmr.INT_FLAGS);
      TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, tmr.TIMER_INTERVAL_CLOCKCYCLES);
    }
    

    I do get consistent and as expected clock counts when I set the breakpoint on line 85 (TimerIntClear).

    However, If I do set a breakpoint on line 86 (TimerLoadSet, additionally or as the only breakpoint) I seem to be stuck in test_tick, as the program halts again after ~60 cycles. What is happening here?

    The behaviour is also reversible, when the breakpoint at line 86 is removed it goes back to call test_tick every ~80000 clock cycles.

  • f I want to concatenate the two 16-bit timers, would this be the correct configuration?

    .TIMER_NR = 0,
    .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
    .TIMER_BASE = TIMER2_BASE,
    .TIMER_CFG = TIMER_CFG_PERIODIC,
    .TIMER_A_OR_B = TIMER_A,          
    .TIMER_INTERVAL_US = 1000, // not implemented
    .callbackFct = test_tick,
    .INT_FLAGS = TIMER_TIMA_TIMEOUT, 

    Hi Dominic,

      Yes. this would be the config if you want to combine both timers into one 32-bit timer. 

      Let me also try your pasted code to see what happens. 

  • I do get consistent and as expected clock counts when I set the breakpoint on line 85 (TimerIntClear).

    However, If I do set a breakpoint on line 86 (TimerLoadSet, additionally or as the only breakpoint) I seem to be stuck in test_tick, as the program halts again after ~60 cycles. What is happening here?

    Can you explain me how you are measuring the count? I don't see in your code where you call TimerValueGet to obtain the count. Are you just viewing the register browser to see what the values are? 

  • Hi Charles,

    Thanks for your reply.

    Im measuring the Clock cycles like described here, not directly in the code. I was expecting this value to be directly linked to the TimerLoadSet value, am i wrong?

  • Hi Dominic,

      I never use the Count Cycles method within CCS and don't have any experience with it. I don't think it will give an accurate result. It will be subject to external influence since you are using the debugger to read cycles from one breakpoint to the next breakpoint. What happens if you have a memory window or a register window opened where the debugger needs to read the memory in order to refresh the memory window? There could be other things the debugger is doing behind the scene that may impact its accuracy. 

      There are two methods that I have always suggested customers. First, in the ISR, toggle a GPIO pin. You can measure on the scope if the cycles is accurate per your timeout setting. This is a hardware measurement that will be most accurate in my opinion which would not subject to any tooling inaccuracy.  Ultimately, it is the hardware timer accuracy that you care. The second method will be to simply read the current count value using TimerValueGet() and subtract the old count value from the new count value. If you insist to use the CCS to measure cycles then I will forward your question to the CCS expert. But before I do that, why don't you try the two methods I mentioned. 

  • Hi Charles,

    Thanks a lot for your input. I tried measuing the timing with a GPIO and Oscilloscope and it worked as expected!

    So, as you said, counting cycles can not be used to verify timings.

    For future reference I post here the code I used to toggle the GPIO Pin D6:

    // --- Includes
    #include <stdio.h>
    #include <stdbool.h>
    #include <stdint.h>
    
    //from global
    #include "inc/hw_memmap.h"
    #include "driverlib/timer.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/gpio.h"
    
    
    
    // --- Typedefs
    typedef void (*pFctHandler)(void);
    
    typedef struct
    {
      uint8_t   TIMER_NR;             //!< A value between 0 and 11 (Timer0 = 0, ..., Timer5 = 5, WTIMER0 = 6, ..., WTIMER5 = 11)
      uint32_t  SYSCTL_TIMER;         //!< system-control of the peripheral
      uint32_t  TIMER_BASE;           //!< TIMER Base
      uint32_t  TIMER_CFG;           //!< TIMER Base
      uint32_t TIMER_A_OR_B;         //!< TIMER_A or TIMER_B or TIMER_BOTH
      uint32_t TIMER_INTERVAL_US;       //!< Timer interval in microseconds
      uint32_t TIMER_INTERVAL_CLOCKCYCLES;        // will be calculated in hal_timer_init
      uint32_t INT_FLAGS;           //!< Interrupt flags
      pFctHandler callbackFct;        //!< Pointer to the interrupt handler
    } timerHW_t;
    
    // --- Local Function Prototypes
    void test_tick(void);
    
    // --- Variables
    timerHW_t tmr =
    {
            .TIMER_NR = 0,
            .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
            .TIMER_BASE = TIMER2_BASE,
            .TIMER_CFG = TIMER_CFG_PERIODIC,
            .TIMER_A_OR_B = TIMER_A,
            .TIMER_INTERVAL_US = 50,
            .callbackFct = test_tick, 
            .INT_FLAGS = TIMER_TIMA_TIMEOUT,
    };
    
    timerHW_t* ptimerHW = &tmr;
    uint8_t gpio_state = 0;
    
    
    int main(void)
    {
      // Set Clock
      SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); //80MHz SysClk
    
      // Enable Interrupts
      IntMasterEnable();
    
      // Enable the peripheral
      SysCtlPeripheralEnable(ptimerHW->SYSCTL_TIMER);
    
      // Wait for the Timer0 module to be ready.
      while(!SysCtlPeripheralReady(ptimerHW->SYSCTL_TIMER))
      {
      }
    
      // Setup GPIO (D6 for LED)
      SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
      while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOD))
      {
      }
      GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_6);
    
      // configure Timer timing
      TimerConfigure(ptimerHW->TIMER_BASE, ptimerHW->TIMER_CFG);
      ptimerHW->TIMER_INTERVAL_CLOCKCYCLES = (ptimerHW->TIMER_INTERVAL_US * (SysCtlClockGet() / (1000*1000))); 
      TimerLoadSet(ptimerHW->TIMER_BASE, ptimerHW->TIMER_A_OR_B, ptimerHW->TIMER_INTERVAL_CLOCKCYCLES); 
    
      // Register the interrupt handler
      TimerIntRegister(ptimerHW->TIMER_BASE, ptimerHW->TIMER_A_OR_B, ptimerHW->callbackFct);
    
      // Enable the Timer
      TimerEnable(tmr.TIMER_BASE, tmr.TIMER_A_OR_B);
      TimerIntEnable(tmr.TIMER_BASE, tmr.INT_FLAGS);
    
      while(1)
      {
        // do nothing
      ;
      }
    }
    
    void test_tick(void)
    {
      TimerIntClear(tmr.TIMER_BASE, tmr.INT_FLAGS);
      TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, tmr.TIMER_INTERVAL_CLOCKCYCLES); 
    
      // toggle GPIO Pin
      if (gpio_state==0xFF)
      {
        gpio_state = 0;
      }
      else
      {
        gpio_state = 0xFF; // off
      }
      GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_6, gpio_state);
    }
    

    Thanks for your support!