2024-02-21 06:31 AM
I have an STM32G071CBTx with a rotary encoder wired to GPIO pins PB5 + PB7 (LPTIM1_IN1 and LPTIM1_IN2), but for some reason it will never count down no matter which way the encoder is turned.
I have checked the encoder waveforms and they are as expected, and it's not just "rattling" with one input stuck low or high as I can make the counter run through the full count range by turning the encoder.
Initialisation code (LL) looks like this:
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 */
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);
/*
* 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);
LL_LPTIM_SetEncoderMode(LPTIM1, LL_LPTIM_ENCODER_MODE_RISING_FALLING);
/* USER CODE END LPTIM1_Init 2 */
}
Reading the counter is always starts at 0 and when I turn the encoder it only ever counts UP before wrapping.
I tried setting LL_LPTIM_ConfigClock(LPTIM1, LL_LPTIM_CLK_FILTER_NONE, LL_LPTIM_CLK_POLARITY_RISING) to LL_LPTIM_CLK_POLARITY_RISING_FALLING but it made no difference.
What am I doing wrong here?
Solved! Go to Solution.
2024-02-21 07:11 AM
Well I'm going to answer my own question - by trawling the HAL library, the LL header files and the docs I've arrived at this which does appear to work correctly:
/* LPTIM1 init function */
void MX_LPTIM1_Init(void)
{
/* USER CODE BEGIN LPTIM1_Init 0 */
LL_LPTIM_Disable(LPTIM1);
/* 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_FALLING);
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 */
}
A bit disappointing that the CubeMX/CubeIDE yet again doesn't generate working code but only a part of the initialisation with not even a comment about what's left out.
2024-02-21 07:11 AM
Well I'm going to answer my own question - by trawling the HAL library, the LL header files and the docs I've arrived at this which does appear to work correctly:
/* LPTIM1 init function */
void MX_LPTIM1_Init(void)
{
/* USER CODE BEGIN LPTIM1_Init 0 */
LL_LPTIM_Disable(LPTIM1);
/* 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_FALLING);
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 */
}
A bit disappointing that the CubeMX/CubeIDE yet again doesn't generate working code but only a part of the initialisation with not even a comment about what's left out.