Timers

The MSPM0G3507 contains many timers. This HAL module is designed to interface with TI General Purpose timers, denoted as TIMGx, and TI Advanced timers, denoted as TIMAx, where the x in each identifies specific timers. A summary of the existing timers is given below; advanced features of each timer are not shown.

General Purpose and Advanced Timers within the MSPM0G3507.

Timer

Size

Prescaler

CCRs

TIMG0

16-bit

8-bit

2

TIMG6

16-bit

8-bit

2

TIMG7

16-bit

8-bit

2

TIMG8

16-bit

8-bit

2

TIMG12

32-bit

none

2

TIMA0

16-bit

8-bit

4

TIMA1

16-bit

8-bit

2

All of these timers are capable of operating in three basic modes:

  • Down Mode: The timer counts down from a specified value to 0, resets to the original specified value, and repeats.

  • Up Mode: The timer counts up from 0 to a specified value, resets to 0, and repeats.

  • Up Down Mode: The timer counts up from 0 to a specific value then counts back down to 0 and repeats.

These modules are also capable of performing Capture and Compare operations during counting. A Capture or Compare operation is tied to one of five Capture Compare Registers, or CCRns, where n is the CCR register, and may be tied to alternate functionality of a GPIO pin; for example, to produce a Pulse Width Modulated (PWM) output. Each CCR has one input/output signal: TIMGx_CCPn or TIMAx_CCPn; where CCP stands for Capture Compare Pin. These may be referred to as simply CCPns for short; with the assumption that they match to the corresponding timer modules in context. Connection of the CCPn signals to GPIO pins is discussed Peripheral Functions.

The first argument for all EmCon HAL functions associated with these timers, GPTIMER_Regs *timer, is the timer module to use: TIMGx or TIMAx; matching the labeling as given in the table above. The TIMGx and TIMAx are defined as pointers; therefore, no & is required for this argument.

Timer Base Configuration

Configuration of the timer’s counting behavior requires the creation of a Timers_TimerConfig type struct. The table below shows the fields for this configuration struct.

Timers_TimerConfig struct fields

Field

type

Possible Values

.mode

TIMER_MODE (enum)

TIMER_MODE_ONE_SHOT_DOWN

TIMER_MODE_PERIODIC_DOWN

TIMER_MODE_ONE_SHOT_UP

TIMER_MODE_PERIODIC_UP

TIMER_MODE_ONE_SHOT_UP_DOWN

TIMER_MODE_PERIODIC_UP_DOWN

.clksrc

TIMER_CLOCK_SRC (enum)

TIMER_CLOCK_BUSCLK

TIMER_CLOCK_2X_BUSCLK

TIMER_CLOCK_MFCLK

TIMER_CLOCK_LFCLK

TIMER_CLOCK_DISABLE

.clkdivratio

TIMER_CLOCK_DIV (enum)

TIMER_CLOCK_DIVIDE_x

where x may be 1-8

.clkprescale

uint8_t

0 to 255

.period

uint16_t

0 to 65535

Field Descriptions:
  • .mode set the counting mode of timer. Each timer is capable of counting in DOWN mode, UP mode, and UP_DOWN mode. Additionally, for each of these modes, the timer can be set to count only once and then stop, using _ONE_SHOT_, or count continuously, using _PERIODIC_. The _ONE_SHOT_ option is not used in this class.

  • .clksrc indicates what the timer module should use as its counting base. For the purposes of this course, we will typically select BUSCLK, which is set to be 32 MHz.

  • .clkdivratio allows for the selected .clksrc to be “divided” in frequency. For example, if this field was set to TIMER_CLOCK_DIVIDE_4, the timer would would increment at a rate of 32 MHz / 4 = 8 MHz.

  • .clkprescale allows for the divided clock (determined from .clkdivratio) to be further divided. The effective divider used for this instance is the value of the field +1. This number is of type uint8_t and can take any value in that range.

  • .period specifies the maximum number that the module will count to before resetting in UP or UP_DOWN modes. In DOWN mode, this specifies the number at which to start counting. This number is of type uint16_t and can take any value in that range. Note that TIMG12 is capable of counting over a 32-bit range as opposed to 16-bit range. This range is not supported for the current version of the HAL; with TIMG12 being artificially limited to a 16-bit range.

With the configuration struct filled in, the timer can be initialized with the function:

void Timers_initTimer( GPTIMER_Regs *timer , Timers_TimerConfig *config )

