Getting started with TIM

Revision as of 15:46, 15 August 2024 by Registered User
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

1. Introduction to timers

The STM32 series devices have various built-in timers outlined as follows :

  • General-purpose timers
  • Advanced timers
  • Basic timers
  • Low-power timers
  • High-resolution timers

In this article, we will be focusing on the first 3 ones.

2. TIM with interrupt

The STM32 families embed multiple timers providing timing resources for software or hardware tasks.
The software task consists mainly of providing time bases, timeout event generation, and time triggers
The hardware tasks are related to I/O, timers can generate waveforms or outputs, and measure incoming signal parameters.

2.1. Objectives

The purpose of this example is to demonstrate how to:

  • Configure our first TIM in CubeMX
  • Select timer frequency
  • Start TIM in code
  • Use TIM overflow(update) interrupt to toggle led


TIM Img 16.png

2.2. Examples setup

To follow along with this wiki, you need a NUCLEO-L476RG board. Before starting the hands-on, make sure you have installed the following software:

STM32CubeMX 6.5.0 (or a newer version)
STM32CubeIDE 1.9.0 (or a newer version)
STM32CubeL4 firmware package

2.3. STM32CubeMX Timer configuration

  • Run STM32CubeMX
  • Menu > File > New Project
  • Select NUCLEO-L476RG using the board selector
  • Start project

2.3.1. GPIO configuration

In CubeMX Pinout view select PA5 set as Output mode

GPIO LED Selection.png

2.3.2. TIM configuration

2.3.2.1. Timer clock source selection

The timer features multiple clocking options. The default clock comes from the clock controller connected to one of the APB clock domains The counter clock can be provided by the internal clock (RCC) or by the external clock through external trigger input (ETR pin), other on-chip timers (ITRx inputs), input pins (TI1,TI2), and quadrature signals from encoders.

TIM Img 15.png

Info white.png Information
The timers are shared between 2 APB domains to implement low power schemes with typically one high-speed APB and one low-speed APB.)

In this example, we will be using the internal clock source, This information can be found in Reference Manual or the Datasheet. The internal clock frequency is coming from the APB bus where the TIM is connected
For example TIM1 on STM32L476
In the datasheet, it is possible to find the STM32L476xx block diagram where we can see that TIM1 is on the APB2 bus.

TIM Img 02.png

in the reference manual, look into System and Memory overview chapter., you can find a memory map where is possible to find TIM1 on APB2.

TIM Img 01.png

For this application, the APB2 timer clock is 4MHz.

TIM Clock Source.png

2.3.2.2. TIM parametes configuration

In STM32CubeMX, select Timers => TIM1

  • Select the Internal clock as the clock source

TIM GeneralAdvanced Selection.png

2.3.2.3. Clock division

We can divide the clocks by /1 /2 /4

TIM Img 13.png

For this example, we will choose no Division(/1).

TIM Img 04.png

2.3.2.4. Clock prescaler

The Prescaler can divide the counter clock frequency by any factor between 1 and 65536.
The counter clock frequency is equal to fck_PSC / (PSC+1).

TIM Img 12.png

In our example, we have APB2 clock = 4MHz
No division in the Clock division block (/1)
So PSC input clock is 4MHz.
If we want to divide it by 1000 (PSC+1)=> We must put PSC=999.
Then we will have output frequency from PSC 4MHz/(999+1) = 4KHz.

TIM Img 06.png

2.3.2.5. Counter period

This timer period.

TIM Img 11.png

For PSC=999 and APB2=4MHz, with no division DIV=1.
We have an input frequency 4kHz.
If we want period 1s, we must set ARR to 3999.
Where period is : f_out = APB2 / ( ARR + 1 ) = 4kHz / ( 3999 + 1 ) = 1Hz = 1s

TIM Img 07.png

2.3.2.6. Counter Mode

We can optionally select the counting mode.

  • Up: the counter counts from 0 to the auto-reload value (ARR).
  • Down: the counter counts from auto-reload value (ARR) to 1.
  • Center alignment(up/down): the counter counts from 0 to (ARR-1) then counts from ARR value down to 1. Then it starts counting from 0

in this application, we will choose the up-counting mode

TIM Img 08.png


By now, our configuration should look like this


TIM Img 03.png

2.3.2.7. Enable interrupt

The Timer can have one or more interrupts. The basic one for timer overflow is called "update".

TIM Img 10.png

We switch to NVIC settings and we will select TIM1 update interrupt.

TIM Interrupt.png

2.4. Code generation

  • Go to Project Manager
  • Set the project name (TIM1)
  • Project location
  • Type of toolchain (CubeIDE)
  • Now we can Generate Code

TIM Generate.png

2.4.1. Starting timer

The timer is now only configured but is not started. For this, we use the function: HAL_TIM_Base_Start_IT
The first argument is our TIM1 handle htim1

Between /* USER CODE BEGIN 2 */ and /* USER CODE END 2 */ tags, insert the function : Starting function is the:

  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim1);
  /* USER CODE END 2 */

2.4.2. Handling timer interrupt

TIM Img 09.png

When TIM1 interrupt is triggered, the function TIM1_UP_TIM16_IRQHandler is called, this calls the HAL library function HAL_TIM_IRQHandler which decodes the interrupt source. For this example, this function is only implemented to get the callback from the function HAL_TIM_PeriodElapsedCallback defined as __weak in stm32l4xx_hal_tim.c.

