cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F103RCT Two rotary encoders on timers 2 and 3 Nothing works!

PPiro.1
Associate II

Hi! I am trying to get two encoders running on my STM32F103RCT6, I had it previously accomplished on STM32L152RCT6 and everything worked, but I needed to change the MCU, to something with a DMA to Peripheral mapping.

I am using exact same board layout that I used for my old MCU, which is great with the STM32 family.

First I tried setting the Encoder mode by simply setting the necessary registers and not using the HAL libraries, but that didn't work for me, then I gave the HAL a shot because I started doubting myself. But even with the help of HAL libraries the TIMx -> CNT register of neither of the timers is changing the value.

This is what the HAL came up with:

static void MX_TIM2_Init(void)
{
 
  TIM_Encoder_InitTypeDef sConfig;
  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_IC_InitTypeDef sConfigIC;
 
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 0;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_IC_Init(&htim2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
  sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
  sConfig.IC1Polarity = TIM_ICPOLARITY_FALLING;
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 0;
  sConfig.IC2Polarity = TIM_ICPOLARITY_FALLING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 0;
  if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_3) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
  if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_4) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
}
 
/* TIM3 init function */
static void MX_TIM3_Init(void)
{
 
  TIM_Encoder_InitTypeDef sConfig;
  TIM_MasterConfigTypeDef sMasterConfig;
 
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 0;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
  sConfig.IC1Polarity = TIM_ICPOLARITY_BOTHEDGE;
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 0;
  sConfig.IC2Polarity = TIM_ICPOLARITY_BOTHEDGE;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 0;
  if (HAL_TIM_Encoder_Init(&htim3, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
}

As a side note, neither one of my encoders is externally pulled up, and the encoders are wired to ACTIVE - LOW so I need to use internal puill-ups. I am using PA2 / PA3 with TIM2 and PB4 / PB5 with TIM3

and this is a GPIO configuration that HAL generated:

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* htim_ic)
{
 
  GPIO_InitTypeDef GPIO_InitStruct;
  if(htim_ic->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspInit 0 */
 
  /* USER CODE END TIM2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();
 
    /**TIM2 GPIO Configuration
    PA0-WKUP     ------> TIM2_CH1
    PA1     ------> TIM2_CH2
    PA2     ------> TIM2_CH3
    PA3     ------> TIM2_CH4
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  /* USER CODE BEGIN TIM2_MspInit 1 */
 
  /* USER CODE END TIM2_MspInit 1 */
  }
 
}
 
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* htim_encoder)
{
 
  GPIO_InitTypeDef GPIO_InitStruct;
  if(htim_encoder->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspInit 0 */
 
  /* USER CODE END TIM3_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM3_CLK_ENABLE();
 
    /**TIM3 GPIO Configuration
    PB4     ------> TIM3_CH1
    PB5     ------> TIM3_CH2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
    __HAL_AFIO_REMAP_TIM3_PARTIAL();
 
  /* USER CODE BEGIN TIM3_MspInit 1 */
 
  /* USER CODE END TIM3_MspInit 1 */
  }
 
}

Any help will be greatly appreciated!

Thank you!

7 REPLIES 7
TDK
Guru

Are GPIOA and GPIOB clocks enabled? I don't see that in the initialization.

If you probe the pins, are they going high/low as expected?

If you feel a post has answered your question, please click "Accept as Solution".
PPiro.1
Associate II

Thank you for your reply,

yes the clocks for GPIOA and GPIOB are enabled, I have two perfectly functioning buttons on those ports.

The AFIO clock is on too, to set the alternate functions.

Using “System Workbench for STM32�? in debug mode I am able to read GPIOx->IDR registers and the pin states are changing as I am rotating the encoders (I rotate them somewhere between the clicks and hit pause), but the TIMx-CNT doesn’t get updated for some odd reason.

Is it possible that only specific pins can be mapped/MUXed for reading the encoders? But if those weren’t, then why would the CubeMX give me options to set those up on the given pins. 

In the table 5 of datasheet for LQFP64 package, it states that default Alternate functions for PA2 & PA3 are TIM2 CH3/TIM2 CH4 respectively and REMAPPED alternate functions for PB4 & PB5 are TIM3 CH1 / TIM3 CH2 respectively.

PPiro.1
Associate II

Will the encoder work on channels 3 and 4 ?

From what I see in the reference manual, it appears as only Channels 1,2 & 3 are connected to a XOR gate.

Only Ch1 and Ch2 inputs​

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

Thank you, I will need to redesign my board then, to use some other pins.

I am still puzzled about PB4 and PB5 which are set to TIM3 CH1 and CH2

> I am still puzzled about PB4 and PB5 which are set to TIM3 CH1 and CH2

Are they? Have you checked all related fields in AFIO's registers, e.g.

To use the serial wire DP to release some GPIOs, the user software must set

SWJ_CFG=010 just after reset.

?

'F1's immature pinmux scheme in conjunction with the inadequate documentation is a real pain.

JW

PPiro.1
Associate II

Panie Janie,

thank you for the response.

When I did the same thing on my STM32L1 it wasn't nearly as much pain, as long as you get an idea of how to read the reference manuals

you can set everything as stated in the specifications, no grey areas with most of the stuff straight forward.

Your suggestion worked !! The PB4 was used for NJRST and I had to disable it and set the remapped alternate function to TIM3 CH1 & CH2, another thing that kept

me from getting it working was that setting the CRL for PB4 with it's reset values for some reason was setting PB4 into a different mode and the IDR4 for Port B was reading 0 as soon as the settings were changed to what was supposed to be the right settings.

The reset state in CNF is Floating Input and I was setting it to floating Input again regardless of the fact that it was already set to Floating input.

Thank you very much for pointing me the right direction! Now I need to redesign my PCB to use other Timer with channels 1 & 2 for the other encoder.