cancel
Showing results for 
Search instead for 
Did you mean: 

TIM1 configured as an encoder counts back even when the encoder is moving forward

Hrishikesh
Senior
Posted on June 21, 2018 at 19:24

I followed some examples to create a CubeMX code to configure TIM1 on my STM32F103C8T6 as an Encoder. My encoder is a cheap 20 PPR one that comes with a breakout board. I've hardware debounced it using a 10K and 0.1uF RC filter. Apart from this the A and B pins are pulled high using 10K resistors.The encoder is set to count from 0 to 19 and based on whether its counting up or down I determine whether the encoder being turned clockwise or counter clockwise. This status is then displayed on a 20x4 HD44780 LCD.

For the most part, this code works but every once in a while I see that the count slips back even when the encoder is being turned so that it counts up. Why is this? Is this an encoder hardware fault? Or is this something to do with software? How do I resolve this?

This is the main while loop that reads the encoder direction and displays the status on the LCD:

while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
 if (GetEncoderDirection() == 1) HD44780_Puts(0, 0, 'Clockwise ');
 if (GetEncoderDirection() == 0) HD44780_Puts(0, 0, 'Counter clockwise');
 //if (GetEncoderDirection() == 2) HD44780_Puts(0, 0, 'Glitch ');
 if (GetEncoderButtonState() == 1) HD44780_Puts(0, 3, 'Button pressed!');
 else HD44780_Puts(0, 3, g_line1);

}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

This is the function that gets the encoder direction. A direction value of 1 means the encoder is being turned CW

uint8_t GetEncoderDirection(void) {
uint8_t l_curr_count = htim1.Instance->CNT;
sprintf(buffer, '%d', l_curr_count);
HD44780_Puts(0, 1, ' ');
HD44780_Puts(0, 1, buffer);
if (l_curr_count == 0 && g_last_value == ENCODER_PPR) g_encoder_direction = 1; // moving CW
else if (l_curr_count == ENCODER_PPR && g_last_value == 0) g_encoder_direction = 0; // moving CCW
else if (l_curr_count - g_last_value > 0) g_encoder_direction = 1; // moving CW
else if (l_curr_count - g_last_value < 0) g_encoder_direction = 0; // moving CCW
//else if (l_curr_count - g_last_value == 0) g_encoder_direction = 2; // encoder glitch
g_last_value = l_curr_count;
sprintf(buffer, '%d', g_last_value);
HD44780_Puts(0, 2, ' ');
HD44780_Puts(0, 2, buffer);
return g_encoder_direction;
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

This is the CubeMX generated TIM1 code:

#define ENCODER_PRESCLAER 4 // If this is not set, then the counts are in multiples of 4
#define ENCODER_PPR 19 // (0-19 = 20 PPR)
#define ENCODER_FILTER 15
void MX_TIM1_Init(void)
{
 TIM_Encoder_InitTypeDef sConfig;
 TIM_MasterConfigTypeDef sMasterConfig;
 htim1.Instance = TIM1;
 htim1.Init.Prescaler = ENCODER_PRESCLAER;
 htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
 htim1.Init.Period = ENCODER_PPR;
 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 htim1.Init.RepetitionCounter = 0;
 htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
 sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
 sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
 sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
 sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
 sConfig.IC1Filter = ENCODER_FILTER;
 sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
 sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
 sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
 sConfig.IC2Filter = ENCODER_FILTER;
 if (HAL_TIM_Encoder_Init(&htim1, &sConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

#stm32-encoder #stm32f103-tim1 #tim1-timer
6 REPLIES 6
Posted on June 21, 2018 at 19:44

The Prescaler value needs to be zero. It does not implement an up/down counter. Scale the count read from CNT if you must.

The IC1Prescaler/IC2Prescaler (on the pins) might, but wouldn't bet money on it.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on June 21, 2018 at 19:52

#define ENCODER_PRESCLAER 4 // If this is not set, then the counts are in multiples of 4

Don't use the prescaler in encoder mode, it may cause surprising results and it may be the cause of the symptoms you are describing. The reason is, that the prescaler does not count up/down as the direction changes.

Instead of that, divide the result by 4.

Using a short period and relying on that you certainly catch all transitions, may also be not the best idea; but that depends on the particularities of the code. What happens, for example, if

g_last_value == ENCODER_PPR 

and you turn the encoder fast enough (compared to duration of

HD44780_Puts() 

which is probably the longest-lasting routine in there), so that the next time when you call

GetEncoderDirection

() the counter is already at value 1 instead of 0?

JW

Posted on June 21, 2018 at 19:57

The IC1Prescaler/IC2Prescaler (on the pins) might, but wouldn't bet money on it.

If IC1Prescaler/IC2Prescaler is (I don't speak Cube's gibberish) is TIMx_CCMR1.IC1PSC/.IC2PSC, then it's ignored in encoder mode, as the slave-mode controller taps off before that (and before the capture-selector/multiplexer). In other words, it influences only the capture itself - and that is useless in encoder mode (although I see it used here sometimes).

JW

Posted on June 22, 2018 at 18:25

Thanks. My logic to decode direction was based on the fact that the counter would count from 20 steps in steps of 4. However, I just used a new encoder and now there is a new problem. The encoder counts from 0 to 20 in steps of 4, then from 3 to 19, then from 2 to 18, 1 to 17 and then again from 0 to Is this normal?

This was after:

htim1.Init.Period = 20;�?�?

because the encoder has 20 detents and I'm assuming its 20 PPR though I'm not sure. Am I defining this correctly? What am I missing here? Also the reference manual says that the encoder direction is also captured in the `TIMx_CR1` register. How do I access values from this?

Posted on June 22, 2018 at 18:28

The value is programmed as N-1, so if you want CNT 0..19 (20 ticks) Period = 19, right now you've selected CNT 0..20 (21 ticks)

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on June 22, 2018 at 18:51

Sheesh! Thank you. It works fine now.