The first argument to this function, GPTIMER_Regs *timer, is the timer module to use (see above). The second argument is the configuration struct as described above. Note that the * within the argument definitions indicates that the arguments must be pass as pointers, which is simply done by adding a & before the struct variable name to be passed; however, this is not required for the TIMGx and TIMAx values as they are already defined as pointers.

An example usage of this function is provided below, where the required configuration struct (of type Timers_TimerConfig) is created and (partially) populated. The configuration is then applied by the function. The configuration struct must be specified prior to calling Timers_initTimer; changing the values of the example tmr_cfg after the function has been called will have no effect.

Timers_TimerConfig tmr_cfg;    // Initialize timer configuration struct
tmr_cfg.mode = TIMER_MODE_PERIODIC_DOWN; // Set mode to down counting and repeating
tmr_cfg.clksrc = ...;
... // Configure the rest of the fields within the configuration struct
Timers_initTImer(TIMG0,&tmr_cfg); // Apply the configuration
... // Start the timer

Capture Compare Registers

Each Capture Compare Register (CCR) may be operated in either Compare mode or Capture mode, independent of the timer configuration or other CCR configurations. All timers excluding TIMA0 have two CCRs, where TIMA0 has four CCRs.

Capture Mode

This mode monitors CCPn signals, or others, for either a Low-to-High (rising edge) or High-to-Low (falling-edge) transition, or both, to generate a Capture Event. This configuration requires the creation of a Timers_CaptureConfig type struct. The table below shows the fields for this configuration struct.

Timer_CaptureConfig struct fields

Field

Type

Possible Values

.ccrn

uint8_t

bit-wise OR of TIMER_CCR_CCRx

where x is 0-3

.inputSel

uint8_t

TIMER_CCR_INPUT_x

where x is one of: CCPn, CCPu,

CCP0, TRIG, XOR, FSUB, FSUB,

COMP0, COMP1, COMP2

.edge

uint8_t

TIMER_CCR_EDGE_NONE

TIMER_CCR_EDGE_RISE

TIMER_CCR_EDGE_FALL

TIMER_CCR_EDGE_BOTH

.invertInput

uint8_t

true (1) or false (0)

Field Descriptions:

  • .ccrn selects one or more CCR register to be configured. If multiple are to be configured, the defines must be combined using bitwise OR.

  • .inputSel selects the input signal to which would generate the capture event. There are many options to use as an input:

    • TIMER_CCR_INPUT_CCPn: Use the directly associated CCP for the selected CCR. For example, if CCR1 is selected, the input would be CCP1.

    • TIMER_CCR_INPUT_CCPu: Use the opposite CCP for the selected CCR. For example, if CCR1 is selected, the input would be CCP0.

    • TIMER_CCR_INPUT_CCP0: Regardless of the selected CCR, use CCP0.

    • Others: The rest of the available signals are for more complex configurations and are not important for this class.

  • .edge: selects the capture edge, with options: rising edge, falling-edge, both edges, or neither edges.

  • .invertInput: setting to true will invert the value of the selected input source via field .inputSel prior to capture, such that rising-edges become falling edges, etc.

With the configuration struct filled in, the CCR(s) can be initialized with the function:

void Timers_initCapture( GPTIMER_Regs *timer , Timer_CaptureConfig *config )

The timer count stored after a capture event may be read via:

uint16_t Timers_getCCRValue( GPTIMER_Regs *timer , uint8_t ccrn )

where the argument uint8_t ccrn selects the CCR to read the captured count value from.

Compare Mode

This mode compares the value stored within the CCRn and performs a specified action on the corresponding CCPn. This configuration requires the creation of a Timer_CompareConfig type struct. The table below shows the fields for this configuration struct.

Timer_CompareConfig struct fields

Field

Type

Possible Values

.ccrn

uint8_t

bit-wise OR of TIMER_CCR_CCRx

where x is 0-3

.action

uint16_t

bit-wise OR of:

TIMER_CCR_ACTION_UPCOMPARE_NONE

TIMER_CCR_ACTION_UPCOMPARE_SET

TIMER_CCR_ACTION_UPCOMPARE_CLEAR

TIMER_CCR_ACTION_UPCOMPARE_TOGGLE

TIMER_CCR_ACTION_DOWNCOMPARE_NONE

TIMER_CCR_ACTION_DOWNCOMPARE_SET

