cancel
Showing results for 
Search instead for 
Did you mean: 

Incremental encoder HAL_TIM_IC_CaptureCallback does not trigger on each step

Kostas Papouis
Associate II

Hello Community,

I have an incremental encoder with 30 detents interfaced on STM32H5562 where I have set my TIMER2 in Encoder mode  and using interrupt as shown below:

 

static void MX_TIM2_Init(void)

{

 

/* USER CODE BEGIN TIM2_Init 0 */

 

/* USER CODE END TIM2_Init 0 */

 

TIM_Encoder_InitTypeDef sConfig = {0};

TIM_MasterConfigTypeDef sMasterConfig = {0};

 

/* USER CODE BEGIN TIM2_Init 1 */

 

/* USER CODE END TIM2_Init 1 */

htim2.Instance = TIM2;

htim2.Init.Prescaler = 0;

htim2.Init.CounterMode = TIM_COUNTERMODE_UP;

htim2.Init.Period = 4294967295;

htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

sConfig.EncoderMode = TIM_ENCODERMODE_TI12;

sConfig.IC1Polarity = TIM_ICPOLARITY_FALLING;

sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;

sConfig.IC1Prescaler = TIM_ICPSC_DIV1;

sConfig.IC1Filter = 0;

sConfig.IC2Polarity = TIM_ICPOLARITY_FALLING;

sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;

sConfig.IC2Prescaler = TIM_ICPSC_DIV1;

sConfig.IC2Filter = 0;

if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)

{

Error_Handler();

}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;

sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)

{

Error_Handler();

}

/* USER CODE BEGIN TIM2_Init 2 */

 

HAL_TIM_Encoder_Start_IT(&htim2,TIM_CHANNEL_ALL); //start the encoder timer 2

 

 

/* USER CODE END TIM2_Init 2 */

 

}

 

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)

{

if (htim == &htim2)

{

counter1 = __HAL_TIM_GET_COUNTER(&htim2);

}

 

 

The problem I have is that HAL_TIM_IC_CaptureCallback is triggered every second step of the encoder.

I would like the HAL_TIM_IC_CaptureCallback to trigger on each turn(detent).

On the other hand, if I use the __HAL_TIM_GET_COUNTER(&htim2) inside the while loop the counter is updated on each turn.

 

Do you know how this can be achieved within the interrupt?

 

Following is a part of the encoder datasheet.

KostasPapouis_0-1719494168885.png

 


Thank you.

13 REPLIES 13

Hi @BarryWhit ,

Following is a bit more info on what is happening:

You can see below on the oscilloscope the capture of the two signals where Encoder_A is on CH1 and Encoder_B is on CH2.

When Encoder_B signal goes low first the two counters are not the same. I believe this happens because the polarity of the Encoder_B on the firmware is set to rising but the signal goes low instead therefore missing 1 count within the interrupt. In the while loop the counter is updates correctly though. 

KostasPapouis_0-1719912663311.png

 

KostasPapouis_1-1719912695830.png

 

On the other hand, when Encoder goes high first the two counters (in the interrupt and while loop)are the same:

KostasPapouis_2-1719912716017.png

 

KostasPapouis_3-1719912732112.png

 

The same behaviour occurs on the opposite direction.

 

As a recap following is how the two signals are configured:

htim2.Instance = TIM2;

htim2.Init.Prescaler = 0;

htim2.Init.CounterMode = TIM_COUNTERMODE_UP;

htim2.Init.Period = 4294967295;

htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

sConfig.EncoderMode = TIM_ENCODERMODE_TI12;

sConfig.IC1Polarity = TIM_ICPOLARITY_FALLING;

sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;

sConfig.IC1Prescaler = TIM_ICPSC_DIV1;

sConfig.IC1Filter = 0;

sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;

sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;

sConfig.IC2Prescaler = TIM_ICPSC_DIV1;

sConfig.IC2Filter = 0;

 

 

 

 

BarryWhit
Senior III

Right. So, you have two edges per counter tick.

 

Input Capture events normally (i.e. I expect that it does) triggers on a switch in logic level, not when the counter changes value.  So, if a pin toggles, but (due to polarity, turn direction, whatever) this edge is not what triggers the change to the counter value, when you read the counter value in the callback, it hasn't yet seen the other edge. When the other edge occurs, the counter updates, but there's no callback, because the 2nd edge does not trigger an Input Capture event. Does that make sense?

- If a post has answered your question, please acknowledge the help you received by clicking "Accept as Solution".
- Once you've solved your issue, please consider posting a summary of any additional details you've learned. Your new knowledge may help others in the future.

Yes absolutely.

In my application I will be just reading the counter value lets say on a different timer interrupt and not within the HAL_TIM_IC_CaptureCallback. 

 

 Thanks for your help.

BarryWhit
Senior III

Wait for JW to weight in though. He is as knowledgeable as could be and, re-reading his earlier comment, I (and AFAICT, the system's behavior) seem to be at odds with his description. Always risky that. Let's see what he says.

 

Also, quite possible, switching the polarity of the other line instead, might align the capture event with the counter change event. Though perhaps only in one direction (but then again-again, maybe your application has a direction you care more about). I've not looked at this too closely tbh.

 

You could also capture the edge on the second input (using another channel), and that way you can be sure you catch the change in a callback. I don't think turning a 30detent encoder would physically result in an interrupt storm, so this might be practical. But it might be even better, as you said, not to to have to rely on the callback at all. If you're concerned it's a bug or problem - I don't think it is. Just a nuance.

- If a post has answered your question, please acknowledge the help you received by clicking "Accept as Solution".
- Once you've solved your issue, please consider posting a summary of any additional details you've learned. Your new knowledge may help others in the future.