Skip to main content
JU_it
Senior
April 8, 2026
Question

G071 LPTIM encoder mode creeps around

  • April 8, 2026
  • 5 replies
  • 528 views

I'm using LPTIM1 in encoder mode and have noticed that, if there's jitter on one of the encoder signals the count will gradually creep up which should not be possible in encoder mode.

This is on a decent encoder that's got an RC network (per the Bourns data sheet / appnote) in front of it too so this isn't nasty HF noise, it's good solid pulses of 50-100ms or more on one channel but the counter seems to creep up.

Identical encoders wired to TIM2 / TIM3 behave normally, and my colleague has seen the same issue in another project using the same setup - LPTIM has a problem where TIMx does not.

Init code for LPTIM1:

/* LPTIM1 init function */
void MX_LPTIM1_Init(void)
{
 /* USER CODE BEGIN LPTIM1_Init 0 */
 /* USER CODE END LPTIM1_Init 0 */
 LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
 LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM1_CLKSOURCE_PCLK1);
 /* Peripheral clock enable */
 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LPTIM1);
 LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
 /**LPTIM1 GPIO Configuration
 PB5 ------> LPTIM1_IN1
 PB7 ------> LPTIM1_IN2
 */
 GPIO_InitStruct.Pin = ENC1_As_Pin;
 GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
 GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
 GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
 GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
 GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
 LL_GPIO_Init(ENC1_As_GPIO_Port, &GPIO_InitStruct);
 GPIO_InitStruct.Pin = ENC1_Bs_Pin;
 GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
 GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
 GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
 GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
 GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
 LL_GPIO_Init(ENC1_Bs_GPIO_Port, &GPIO_InitStruct);
 /* USER CODE BEGIN LPTIM1_Init 1 */
 /* USER CODE END LPTIM1_Init 1 */
 LL_LPTIM_SetClockSource(LPTIM1, LL_LPTIM_CLK_SOURCE_INTERNAL);
 LL_LPTIM_SetPrescaler(LPTIM1, LL_LPTIM_PRESCALER_DIV1);
 LL_LPTIM_SetPolarity(LPTIM1, LL_LPTIM_OUTPUT_POLARITY_REGULAR);
 LL_LPTIM_SetUpdateMode(LPTIM1, LL_LPTIM_UPDATE_MODE_IMMEDIATE);
 LL_LPTIM_SetCounterMode(LPTIM1, LL_LPTIM_COUNTER_MODE_EXTERNAL);
 LL_LPTIM_ConfigClock(LPTIM1, LL_LPTIM_CLK_FILTER_NONE, LL_LPTIM_CLK_POLARITY_RISING);
 LL_LPTIM_TrigSw(LPTIM1);
 LL_LPTIM_SetInput1Src(LPTIM1, LL_LPTIM_INPUT1_SRC_GPIO);
 LL_LPTIM_SetInput2Src(LPTIM1, LL_LPTIM_INPUT2_SRC_GPIO);
 /* USER CODE BEGIN LPTIM1_Init 2 */
	/*
	 * To activate the Encoder mode the ENC bit has to be set to ‘1’. The LPTIM must first be
	 * configured in Continuous mode.
	 * When Encoder mode is active, the LPTIM counter is modified automatically following the
	 * speed and the direction of the incremental encoder.
	 */
	LL_LPTIM_StartCounter(LPTIM1,LL_LPTIM_OPERATING_MODE_CONTINUOUS);
	// This function must be called when the LPTIM instance is disabled.
	LL_LPTIM_SetEncoderMode(LPTIM1, LL_LPTIM_ENCODER_MODE_RISING/*_FALLING*/);
	/*
	 * This function must be called when the LPTIM instance is disabled.
	 * In this mode the LPTIM instance must be clocked by an internal clock
	 * source. Also, the prescaler division ratio must be equal to 1.
	 * LPTIM instance must be configured in continuous mode prior enabling
	 * the encoder mode.
	 */
	LL_LPTIM_EnableEncoderMode(LPTIM1);
	LL_LPTIM_Enable(LPTIM1);
	/*
	 * The counter just counts continuously between 0 and the
	 * auto-reload value programmed into the LPTIM_ARR register
	 * (0 up to ARR or ARR down to 0 depending on the direction).
	 * Therefore LPTIM_ARR must be configured before starting
	 */
	LL_LPTIM_SetAutoReload(LPTIM1, ENCODER_RELOAD);
	//LPTIM instance must be enabled before starting the counter.
	LL_LPTIM_StartCounter(LPTIM1,LL_LPTIM_OPERATING_MODE_CONTINUOUS);
 /* USER CODE END LPTIM1_Init 2 */
}

 

