2020-01-29 04:24 PM
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!
2020-01-29 07:15 PM
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?
2020-01-30 02:27 AM
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.
2020-02-01 05:12 PM
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.
2020-02-01 05:28 PM
Only Ch1 and Ch2 inputs
2020-02-01 05:38 PM
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
2020-02-02 02:41 AM
> 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
2020-02-02 09:30 AM
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.