Start a timer and read its counter
We set TIM1 to count 1000 times per second (16 MHz clock, prescaler 15999), start it, and read its counter twice, one second apart. The difference is about 1000.
In this lesson · 6 steps
1A new project at 16 MHz
- Start a new project as in lesson 1.1 (Board Selector → NUCLEO-F401RE → No to default peripherals) and name it
STM32_TIMER_PROGRAMMING. In the video the previous project is copied instead; a fresh one avoids name mix-ups - Clock Configuration tab: type
16in HCLK (MHz) and press Enter. CubeMX switches to the 16 MHz HSI
Why?
TIM1 gets its clock from the APB2 bus, which here runs at the same 16 MHz as the core. That is the clock the prescaler divides.
2TIM1: 1000 counts per second
- Timers → TIM1: Clock Source =
Internal Clock(this turns the timer on) - Parameter Settings: Prescaler =
15999, Counter Period =65535 - Project Manager: Project Name =
STM32_TIMER_PROGRAMMING, the same Project Location as before → Generate Code → Open Project
Why?
16 MHz / (15999 + 1) = 1000 Hz, so the counter goes up by one every millisecond. 65535 is the largest value, so the counter runs as long as possible (about 65 seconds) before it wraps to zero.
3Two variables
main.c · USER CODE BEGIN PV
Two places to keep the counter values.Grey lines are already in the file: add only the coloured ones. Copy takes just those.
/* USER CODE BEGIN PV */ uint16_t timer1, timer2; /* USER CODE END PV */
4Start the timer
main.c · USER CODE BEGIN 2
CubeMX sets the timer up, but it does not start it. This line does.Grey lines are already in the file: add only the coloured ones. Copy takes just those.
/* USER CODE BEGIN 2 */ HAL_TIM_Base_Start(&htim1); /* USER CODE END 2 */
Why?
htim1 is a handle: a struct that CubeMX made in main.c, holding TIM1's settings. &htim1 passes its address, so the HAL function knows which timer to use. You will see the same with &htim2 and &huart2 later.5Read the counter twice
main.c · inside the braces of while (1)
Read the counter, wait one second, read it again.Grey lines are already in the file: add only the coloured ones. Copy takes just those.
/* USER CODE BEGIN WHILE */ while (1) { timer1 = __HAL_TIM_GET_COUNTER(&htim1); HAL_Delay(1000); timer2 = __HAL_TIM_GET_COUNTER(&htim1); HAL_Delay(1000); /* USER CODE END WHILE */
Why?
__HAL_TIM_GET_COUNTER returns the counter value at that moment. The timer keeps counting on its own; the CPU only looks at it.6Run and compare
- Debug, then Resume
- Open Window → Show View → Live Expressions. Click Add new expression, type
timer1, press Enter; do the same fortimer2. The values update while the program runs
Why?
timer1 and timer2 are global variables, so the Variables view (which shows local ones) does not list them. Live Expressions does.Check & troubleshoot
What you should see
timer2is about 1000 more thantimer1: 1000 counts in one second
If it doesn't work
- Both values stay 0:
HAL_TIM_Base_Start(&htim1)is missing in USER CODE BEGIN 2 - The difference jumps around at random: the prescaler is still 0, so the counter runs at 16 MHz and wraps hundreds of times per second; set it to 15999 and generate the code again
- The difference is about 5250, not 1000: HCLK is still 84 MHz; set it to 16 in Clock Configuration
- timer2 is smaller than timer1: the counter passed 65535 and wrapped to 0 between the two reads.
(uint16_t)(timer2 - timer1)still gives the right difference
Good to know
- The prescaler is 16 bits, so it can be at most 65535. For bigger divisions, also use the counter period, or a 32-bit timer such as TIM2 or TIM5