Skip to main content
SMusc.1
Associate III
April 19, 2025
Solved

Measure the speed rotation by using TIM in encoder mode

  • April 19, 2025
  • 3 replies
  • 1819 views

hello, I ask for the following clarification: I am using a two-channel encoder, A and B, and I want to estimate the rotation speed of the encoder shaft as precisely as possible. I started setting the TIM2 in encoder mode.
My encoder generates 1200 pulses per revolution.
I called the function:

 
HAL_TIM_Encoder_Start_IT(&htim2, TIM_CHANNEL_ALL);

and :
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
counter = __HAL_TIM_GET_COUNTER(htim);
}


which increases the counter variable every time a channel A (and B) get a variation (rising/falling edge settable from CUBE IDE).
At one full revolution of the encoder, with my settings of frequency = 160 MHz, Ps =0 and ARR = 4800, the counter variable starts at zero and goes to the value 4800 at each full revolution, then goes back to zero, and so on (all visible via serial on my laptop by using Putty software). My question is: how can I estimate the rotation speed? I have read a lot of information about this but I am still confused. Any advice?

Best answer by CBerg

most probably you are creating to many interrupts.

HAL_TIM_PeriodElapsedCallback

is a call back function. That means that it is called every time (automatically) when the corresponding interrupt occurs. If your MCU is completely busy with handling the interrupts (you are generating), then you'll see that behaviour, that you describe as "freezing".

How to fix this: use an approach where you are not spamming your core with interrupt requests.

3 replies

KnarfB
Super User
April 19, 2025

You may set the period to a larger multiple of 4800, setup another timer with a periodic interupt, measure the counter as you do now and calc the counter difference and divide by 1200 (or 4800?) to get the number of rotations.

Vice versa, leave the encoder timer as is, but add an update interrupt generation + handler for the existing encoder timer, set another timer to continuosly count the time and calc the difference between two interrupts to get the time for one rotation.

Consider the expected speed range to decide which way you go, if you count milliseconds or microseconds or whatever suits you application. Take care of handling overflows correctly

hth

KnarfB 

SMusc.1
SMusc.1Author
Associate III
April 20, 2025

Hi, I think there is some problem:
I left the TIM2 in encoder mode and using the function:

 

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)


I can print the counts number on the video via serial every time I move the encoder shaft. Using scaling I can then obtain the position in degrees (one complete turn = 360 degrees) but as soon as I enable a timer (TIM6) to do a test and using the function to turn a LED on and off:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6)
{
HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port,LED_GREEN_Pin);//,GPIO_PIN_SET);
}


printing to video via serial no longer shows the position of the encoder shaft which remains fixed at a value. Is there a conflict in this sense between the two interrupts? How can I solve this?

CBerg
Senior II
April 21, 2025

I don't understand your approach, so I can not comment on that.

I am measuring angular position / speed in this way:

	int32_t ctr = (int32_t)(__HAL_TIM_GET_COUNTER(&htim2));
	int32_t zctr = (int32_t)(__HAL_TIM_GET_COMPARE(&htim2, TIM_CHANNEL_3));

I use Timer2 in this example: Channel 1 + 2 are set up in encoder mode, Channel 3 is set up as "Input Capture direct mode". Note: make sure to set Encoder Mode to "Encoder Mode Ti1 and Ti2" for X4 Evaluation in CubeMX.

The function containing this code is called periodically with a fixed timing, e.g. triggered by an other timer.

ctr is the position information from the encoder. The setting "encoder mode" does this for you. If you use e.g. a 1024 Tics/rev. Encoder and X4-Evaluation, the counter variable can reach the values 0 - 4095 before it rolls over.

zctr is the position information of the zero tic - if your encoder has one.

Or the other way round: the setting "encoder" mode handles already most of the tasks for you. You just have to give the information of the Tics per Revolution and set the Evaluation mode correctly, then each read of the CNT register will give you the actual angle position of the encoder in increments. If you want/need to, you can reference this position with the Zero-Tic.

Lets say your timing source is the SysTic and you are calling the function with the code above every 1 Millisecond. Then you can compare the last value of ctr with your actual value of ctr. This gives you the information how the angle position has changed (in increments).

In the simplest approach you now have a change of angle and a change of time: if you divide the angle change by the time change you get: v = d phi / d t which is an angular speed. From there you can convert into any unit you want, may it be rev. per minute or rad/s or whatever ...

In reality you will have to filter that value, because 1 Millisecond is very small and especially at low angular speed you will either device e.g. 1 (tic increment) by 0.001s wich leads to a huge value (1000 tics/s). You can decide on your own if you want to use e.g. a block average or e.g. a simple exponential moving average.

Pitfalls: you will have to make sure you read the angle and calculate the angle difference faster than the encoder can roll over. Otherwise you will run into an issue to detect the real change and you will not be able to detect if the angle has increased or decreased when your readout timing is so slow, that the encoder position can more than 180°

Tesla DeLorean
Guru
April 19, 2025

Measure the TIME for one pulse, many, or a rotation

You could perhaps measure micro-seconds via TIM5 (32-bit)

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
CBerg
Senior II
April 20, 2025

@Tesla DeLoreanI agree with that you have to measure the time. But measuring the length of one pulse will lead to a very noisy speed signal because the time increments become very small and dividing 1 by something very small tends to go to +- infinity ...

=============

The principle is:

you measure time and you measure angle increments. What I use is: I use a "window" of - lets say 100 ms (for encoders with 1024 - 2048 Tics / rev) and count all the pulses that come within that 100 ms window.

As one pulse corresponds to 360° / Tic-Count (at X1 evaulation) or 360° / 4 x Tic-Count (at X4 evauation) now you have ange / time which is actually a speed value. You just have to convert it with a fixed factor to the unit's you want to see like angle/second or revolutions / minute.

If you need more frequent speed updates you could use e.g. 10 of those 100 ms Windows. Then you get a new speed value every 10 ms, but each window is still 100 ms long. Of course you need to "overlap" these windows.