TIMER_CCR_ACTION_DOWNCOMPARE_CLEAR

TIMER_CCR_ACTION_DOWNCOMPARE_TOGGLE

TIMER_CCR_ACTION_LOAD_NONE

TIMER_CCR_ACTION_LOAD_SET

TIMER_CCR_ACTION_LOAD_CLEAR

TIMER_CCR_ACTION_LOAD_TOGGLE

TIMER_CCR_ACTION_ZERO_NONE

TIMER_CCR_ACTION_ZERO_SET

TIMER_CCR_ACTION_ZERO_CLEAR

TIMER_CCR_ACTION_ZERO_TOGGLE

.value

uint16_t

0 to 65535

.invertOutput

uint8_t

true (1) or false (0)

Field Descriptions:

  • .ccrn selects one or more CCR register to be configured. If multiple are to be configured, the defines must be combined using bitwise OR.

  • .action determines the action(s) to take when events occur. Specifically, there are four events that can occur:

    • UPCOMPARE: Event that occurs when the timer is counting UP and is equal to the compare value.

    • DOWNCOMPARE: Event that occurs when the timer is counting DOWN and is equal to the compare value.

    • LOAD: Event that occurs when the timer counter equals the .period field value.

    • ZERO: Event that occurs when the timer counter equals 0.

    Note that for UP counting mode, a LOAD event will be immediately followed by a ZERO event. Similarly, for DOWN counting mode, a ZERO event will be immediately followed by a LOAD event. IN UP DOWN counting mode, LOAD and ZERO events will be evenly spaced in time.

    For each of the four events, the CCR can trigger the CCP output to do one of four actions:

    • NONE: Do not modify the output.

    • SET: Set the output to high (true).

    • CLEAR: Set the output to low (false).

    • TOGGLE: Toggle the value of the output.

    To Select multiple actions, all desired actions should be combined via a bit-wise OR. Only one action per event should be selected.

  • .value specifies that compare value to assign to the CCR.

  • .invertOutput: setting to true will invert the value of the output signal.

With the configuration struct filled in, the CCR(s) can be initialized with the function:

void Timers_initCompare( GPTIMER_Regs *timer , Timer_CompareConfig *config )

After configuration, the compare value may be changed via:

uint16_t Timers_setCCRValue( GPTIMER_Regs *timer , uint8_t ccrn , uint16_t value )

where the argument uint8_t ccrn selects the CCR(s) to set the new compare value to and uint16_t compareValue is the new compare value.

Timer Run Control

Configuration of the timer will not start the timer. This requires the function

void Timers_startTimer( GPTIMER_Regs *timer )

Likewise, the counter may be paused using

void Timers_stopTimer( GPTIMER_Regs *timer )

The timer counter may also be reset to 0 via

void Timers_clearCounter( GPTIMER_Regs *timer )

The period of the timer may be changed using

void Timers_setPeriod( GPTIMER_Regs *timer , uint16_t period )

Timer Status

The timer’s counter value can be monitored by calling the function

uint16_t Timers_getCounter( GPTIMER_Regs *timer )

This function will return the value of the current timer count, where the timer module is specified by timer.

Timer Interrupts

There are 19 total interrupt sources that TIMGx or TIMAx may use to trigger an interrupt. These may be generated by the timer’s counting or by capture or compare events, among others. A subset of these are:

  • TIMER_INTSRC_ZERO: Occurs when the timer counter equals to 0.

  • TIMER_INTSRC_LOAD: Occurs when the timer counter equals to the configured period value.

  • TIMER_INTSRC_CCRn_DN: Triggered on a Capture or Compare event from CCRn when timer is counting DOWN. Replace n with the desired CCR number.

  • TIMER_INTSRC_CCRn_UP: Triggered on a Capture or Compare event from CCRn when timer is counting UP. Replace n with the desired CCR number.

  • TIMER_INTSRC_OVERFLOW: Triggered when the timer counter overflows from 65535 to 0. The event should typically not occur in normal operation.

All of the above interrupt sources will trigger a single interrupt request handler per timer, or IRQ Handler; that is, each timer has a one IRQ Handler each.

Enabling Interrupts

Each interrupt source may be individually enabled and disabled through the functions:

  • void Timers_enableInterrupt( GPTIMER_Regs *timer , uint32_t interruptMask )

  • void Timers_disableInterrupt( GPTIMER_Regs *timer , uint32_t interruptMask )

