cancel
Showing results for 
Search instead for 
Did you mean: 

Encoder mode and rotary encoder

Ariel G
Associate II
Posted on April 03, 2018 at 11:10

Hi,

I'm using STM32F072RB and a rotary encoder (with 30 dentet) connected to TIM2 in 'encoder mode' and I can see in TIM2->CNT that it is actually counting correctly.

I trying to get an interrupt on each detent rotation but unsuccessfully

To start the encoder in interrupt mode in the main function I'm using:

HAL_TIM_Encoder_Start_IT(&htim2, TIM_ENCODERMODE_TI2);

And the callback:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)

{

if(htim->Instance == TIM2)

counterISR++;

}

but the counterISR is not equal to TIM2->CNT.

How to get an interrupt of each detent rotation? or what is the correct way to implement such thing?

Timer 2 init:

/* TIM2 init function */

static void MX_TIM2_Init(void)

{

TIM_Encoder_InitTypeDef sConfig;

TIM_MasterConfigTypeDef sMasterConfig;

htim2.Instance = TIM2;

htim2.Init.Prescaler = 0;

htim2.Init.CounterMode = TIM_COUNTERMODE_UP;

htim2.Init.Period = 29;

htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

sConfig.EncoderMode = TIM_ENCODERMODE_TI2;

sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;

sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;

sConfig.IC1Prescaler = TIM_ICPSC_DIV1;

sConfig.IC1Filter = 0x0F;

sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;

sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;

sConfig.IC2Prescaler = TIM_ICPSC_DIV1;

sConfig.IC2Filter = 0x0F;

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

{

_Error_Handler(__FILE__, __LINE__);

}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;

sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

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

{

_Error_Handler(__FILE__, __LINE__);

}

}

Note: this post was migrated and contained many threaded conversations, some content may be missing.
24 REPLIES 24
Posted on April 04, 2018 at 20:11

the over/under flow are in fact the only event that trigger an Update event.

Strictly speaking, no. You can generate Update event also by writing 1 into TIMx_EGR.UG, and also by the slave mode controller, in Reset mode (or the combined trigger+reset mode in newer STM32 models). Also TIMx_CR1.UDIS and .URS influence the exact behaviour of Update, though the RM is not very precise in describing this.

In this case, I would simply use the prescaler

No. Never use the prescaler in encoder mode, unless you like nasty surprises. The prescaler here counts regardless of the up/down setting, so at the direction reversal CNT ends up with a surprising value.

and/or the maximum value (ARR if I remember correctly) to generate an overflow each time I need an event.  (For a set number of edges)

The Update interrupt comes upon over/underflow, but that may never happen, if the encoder operates up-down in a narrow range and stays between 0 and ARR.

Capture interrupt is here appropriate for wakeup.

Should more sophistication be needed, after waking up because of the encoder being turned (i.e. from the capture interrupt), I would change the wakeup source to a fixed timer and would go to sleep for a fixed time. If, upon waking up from that fixed time, I'd found the encoder did not move anymore, I'd go back to the wakeup from capture.

JW

Posted on April 05, 2018 at 14:06

waclawek.jan wrote:

No. Never use the prescaler in encoder mode, unless you like nasty surprises. The prescaler here counts regardless of the up/down setting, so at the direction reversal CNT ends up with a surprising value.

That's a silly behavior I didn't knew about... Are you sure about it ? I don't find any warning about that in the RM (for F0x1/2/8 ).

However, I will be extra careful now ! 

Thanks for the precisions ! 

Jeff Tenney
Senior

How to get an interrupt of each detent rotation? or what is the correct way to implement such thing?

I'm looking at the same question right now.

Unfortunately, the timers don't seem to offer an interrupt on the "count" event (CK_CNT). That would have been very helpful for rotary encoders with detents.

So I'm using CCR3 and CCR4 in compare mode, where CCR3 is set to CNT-1 and CCR4 is set to CNT+1. That way, if CNT changes, you get an interrupt.

I also thought about capturing "both" rising and falling edges into CCR1 and/or CCR2, but the reference manual specifically says not to set CC1NP / CC2NP, which is required to capture both edges.

Jeff

Perhaps you could use an EXTI off the input?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Jeff Tenney
Senior

Hi Clive - Yes that would work but in my case the timer is also providing filtering (debounce).