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.
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.
Field |
type |
Possible Values |
|---|---|---|
|
|
|
|
|
|
|
|
where |
|
|
|
|
|
|
- Field Descriptions:
.modeset the counting mode of timer. Each timer is capable of counting inDOWNmode,UPmode, andUP_DOWNmode. 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..clksrcindicates what the timer module should use as its counting base. For the purposes of this course, we will typically selectBUSCLK, which is set to be 32 MHz..clkdivratioallows for the selected.clksrcto be “divided” in frequency. For example, if this field was set toTIMER_CLOCK_DIVIDE_4, the timer would would increment at a rate of 32 MHz / 4 = 8 MHz..clkprescaleallows 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 typeuint8_tand can take any value in that range..periodspecifies the maximum number that the module will count to before resetting inUPorUP_DOWNmodes. InDOWNmode, this specifies the number at which to start counting. This number is of typeuint16_tand can take any value in that range. Note thatTIMG12is 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; withTIMG12being 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.
Field |
Type |
Possible Values |
|---|---|---|
|
|
bit-wise OR of where |
|
|
where
|
|
|
|
|
|
|
Field Descriptions:
.ccrnselects one or more CCR register to be configured. If multiple are to be configured, the defines must be combined using bitwise OR.
.inputSelselects 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 totruewill invert the value of the selected input source via field.inputSelprior 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.
Field |
Type |
Possible Values |
|---|---|---|
|
|
bit-wise OR of where |
|
|
bit-wise OR of:
|
|
|
|
|
|
|
Field Descriptions:
.ccrnselects one or more CCR register to be configured. If multiple are to be configured, the defines must be combined using bitwise OR.
.actiondetermines 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.periodfield value.
ZERO: Event that occurs when the timer counter equals 0.Note that for UP counting mode, a
LOADevent will be immediately followed by aZEROevent. Similarly, for DOWN counting mode, aZEROevent will be immediately followed by aLOADevent. IN UP DOWN counting mode,LOADandZEROevents 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.
.valuespecifies that compare value to assign to the CCR.
.invertOutput: setting totruewill 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. Replacenwith the desired CCR number.
TIMER_INTSRC_CCRn_UP: Triggered on a Capture or Compare event from CCRn when timer is counting UP. Replacenwith the desired CCR number.
TIMER_INTSRC_OVERFLOW: Triggered when the timer counter overflows from65535to0. 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)orvoid 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_xdefined 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_xdefined 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_xdefined 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?)
}
}