cancel
Showing results for 
Search instead for 
Did you mean: 

Using TIMER in Encoder Mode with interrupts on value change

Fabiolino Pancot
Associate II
Posted on June 19, 2018 at 11:55

Hi all,

I'm quite new to the embedded stm32 environment so if I'm asking something stupid please forgive me.

First of all, my setup. 

I'm using a NUCLEO-32bit with STM32L432KC, and we need to test some performances on encoders because we have some problems. 

One of these tests is the one in which I'm stuck. 

Basically I want to use one of the available TIMx  (TIM2) in Encoder Mode with interrupts, and I've successfully managed to do let's say 80% of the work. The only part that I'm stuck with is the interrupt part: I want to fire an interrupt each time the CNT value changes, and also know the direction of the change (i.e. if my encoder rotates CW, increase a variable, if it rotates ACW i want the variable to decrement).

Actually the interrupt fires regularly also when the CNT value of encoder does not changes, so I do not know what I'm doing wrong. And I also experience a strange behaviour of the encoder: also when it is not moved, sometimes the CNT value decrements itself until 0. 

Here my actual code:

/* TIM2 init function */

static void MX_TIM2_Init(void)

{

TIM_Encoder_InitTypeDef sConfig;

TIM_MasterConfigTypeDef sMasterConfig;

htim2.Instance = TIM2;

htim2.Init.Prescaler = 0;

htim2.Init.CounterMode = TIM_COUNTERMODE_UP;

htim2.Init.Period = 200;

htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

sConfig.EncoderMode = TIM_ENCODERMODE_TI12;

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(__FILE__, __LINE__);

}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;

sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}

}

/* MAIN */

int main(void)

{

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MCU Configuration----------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */

HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */

SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_TIM2_Init();

MX_USART2_UART_Init();

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

HAL_TIM_Encoder_Start_IT(&htim2,htim2.Channel);

/* Infinite loop */

/* USER CODE BEGIN WHILE */

while (1)

{

/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */

}

/* USER CODE END 3 */

}

/* the last part is the IRQ handler: */

void TIM2_IRQHandler(void)

{

/* USER CODE BEGIN TIM2_IRQn 0 */

char asd[50];

sprintf(asd,'%d\n',htim2.Instance->CNT);

HAL_UART_Transmit(&huart2,(uint8_t *)asd,sizeof(asd),20);

/* USER CODE END TIM2_IRQn 0 */

HAL_TIM_IRQHandler(&htim2);

/* USER CODE BEGIN TIM2_IRQn 1 */

/* USER CODE END TIM2_IRQn 1 */

}

The part regarding init functions has been automatically generated by CubeMX.

has Anyone an Idea on what's the problem?

Thanks!

#quadrature-encoder #nucleo-l432kc #timer-interrupts #st-cube
1 ACCEPTED SOLUTION

Accepted Solutions
Posted on June 20, 2018 at 10:02

Thank you JW, I've found the (very stupid) mistake I've done in order to mess everything. When connecting my encoder I haven't wired the encoder's GND with the same GND of my nucleo (I connected my encoder Vcc and GND to an external power supply for 5v instead of using the built-in 5V and GND pins of the board). Obviously the signals where completely strange. My bad.

Very poor background in electronics, but fortunately I've found a solution. Thank you for your support anyway!

View solution in original post

4 REPLIES 4
Posted on June 19, 2018 at 15:33

First of all, don't use HAL_UART_Transmit() inside the interrupt.

I want to fire an interrupt each time the CNT value changes, and also know the direction of the change (i.e. if my encoder rotates CW, increase a variable, if it rotates ACW i want the variable to decrement).

This is what TIMx_CNT does, when timer is set in encoder mode. Why would you want to reproduce it in software?

Note that due to latencies etc. you may be missing some of the interrupts, whereas CNT counts up to the filtered inputs rate.

Actually the interrupt fires regularly also when the CNT value of encoder does not changes,

If you move the encoder slightly to one side and then back again, as the result CNT won't change but there are two edges in one of the signals resulting in two interrupts.

Depending on the particular electromechanical construction of the encoder, it may output rapidly bouncing signal if stopped at a particular spot.

sometimes the CNT value decrements itself until 0. 

This indicates either some strange electrical error, or some software bug.

JW

Posted on June 19, 2018 at 16:43

Thank you. 

Effectively the CNT value is doing what I want to. I've found out that the reproducibility of the encoder values are not good. To let you understand, if I start at 0 position, I rotate the knob for let's say X degrees and move back the kob to its starting position, this leads to an error, and not a small one. for example with a range from 0 to 5000, starting from CNT = 0 going to 2000 and then back to the starting position, CNT is like 270. so the error is huge, and I'm investigating why.

Also, thanks for the hint about UART usage.

Posted on June 19, 2018 at 20:47

The distance between edges is probably shorter than the set filter permits. I don't use Cube so don't know what exactly

sConfig.IC1Filter = 15;

means (and not going to look it up in the sources or 'documentation', which I suspect is of no help anyway), but I guess this value goes straight into TIMx_CCMRy.ICzF; and as such depends on setting of the DTS divider (which again I don't know how is set in Cube).

So, observe the input on logic analyzer or oscilloscope while rotating and confront the waveform with the filter's setting together with the given timer's internal clock. Note, that the two inputs *after filtering* must yield edges which are in the expected sequence, which with heavily assymetrical waveforms may cease to be the case.

JW

Posted on June 20, 2018 at 10:02

Thank you JW, I've found the (very stupid) mistake I've done in order to mess everything. When connecting my encoder I haven't wired the encoder's GND with the same GND of my nucleo (I connected my encoder Vcc and GND to an external power supply for 5v instead of using the built-in 5V and GND pins of the board). Obviously the signals where completely strange. My bad.

Very poor background in electronics, but fortunately I've found a solution. Thank you for your support anyway!