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
Kostas Papouis
Associate II

Hello,

The reason the HAL_TIM_IC_CaptureCallback is fired every second turn is because the nature of the incremental encoder I have. The A & B signals go low every second turn and both IC1Polarity and IC2Polarity was set to TIM_ICPOLARITY_FALLING.

 

I have changed the sConfig.IC2Polarity  from  TIM_ICPOLARITY_FALLING to TIM_ICPOLARITY_RISING and now the HAL_TIM_IC_CaptureCallback is fired on each turn of the incremental encoder.

 

The only issue I have is that the counter value decrements when I rotate the encoder clockwise. I need the counter value to increment when I rotate the encoder clockwise and decrement when I rotate the encoder counter clockwise. Is there any way that this can be fixed in firmware?

Any help is much appreciated.

 

 

BarryWhit
Senior III

The timer's "Encoder" mode uses encoder events as the "clock" feeding the counter, which can configured in either  "Up" or "Down" counter mode. Choose which one you need.

(Update: this is wrong, I forgot the DIR(ection) is also taken over by the encoder mode)

 

Sidenote: if you're not using a very nice encoder, be aware that mechanical ones may require debouncing. If you do need to debounce the inputs, you can use the timer's handy "input filter" feature to do so.

 

- 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.

Hi Barry,

Changing the TIM_COUNTERMODE_UP to TIM_COUNTERMODE_DOWN did not fix my problem.

The only way I get the counter to increase while rotate the encoder clockwise is to flip the A & B cables. This is not desirable though as I prefer to do it in the firmware.

 

I tried changing the  IC1Selection and IC2Selection from TIM_ICSELECTION_DIRECTTI (TIM Input 1, 2, 3 or 4 is selected to be connected to IC1, IC2, IC3 or IC4, respectively) to TIM_ICSELECTION_INDIRECTTI (TIM Input 1, 2, 3 or 4 is selected to be connected to IC2, IC1, IC4 or IC3, respectively) however it did not work.

 

 

BarryWhit
Senior III

My bad, you're right. the DIR(ection) signal is also generated by the encoder, I forgot about that.

Did you try playing with the polarities for each channel? The reference manual has all the details, you should go through it.

 

This is from G4 RM but should apply

encoder.jpg

 

In the RM, this table is followed by two waveform diagrams, you should look at those.

- 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.

You can change direction with setting polarity of *one* of the signals, i.e. setting one of the TIMx_CCER.CCxP bits.

I don't understand why would you want to have interrupts upon every edge; I don't think it's possible and I don't quite understand why would changing polarity change that. [EDIT] Re-reading your initial post, I now see you want one interrupt per *detent* - that might be possible if there are several (2 or 4) steps per *detent* and it may then depend on selected polarity. What I wrote about direction of counting above holds. You also can always reverse direction of rotation in software, simply by subtracting current TIMx_CNT value from (TIMx_ARR + 1).  [/EDIT]

I don't use Cube.

JW

Hi @waclawek.jan ,

When I set the two signal polarities to  TIM_ICPOLARITY_FALLING the HAL_TIM_IC_CaptureCallback triggers every second turn. The direction is correct though.

Changing the direction of just one of the signals causes the HAL_TIM_IC_CaptureCallback  to triggere on every turn however  the direction is the opposite o want it to be.

I am using 0011 setting on SMS[3:0]


BarryWhit
Senior III

JW, the forum lets you add a "signature" to your profile, which is automatically appended to every post. Most people use it to solicit donations or gameification (sp?) credits. You might want to place your "motto" there.

- 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.
Kostas Papouis
Associate II

Hi @waclawek.jan  and @BarryWhit ,

Thanks for your help on this.

I have noticed that sometimes the counter1 value within the HAL_TIM_IC_CaptureCallback funtion ( counter1 = __HAL_TIM_GET_COUNTER(&htim2) ) does not update even though the HAL_TIM_IC_CaptureCallback  function does trigger.

On the other hand when I call the the same __HAL_TIM_GET_COUNTER(&htim2) within the while loop the counter always updates.

I have tried adding a filter in the firmware but this did not improve things. On my PCB there is hardware debounce circuit for the A & B signals. 

Is this a limitation of reading the counter value within the interrupt?

 

Thank you. 

 

 

BarryWhit
Senior III

I don't know. Possibly (well, not impossibly), it's due to the mechnical nature of the device, which must have some amount of recoil. It's possible that when you turn the knob just so, a pulse is generated, followed by a slight recoil in the reverse direction. Perhaps a tight loop is fast enough to catch the change, and a slower interrupt invocation isn't. (Though in that case, a polling loop should catch both the +1 and the -1 that immediately follows, so maybe not).

If this is critical to your design, hook up a logic analyzer and watch the waveforms closely as you turn. If you find hard proof that the waveforms are clean and bounce-free, but they aren't being decoded properly, show us.

 

I have tried adding a filter in the firmware but this did not improve things.

I can tell you that when was working with an encoder, tuning the input filter for debouncing was far more finicky than I  expected. But I had no hardware debouncing, and the weird behavior I saw without the filter was not subtle. Once tuned, it worked reliably. I did not however examined whether there was any slop to its operation, there might have been.

- 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.