Skip to main content
Senior
September 1, 2023
Solved

How to use Timers ?

  • September 1, 2023
  • 11 replies
  • 10386 views

Hi, I'm learning how to use timers by following digikey's tutorial : Getting Started with STM32 and Nucleo Part 6: Timers and Timer Interrupts | Digi-Key Electronics - YouTube

However at 9:41 in the video I don't understand the code : 

// If enough time has passed (1 second), toggle LED and get new timestamp
if (__HAL_TIM_GET_COUNTER(&htim16) - timer_val >= 10000)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
timer_val = __HAL_TIM_GET_COUNTER(&htim16);
}

What does  __HAL_TIM_GET_COUNTER(&htim16) really do? Then what the condition if is testing? 

 

Thanks

This topic has been closed for replies.
Best answer by Issamos

Hello @Gnahore 

This part of the code is testing if the difference between TIMER_CNT(now)  and TIMER_CNT went the TIMER start counting (timer_val)is equal to 10000. Which means that the TIMER has counted 1 Second. 

If so, the code toggle the led and update the timer_val with the new TIMER_CNT value to restart a 1 Second count .

Rq: 10000 is calculated depends on the timer frequency and other  conditions that are well explained in the tutorial.

Best regards.

II

11 replies

ST Technical Moderator
September 1, 2023

Hello @Gnahore,

The HAL_TIM_GET_COUNTER is used to get the current TIM Counter Register value on runtime (from the CNT register), and return 16-bit or 32-bit value of the timer counter register (TIMx_CNT).

Imen

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Thanks
Issamos
IssamosBest answer
Super User
September 3, 2023

Hello @Gnahore 

This part of the code is testing if the difference between TIMER_CNT(now)  and TIMER_CNT went the TIMER start counting (timer_val)is equal to 10000. Which means that the TIMER has counted 1 Second. 

If so, the code toggle the led and update the timer_val with the new TIMER_CNT value to restart a 1 Second count .

Rq: 10000 is calculated depends on the timer frequency and other  conditions that are well explained in the tutorial.

Best regards.

II

GnahoreAuthor
Senior
September 4, 2023

@Issamos Thank you for your answers ! But I'm still very confused. HAL_TIM_GET_COUNTER() get the tim counter register value on runtime since the start of the timer with the function HAL_TIM_Base_Start(), right?  And timer_val was also set with HAL_TIM_GET_COUNTER(). So timer_val is a fixed value and HAL_TIM_Base_Start() is the one that inscrease.

Does HAL_TIM_GET_COUNTER() start at 0 when it is called in the test? Also does timer_val as for first value 0? I wonder if by the time, the timer started thanks to HAL_TIM_Base_Start() and the time the value is put in timer_val, the value of timer_val is really 0.

I'm slowly understanding but what happens when timer_val get 60000 as value? HAL_TIM_Base_Start() can't go above 65 536

 

Here is the full code : 

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim16;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM16_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint16_t timer_val;

/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM16_Init();
/* USER CODE BEGIN 2 */

// Start timer
HAL_TIM_Base_Start(&htim16);

// Get current time (microseconds)
timer_val = __HAL_TIM_GET_COUNTER(&htim16);

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// If enough time has passed (1 second), toggle LED and get new timestamp
if (__HAL_TIM_GET_COUNTER(&htim16) - timer_val >= 10000)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
timer_val = __HAL_TIM_GET_COUNTER(&htim16);
}

/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

 

waclawek.jan
Super User
September 4, 2023

> What does  __HAL_TIM_GET_COUNTER(&htim16) really do?

You can find out yourself, Cube is open source, so you can read its sources and/or step through the code in debugger.

https://github.com/STMicroelectronics/STM32CubeF3/blob/121e4039445ba59f284ebff013d8b3782d503e9f/Drivers/STM32F3xx_HAL_Driver/Inc/stm32f3xx_hal_tim.h#L1466

JW

waclawek.jan
Super User
September 4, 2023

Btw. there is also a problem with this code:

If you'd observe timer_val in a debugger, you'd see that it changes in this way: 0-10000-20000-30000-40000-50000-60000-0-10000-20000-... etc. It means, that every 7th interval (between 60000 and 0) is half the expected lenght, as it lasts only 0.5536 seconds. And yes, it's clearly visible on the LED blinking.

This is due to the facts that a) TIM16 is 16-bit timer; b) STM32 is a 32-bit mcu thus int type is 32-bit wide; c) integer promotion rules in C are at times really tricky (although they are logical and straightforward, but sometimes understanding logical and straightforward things requires significant effort).

Correctly, the 16-bit rollover should be handled like this:

 if ((uint16_t)(__HAL_TIM_GET_COUNTER(&htim16) - timer_val) >= 10000)

now timer_val will go like

... 50000-60000-4464-14464...

JW

GnahoreAuthor
Senior
September 4, 2023

Thanks for answering @waclawek.jan 

How does timer_val go back to 0 ? From what I understood HAL_TIM_GET_COUNTER don't go above 65 536 so at what moment 

HAL_TIM_GET_COUNTER(&htim16) - timer_val) >= 10000

can happen when timer_val reached 60 000 ? 

Piranha
Principal III
September 7, 2023

Jan presented this:

(uint16_t)(__HAL_TIM_GET_COUNTER(&htim16) - timer_val) >= 10000

You asked to explain this:

(HAL_TIM_GET_COUNTER(&htim16) - timer_val) >= 10000

There is a critically important difference in these lines... Without the cast in front the code is prone to fail at overflow because of integer promotion. In this particular case the only thing that saves the day is the fact that the TIM_CNT register is actually defined as uint32_t for all timers, including the 16-bit ones. But, if both sides of subtraction would be of an uint16_t type, the subtraction would be done as int (signed 32-bit) type, produce a negative result and the comparison would also be done on a signed int type and fail.

GnahoreAuthor
Senior
September 5, 2023

Any answer ?

Issamos
Super User
September 5, 2023

Hello @Gnahore 

I don't have a clear answer to your question but I suggest you to close these post by choosing a best answer and copie your new request to a new post to give him more visibility.

Best regards.

II

waclawek.jan
Super User
September 5, 2023

How does timer_val go back to 0 ? From what I understood HAL_TIM_GET_COUNTER don't go above 65 536 so at what moment 

HAL_TIM_GET_COUNTER(&htim16) - timer_val) >= 10000

can happen when timer_val reached 60 000 ? 

In this case, the subtraction is between unsigned variables, and that, according to C99 conversion rules, results in an unsigned value. So, when TIM_CNT rolls over from 65535 to 0, the subtraction result is 0x0000'0000-0x0000'EA60 = 0xFFFF'15A0 and that is higher than 10000.

> does timer_val as for first value 0?

Well spotted - as an uninitialized local variable, timer_val has a "random" initial value, so that's another bug in that program. However, due to the just described unsigned subtraction and the range of TIMx_CNT, the first period won't last longer than 1s, and most probably (for most timer_val values) the program won't wait at all in the first period.

JW

GnahoreAuthor
Senior
September 6, 2023

Wow thank you a lot !