2024-06-27 06:19 AM
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.
Thank you.
2024-06-30 02:57 PM
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.
2024-06-30 05:28 PM - edited 2024-07-01 04:09 AM
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.
2024-07-01 03:50 AM
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.
2024-07-01 04:08 AM - edited 2024-07-01 04:21 AM
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
In the RM, this table is followed by two waveform diagrams, you should look at those.
2024-07-01 04:28 AM - edited 2024-07-01 04:35 AM
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
2024-07-01 04:48 AM
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]
2024-07-01 06:12 AM - edited 2024-07-01 06:12 AM
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.
2024-07-01 08:50 AM
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.
2024-07-01 10:07 AM - edited 2024-07-02 04:45 AM
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.