so we will redefine it in our main.c by adding a GPIO toggle : Between /* USER CODE BEGIN 0 */ and /* USER CODE END 0 */ tags, add

/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
/* USER CODE END 0 */
Info white.png Information
If you expect to use multiple timers you must add a check for instance.

2.4.3. Compile the code

To compile project press CTRL+B Or got to Menu>Project>Build all

TIM Build.png

2.4.4. Run in debug

  • Connect your board to the PC
  • Start debug
  • Menu>Run>Debug As>STM32 Cortex-M ...
  • No need to change anything in the next window just click debug

TIM Debug.png

  • Run code by F8

TIM Debug Run.png

the LD2 should toggle each 1s.

Info white.png Information
When the code is stopped in debug mode, timers are not stopped. In order to stop TIM1 in debugging, you have to call __HAL_DBGMCU_FREEZE_TIM1 (on some devices is necessary enable DBGMCU clocks.)

3. TIM with DMA transfer

3.1. Objectives

  • Learn how to set up TIM with DMA in CubeMX
  • Indicate TIM DMA transfer with LED toggle

3.2. STM32CubeMX configuration

This example will be based on the previous timer and GPIO configuration, except the step " Enable Interrupt" will be skipped since we'll be using DMA instead this time.

3.2.1. DMA configuration

In the TIM1 configuration click on "DMA settings"

DMA Configuration1.png

  • Add DMA request as TIM1 UP
  • Change direction to: Memory to Peripheral
  • Change DMA mode to circular

DMA Configuration2.png

3.3. Code configuration

The following flowchart describes the steps :

Timer with DMA.png

3.3.1. Variable data definition

Between /* USER CODE BEGIN PV */ and /* USER CODE END PV */ tags, add

/* USER CODE BEGIN PV */
uint16_t data[]={GPIO_PIN_5,0x0000};
/* USER CODE END PV */

3.3.2. DMA and Timer start

Between /* USER CODE BEGIN 2 */ and /* USER CODE END 2 */ tags, add

 __HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);
  HAL_DMA_Start(&hdma_tim1_up,(uint32_t)data,(uint32_t)&GPIOA->ODR,2);
  HAL_TIM_Base_Start(&htim1);

4. TIM synchronization

STM32xx Series timers are linked together internally for timer synchronization or chaining.
Each timer has several internal input and output triggers. These signals allow timer interconnection.

4.1. Objectives

  • Explain timer internal triggering system
  • Use a master timer to start a slave timer: Trigger TIM2 with the update of TIM1

4.2. STM32CubeMX configuration

Before configuring timers in STM32CubeMX, you need to know which timer will start counting
To do so, refer to the STM32L476 reference manual, internal trigger connections.
The table below shows how the timer triggers are connected internally, it is necessary to select the right timers for your application. In this example, we will select TIM2 as a slave timer, and TIM1 as a master using ITR0 (TS = 000) internal signal.

internal trigger connections.png

Info white.png Information
Similarly, TIM3, TIM4,TIM5 could be triggered by TIM1 using the same signal ITR0.
Warning white.png Warning
TIM1 and TIM2 run at different clock frequencies since TIM1 is connected to APB2 bus and TIM2 to APB1 bus.

4.2.1. Master TIM configuration

When a timer is selected as a master timer, the corresponding trigger output signal is used by the slave internal trigger (when configured).
The trigger output can be selected from the following list :

  • Reset
  • Enable
  • Update
  • OC1Ref
  • OC2Ref
  • OC3Ref
  • OC4Ref

In this example, we will use PWM output to monitor timer outputs.
As TIM1 is the master timer, we will only enable the PWM output on Channel1

TIM1 PWM generation.png

We will use the same values of prescaler and Auto-reload as in the previous examples.
Choose the trigger event selection as an Update event: This will generate the update event flag whenever the counter overflows

Update event.png

Set the Pulse to 800, as the Duty cycle= Pulse/ ARR, since ARR=4000, choose Pulse=1000 to obtain a 25% duty cycle.
Set the Channel polarity to low, in this case, the output will be inverted

TIM1 PWM Channel1 configuration.png

4.2.2. Slave TIM Configuration

  • Select the trigger mode as the slave mode
  • Select ITR0 as the trigger source
Info white.png Information
The trigger mode allows the master timer to trigger the start of the slave timer.

We will also enable PWM generation for TIM2, to visualize the output

TIM2 Configuration.png

  • Check that the slave mode controller is trigger mode.
  • Set the channel polarity to LOW
  • Set the ARR value to a random value

TIM2 PWM generation.png

4.3. Code generation

In the main function, between /* USER CODE BEGIN 2 */ and /* USER CODE END 2*/ tags, add

 /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); 
  HAL_Delay(500); 
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
  /* USER CODE END 2 */

We add the delay between the two PWM starts, to make sure the slave mode is working. So the TIM2 won't start counting before TIM1

After running the code, you can use a logic analyzer to visualize the timer signals

  • TIM1_CH1 : PA8
  • TIM2_CH1 : PA0

As shown in the image below, TIM2 starts after TIM1 :

logic analyzer snippet.png

Note that there are several other modes used in timer synchronization, refer to the examples provided in the STM32Cube package: Examples:

  • \TIM\TIM_CascadeSynchro
  • \TIM_ExtTriggerSynchro\TIM_Synchronization
  • \TIM_ParallelSynchro folders
Info white.png Information
You can proceed with 3 timers, using the same configurations in all the slave timers.
No categories assignedEdit