cancel
Showing results for 
Search instead for 
Did you mean: 

Encoder mode shows only 0 or the max ARR value. Does not increment.

Hrishikesh
Senior

I'm trying to simulate a quadrature encoder signal coming out of an automotive car seat motor. I don't have the motor but I do have the hall sensor used in it (Allegro A1221LLH). The Vout from the sensor is not accessible in the motor and its a 3-pin sensor converted to a 2-pin setup. Hence some hardware processing was needed to make it readable by the STM32F407. The good part is that it works and I see a nice square wave when I rotate a magnet over it (its not strictly quadrature since I have only one sensor for now).

I tried reading the count by configuring TIM2 in encoder mode. However I dont see the count increment. I see either 0 or the maximum ARR value (65535). What am I missing here? I've used the same count function with a standard optical encoder and that works fine. Here is the code:

typedef struct
{
	uint16_t current_count; // variable that stores the current encoder count
	uint16_t previous_count; // variable that stores the previous encoder count
	uint16_t number_of_revs; // variable that stores the number of revs of the encoder
	uint16_t total_count; // variable that stores the total encoder count
} Motor_Encoder;
 
Motor_Encoder M2;
 
uint16_t Get_Encoder_Count(Motor_Encoder *motor, TIM_HandleTypeDef *htim)
{
	motor->current_count = __HAL_TIM_GET_COUNTER(htim);
	motor->total_count = (motor->number_of_revs * __HAL_TIM_GET_AUTORELOAD(htim)) + motor->current_count;
	motor->previous_count = motor->current_count;
	if (motor->total_count < 0) motor->total_count = 0;
	return motor->total_count;
}
 
int main(void)
{
	HAL_Init();
	MX_GPIO_Init();
	MX_TIM2_Init();
 
	__HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);
	HAL_TIM_Base_Start_IT(&htim2); // Encoder M2
	HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
 
	while (1)
	{
		/* USER CODE END WHILE */
 
		/* USER CODE BEGIN 3 */
		Get_Encoder_Count(&M2, &htim2);
		HAL_Delay(1);
	}
}
 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Instance == TIM2)
	{
		__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim2) ? M2.number_of_revs-- : M2.number_of_revs++;
		if (M2.number_of_revs < 0) M2.number_of_revs = 0;
	}
}

1 ACCEPTED SOLUTION

Accepted Solutions

It seems that encoder is configured to use 2 pins, so you need the two inputs, with signal in quadrature. This allows the counter to identify if value should be increment or decremented.

As you have only one signal changing, the counter is doing +1 then -1 then +1.

Pin sequence should be like : 00 then 01 then 11 then 10 . If you change the direction, it will do 00 10 11 01.

View solution in original post

8 REPLIES 8

Be aware that on the F4 that TIM2 and TIM5 are 32-bit

If the counter isn't counting up/down more likely off in the code you're NOT showing..

Check peripheral clocks, both the TIM and GPIO

Check the GPIO pins map to the TIM properly, via AF settings and CH1 or CH2

Check the TIM configuration is correct.

Don't assume CubeMX does it right, inspect all the involved registers, and check against RM documentation.

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

Yes I'm aware of the 32-but counters but how would that matter here? I did not want to paste a wall of standard CubeMX auto-generated code but here is the configuration of the timers, Its configured only for the rising edge of TI1

void MX_TIM2_Init(void)
{
  TIM_Encoder_InitTypeDef sConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
 
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 65535;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 15;
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 15;
  if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
 
}
 
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(tim_encoderHandle->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspInit 0 */
 
  /* USER CODE END TIM2_MspInit 0 */
    /* TIM2 clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();
 
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**TIM2 GPIO Configuration
    PA15     ------> TIM2_CH1
    PB3     ------> TIM2_CH2
    */
    GPIO_InitStruct.Pin = ENC2_A_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
    HAL_GPIO_Init(ENC2_A_GPIO_Port, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = ENC2_B_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
    HAL_GPIO_Init(ENC2_B_GPIO_Port, &GPIO_InitStruct);
 
    /* TIM2 interrupt Init */
    HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
  /* USER CODE BEGIN TIM2_MspInit 1 */
 
  /* USER CODE END TIM2_MspInit 1 */
  }
}

This is the CubeMX configuration for the timers,

0693W000007ClFtQAK.png

rpip.1
Associate III

looks like timer is just count one up then count down,and repeat. You could try change one of the ICPolarity to falling edge.

That did not work. Yes instead of counting to 65535 it counts only 0 or 65535.

For the encoder mode, do both inputs need to be active? I ask because I only one sensor and hence only one input is active.

I see you only have one active signal. you should use External Clock Mode 1. use this signal to clock the timer is appropriate . Another way is to change TIM_ICSELECTION of the inactive input signal to INDIRECT. this may work.

I don't see any of these parameters in the configuration menus. For ICSELECTION I only see DIRECT.

It seems that encoder is configured to use 2 pins, so you need the two inputs, with signal in quadrature. This allows the counter to identify if value should be increment or decremented.

As you have only one signal changing, the counter is doing +1 then -1 then +1.

Pin sequence should be like : 00 then 01 then 11 then 10 . If you change the direction, it will do 00 10 11 01.