5 replies

waclawek.jan
Super User
April 8, 2026

> creep up

What does that mean, exactly?

> if there's jitter on one of the encoder signals

Please elaborate; if possible, illustrating it with oscilloscope screenshots.

It also sounds like you do have some setup where there's no such "creep up", do you?

JW

JU_it
JU_itAuthor
Senior
April 8, 2026

JW,

By "creep up" I mean that it's possible to make the LPTIM count gradually increase without the encoder actually moving off of position - just by ONE channel of the encoder signal going high/low EG because the encoder transition is on a detent position, or someone is resting their finger on it & moving it slightly across the transition without actually rotating it - we can see the encoder *count* in LPTIM increment more than a full circle if there are enough pulses on one channel.

And to be clear - this is not noise on the signal, it's very clear clean robust pulses as I described that should only result in the encoder/timer count going +1 count and then back again -1 because the other channel never changes state.

The setup where there is no creep is the other 2 encoders attached to the MCU - running on TIM2 and TIM3, the hardware is identical for all 3 channels.

waclawek.jan
Super User
April 8, 2026

And when this happens, the "steady" other channel is low or high?

Can you please read out and post the LPTIM registers content.

JW

JU_it
JU_itAuthor
Senior
April 8, 2026

I will have to change my setup to read out the registers - I have attached a scope trace of the problem happening, this is purely from me jiggling the encoder so it goes back & forth over a transition on one channel, as you can see the other channel is steady in the "high" state.

According to the datasheet the encoder detent stability point is always when channel A is high and B is near switching.

waclawek.jan
Super User
April 8, 2026

Can you please zoom in on the edges (both rising and falling) so that we see the RC time constant?

JW

JU_it
JU_itAuthor
Senior
April 8, 2026

..

waclawek.jan
Super User
April 9, 2026

Okay so rise/fall times in 100us range? What's the LPTIM's internal clock?

As a quick test, I'd at least try to switch on the glitch filter, if you don't have it already switched on (I don't understand the Cube/LL gobbledygook, mainly that's why I asked for registers content).

JW

JU_it
JU_itAuthor
Senior
April 9, 2026

LPTIM clock is 16MHz, currently no filtering but I'm just adding that in as an experiment, I will also pull register info out.

JU_it
JU_itAuthor
Senior
April 9, 2026
LPTIM1:
0x40007C00=0x00000010
0x40007C04=0x00000000
0x40007C08=0x00000000
0x40007C0C=0x01800018
0x40007C10=0x00000001
0x40007C14=0x00000000
0x40007C18=0x000000FF
0x40007C1C=0x00000000
0x40007C20=0x00000000
waclawek.jan
Super User
April 9, 2026

OK I see you've already enabled the glitch filter - did it make any difference?

JW

JU_it
JU_itAuthor
Senior
April 9, 2026

No difference - wiggling the encoder produces the fairly clean pulse train in the trace below and a string of counts in one direction only (count rising).

I'm modifying my code to spit out the raw count rather than deltas just to see if anything leaps out.

JU_it
JU_itAuthor
Senior
April 14, 2026

Well even trying a wrinkle mentioned in the reference manual didn't help...

RM0444 pg 842:


It should be noted that to read reliably the content of the LPTIM_CNT register two
successive read accesses must be performed and compared. A read access can be
considered reliable when the value of the two read accesses is equal. Unfortunately
when asynchronous reset is enabled there is no possibility to read twice the
LPTIM_CNT register.

The code:

// Read counter until value is the same twice
while(now != chk)
{
	if(e > 0)
	{
		NOP;
	}
	now = LL_LPTIM_GetCounter((LPTIM_TypeDef *)gEncs[enc]);
	chk = LL_LPTIM_GetCounter((LPTIM_TypeDef *)gEncs[enc]);
	e++;
}

Unless I span the encoder very fast I never saw the two values disagree with each other, even while the encoder count was creeping up with successive up/down twiddling of the encoder.

So, is there some bug with the LPTIM peripheral or have I missed something else in the code / config?