where uint32_t interruptMask is the desired interrupt source(s) to enable/disable, listed above. If multiple sources are desired, they are combined via a bitwise OR or enabled independently. These functions must be called after :code:`void Timers_initTimer()`, otherwise the requested change will be reverted.

Further, the associated timer interrupt must also be enabled in the CPU with

NVIC_EnableIRQ( IRQn_Type IRQn )

where IRQn_Type IRQn is a defined identifier corresponding to the TIMG or TIMA module to enable of the form: TIMGx_INT_IRQn or TIMAx_INT_IRQn, where x is the desired timer module number.

Each timer interrupt, if enabled, must have an associated IRQ Handler function. These functions must be named to pre-defined required names such that the interrupt requests are linked as desired. The naming convention for each IRQ Handler is:

void TIMGx_IRQHandler(void) or void TIMAx_IRQHandler(void)

where the x is replaced with the desired timer module number.

Interrupt Handling

The IRQ Handler associated with an interrupt will automatically be called when the specific interrupt is triggered. Within this function, the code must determine which interrupt source(s) triggered the handler. This may be done with one of three functions:

  • uint32_t Timers_getActiveInterrupt( GPTIMER_Regs *timer ):

    • Indicates the highest priority interrupt source pending for the corresponding module and will acknowledge the interrupt source. The function will return one of TIMER_INTSRC_x defined values as listed above. If using this identification method, the IRQ Handler will be called repeatedly until all enabled sources are acted upon.

  • uint32_t Timers_getPendingInterrupts( GPTIMER_Regs *timer ):

    • Returns all interrupt sources that are currently pending and enabled for the corresponding module without achnowledging any interrupt source. This is a bitwise OR combination of the corresponding TIMER_INTSRC_x defined values as listed above. If using this identification method, only one IRQ Handler call is needed for all sources to be acted upon.

  • uint32_t Timers_getAllPendingInterrupts( GPTIMER_Regs *timer ):

    • Returns all interrupt sources that are currently pending, including disabled sources, for the corresponding module without achnowledging any interrupt source. This is a bitwise OR combination of the corresponding TIMER_INTSRC_x defined values as listed above. If using this identification method, only one IRQ Handler call is needed for all sources to be acted upon. This function should only be used in Activity 7.

If using the second or third functions as given above, each enabled interrupt source must be acknowledged (done automatically using the first function). If this acknowledgment is not done, the interrupt will continuously trigger; that is, the IRQ Handler will be immediately called again once it completes, erroneously. To acknowledge, or clear an interrupt source, use the following function:

  • void Timers_clearInterrupt( GPTIMER_Regs *timer , uint32_t interruptMask )

where uint32_t interruptMask is the desired interrupt source(s) to acknowledge and clear. If multiple sources are desired to be acknowledged, they are combined via a bitwise OR or enabled independently.

Two example IRQ Handlers are provided below; both for TIMG0 with both the LOAD interrupt source and CCR0_DN interrupt source enabled.

Example 1

This example IRQ Handler will run once per enabled interrupt source

void TIMG0_IRQHandler(void){ // Function name is fixed for TIMG0
     // Get the highest priority pending interrupt
     uint32_t intsrc = Timers_getActiveInterrupt(TIMG0);
     // Check if LOAD event
     if(intsrc == TIMER_INTSRC_LOAD){
         // Do what is needed for a timer LOAD event (increment counter?)
     }
     // Check if CC0_DN event
     else if(intsrc == TIMER_INTSRC_CCR0_DN){
         // Do what we need for the CCR0_DN event (calculate speed?)
     }
}

Example 2

This example IRQ Handler will run once, handling all pending interrupt sources

void TIMG0_IRQHandler(void){ // Function name is fixed for TIMG0
     // Get all pending interrupts
     uint32_t intsrc = Timers_getPendingInterrupts(TIMG0);
     // Check if LOAD event
     if(intsrc & TIMER_INTSRC_LOAD){
         // Acknowledge interrupt source
         Timers_clearInterrupt(TIMER_INTSRC_LOAD);
         // Do what is needed for a timer LOAD event (increment counter?)
     }
     // Check if CC0_DN event
     if(intsrc == TIMER_INTSRC_CCR0_DN){
         // Acknowledge interrupt source
         Timers_clearInterrupt(TIMER_INTSRC_CCR0_DN);
         // Do what we need for the CCR0_DN event (calculate speed?)
     }
}