I'm trying to use TIM4 for quadrature encoder input on my STM32F4DISCOVERY board.
Here is my code :
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6| GPIO_Pin_7;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_TIM4);
/* Configure the timer */
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
/* TIM4 counter enable */
TIM_Cmd(TIM4, ENABLE);
sadly, the TIM4->CNT counter won't move when I turn the encoder. I works perfectly with TIM8.
Here is the full code of the working TIM8 and the not working TIM4 :
https://gist.github.com/nraynaud/5082298
I check by printing rT2() in gdb after moving the encoder by hand.
I have used an STM32F407 to read encoder counts from 3 optical encoders. I am using ChibiOS RTOS so the timer struct is slightly different from the ST Peripheral library timer struct, but the information is basically the same. Here is how I configure the registers of the actual timers:
stm32_tim_t * encoderTimers[3] = {STM32_TIM8, STM32_TIM3, STM32_TIM4};
for (auto timer : encoderTimers) {
timer->SMCR = 3; // Encoder mode 3
timer->CCER = 0; // rising edge polarity
timer->ARR = 0xFFFF; // count from 0-ARR or ARR-0
timer->CCMR1 = 0xC1C1; // f_DTS/16, N=8, IC1->TI1, IC2->TI2
timer->CNT = 0; // Initialize counter
timer->EGR = 1; // Generate an update event
timer->CR1 = 1; // Enable the counter
}
Also, make sure you enable the timer peripherals in the APB1 or APB2 registers of the RCC. Something like:
RCC->APB1ENR |= ((1 << 2) // TIM4 -- Front wheel angle measurement
| (1 << 1));// TIM3 -- Steer angle measurement
Finally, you need to make sure you configure the GPIO settings correctly. This means configuring the GPIO for Alternate Function.
You may need to use pull ups for input unless you are using external resistors
or powering the encoder. I usually connect encoder to ground and use internal pull ups to set idle state.
encoder with standard driver
#include "mbed.h"
#include "stm32f4xx.h"
#include "stm32f4xx_hal_tim_ex.h"
TIM_HandleTypeDef timer;
TIM_Encoder_InitTypeDef encoder;
//direction to PA_9 -- step pulse to PA_8
int main(){
GPIO_InitTypeDef GPIO_InitStruct;
__TIM1_CLK_ENABLE();
__GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
timer.Instance = TIM1;
timer.Init.Period = 0xffff;
timer.Init.Prescaler = 0;
timer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
timer.Init.CounterMode = TIM_COUNTERMODE_UP;
encoder.EncoderMode = TIM_ENCODERMODE_TI12;
encoder.IC1Filter = 0x0f;
encoder.IC1Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
encoder.IC1Prescaler = TIM_ICPSC_DIV4;
encoder.IC1Selection = TIM_ICSELECTION_DIRECTTI;
encoder.IC2Filter = 0x0f;
encoder.IC2Polarity = TIM_INPUTCHANNELPOLARITY_FALLING;
encoder.IC2Prescaler = TIM_ICPSC_DIV4;
encoder.IC2Selection = TIM_ICSELECTION_DIRECTTI;
HAL_TIM_Encoder_Init(&timer, &encoder);
HAL_TIM_Encoder_Start(&timer,TIM_CHANNEL_1);
TIM1->EGR = 1; // Generate an update event
TIM1->CR1 = 1; // Enable the counter
while (1) {
int16_t count1;
count1=TIM1->CNT;
printf("%d\r\n", count1);
wait(1.0);
};
}