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 10:47

I would attack the issue with a new timer,    set to interrupt every 100mS 

use the encoder hardware to adjust the counters, but not interrupt.

every 100mS, you can see the difference in counts directly.

likely better to ignore counts less than 2 or 3

is one cycle 4 counts ? (no matter.  it is, what it is.)

Posted on April 04, 2018 at 10:59

Yes it will be my approach if I won't able to get the interrupt to work correctly.

The thing is that I want to make a low power system and wake up the system on rotation and in the noted approach scanning the encoder every 100ms will prevent the system to get in to sleep mode unless I'm wrong here?   

Posted on April 04, 2018 at 11:09

Ariel G wrote:

you mean to set an interrupt on timer overflow?

No, I mean on timerupdate that is, each time its value (TIMx->CNT) changes.

Also, be aware that the timer will count both upwardand backward following the rotation direction of your encoder.

You may try to check if you are updating your value only when the encoder is rotating 'upward' with a code like the following :

unsigned int previousValue = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
 if(htim->Instance == TIM2)
 {
 if(previousValue < TIM2->CNT || TIM2->CNT == 0)
 {
 counterISR++;
 previousValue = TIM2->CNT;
 }
 }
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

This is a very basicway of handling the issue but it should provide a better result if you rotate the encoder in only one direction, not too fast, etc...

Please note that you have to handle the case when there is an overflow (the best way is to also trigger an interrupt on overflow interrupt)

I'm assuming that your

HAL_TIM_IC_CaptureCallback

function is called on the right interrupt (which may not be the case).

Finally, if I remember correctly, you can set the capture to trigger on falling, rising or even all edges from one or both of your signals. If you got a x2 or x4 ratio, you should look into that.

Posted on April 04, 2018 at 11:13

Ariel G wrote:

The thing is that I want to make a low power system and wake up the system on rotation and in the noted approach scanning the encoder every 100ms will prevent the system to get in to sleep mode unless I'm wrong here?   

Not really, but you will have to wake up your system every 100ms which is far from perfect

Posted on April 04, 2018 at 11:16

T J wrote:

I would attack the issue with a new timer,    set to interrupt every 100mS 

use the encoder hardware to adjust the counters, but not interrupt.

every 100mS, you can see the difference in counts directly.

likely better to ignore counts less than 2 or 3

is one cycle 4 counts ? (no matter.  it is, what it is.)

Since a timer configured to work as an incremental encoder reader work exactly the same way as a normal timer, it would be a waste to work that way.

Also, it's possible to prescale those timers if needed (count 1 each 3 edges, in example) . All of those features are just handled by the hardware.

Posted on April 04, 2018 at 11:35

we don't sleep... 

:(

I would track the counters in my foreground loop.

I get a much more active response with this coding style.

Like Bill, we don't care about power consumption or saving horsepower or a lack of horsepower

and 256K Ram is all you will ever need.

Posted on April 04, 2018 at 11:54

T J wrote:

I get a much more active response with this coding style.

I don't really get what do you mean by that, since I don't see how you may be faster than pure hardware handling...

It may be more convenient but you got to use a dedicated timer for this purpose, which you don't necessarily have.

Even if you can oversize your MCU doesn't mean that you should always do it (well, that's a personal opinion, though), and I prefer save any timer I can since the last project I worked on were used an F072RB, FreeRTOS @ 10kHz, 4 standards input capture channels, two PWM output and two encoders reading, which required precision and relatively high speed... And worked perfectly fine (even with a colored user interface fully handled by the STM and sent through UART...).

The point is only to use the most out of the peripherals (and forget about the HAL, but that's another story)

Posted on April 04, 2018 at 14:26

No, I mean on timer update that is, each time its value (TIMx->CNT) changes.

You may be misunderstanding the 'update' term used in STM32 TIM chapter - here it does not refer to CNT changes, but to the internal signal, upon which the shadow registers are copied to working registers (eg. PSC, or ARR if it is set so, or the CCR registers when used for output compare and shadowing is set for them). The 'normal' way how this signal is asserted is overflow, but there are other ways too.

JW

Posted on April 04, 2018 at 14:28

You can use the capture interrupt for wakeup, but use only CNT as the output value, don't count interrupts.

JW

Posted on April 04, 2018 at 16:09

That's my bad. 

It's been a long time since I've looked into that and the over/under flow are in fact the only event that trigger an Update event.

In this case, I would simply use the prescaler 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)