3

I have a problem with the quadrature encoder mode on timer TIM3 of my STM32F446RE / NUCLEO-F446RE:

  1. TIM3 counts on every rising edge on the first signal. The CNT register counts up and I read the value with 1 Hz and then I set the register to 0.

    When I look on the oscilloscope the frequency is half as high as the value from the CNT register output (1hz).

    Why?

  2. TIM3 counts on both edges on the first signal. The CNT register output (1 Hz) is completely wrong.

My configuration is:

GPIO_InitTypeDef sInitEncoderPin1;
sInitEncoderPin1.Pin                    = pin1Encoder.pin;  // A GPIO_PIN_6
sInitEncoderPin1.Mode                   = GPIO_MODE_AF_PP;
sInitEncoderPin1.Pull                   = GPIO_PULLUP;
sInitEncoderPin1.Speed                  = GPIO_SPEED_HIGH;
sInitEncoderPin1.Alternate              = altFunctionEncoder; // GPIO_AF2_TIM3

GPIO_InitTypeDef sInitEncoderPin2;
sInitEncoderPin2.Pin                    = pin2Encoder.pin; // A GPIO_PIN_7
sInitEncoderPin2.Mode                   = GPIO_MODE_AF_PP;
sInitEncoderPin2.Pull                   = GPIO_PULLUP;
sInitEncoderPin2.Speed                  = GPIO_SPEED_HIGH;
sInitEncoderPin2.Alternate              = altFunctionEncoder; // GPIO_AF2_TIM3

HAL_GPIO_Init(GPIOA, &sInitEncoderPin1);
HAL_GPIO_Init(GPIOA, &sInitEncoderPin2);

encoderTimer.Init.Period                        = 0xffff;
encoderTimer.Init.Prescaler                     = 0;
encoderTimer.Init.CounterMode                   = TIM_COUNTERMODE_UP;
encoderTimer.Init.ClockDivision                 = TIM_CLOCKDIVISION_DIV1;
encoderTimer.Init.RepetitionCounter             = 0;

HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 1);

encoder.EncoderMode                             = TIM_ENCODERMODE_TI1;
encoder.IC1Filter                               = 0x0f;
encoder.IC1Polarity                             = TIM_INPUTCHANNELPOLARITY_RISING; // TIM_INPUTCHANNELPOLARITY_BOTHEDGE
encoder.IC1Prescaler                            = TIM_ICPSC_DIV1;
encoder.IC1Selection                            = TIM_ICSELECTION_DIRECTTI;

encoder.IC2Filter                               = 0x0f;
encoder.IC2Polarity                             = TIM_INPUTCHANNELPOLARITY_RISING;
encoder.IC2Prescaler                            = TIM_ICPSC_DIV1;
encoder.IC2Selection                            = TIM_ICSELECTION_DIRECTTI;

HAL_TIM_Encoder_Init(&encoderTimer, &encoder);
HAL_TIM_Encoder_Start_IT(&encoderTimer, TIM_CHANNEL_ALL);
HelpingHand
  • 1,294
  • 11
  • 27
Max3579
  • 41
  • 4

1 Answers1

0

The oscilloscope screenshot shows a frequency of about 416 Hz.

  1. The values shown in the first shell output are (very roughly!) twice as high (as the question points out already). This appears (nearly...) correct to me since the shown configuration

    encoder.EncoderMode = TIM_ENCODERMODE_TI1;
    

    selects the "X2 resolution encoder mode", which counts 2 CNT increments per signal period. In an application note on timer overview, (Sec. 4.3.4 / Fig. 7) there is an illustrative diagram how the encoder mode works in detail.

  2. The second screenshot results from an incorrect TIM3 configuration: The encoder mode (TIM_ENCODERMODE_TI1) assumes that both channels trigger only upon directed flanks in an alternating way (see the AN link above).

    If one of the two channels triggers twice as many events due to configuration

    encoder.IC1Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE,
    

    the counter will only count up one position and then "recognize" a "reversal" event (= change of direction). Keeping in mind that

    65535u = 0xFFFF = -1
    

    the second screenshot only shows values -1, 0, +1 - which fits perfectly with this explanation.


The question remains why the first screenshot shows (reproducible) measurements between 800 and 822. I assume that

  • the physical source of the encoder signal runs at a constant pace
  • the 1 Hz timer that triggered shell output is independent from TIM3, and
  • it has been started before the encoder timer (i.e., above the shown code sample).

This may explain why the first two values look like nonsense (0: TIM3 has not been started yet. 545: TIM3 has been started during the shell output timer period). Discarding the first two measurement samples, the average and standard deviation, resp., of the measured signal frequency are

808,9091 +/- 0,5950 [X2 increments per second]

404,4545 +/- 0,2975 [Hz]

which corresponds to a period of

2,4331 +/- 0,003 [ms].

Hence, the measured frequency is too low by about 11 Hz, i.e., measured period too high by nearly 30 µs, and this error is clearly beyond the statistical noise.

The question gives a hint where this error might come from:

The CNT register counts up and I read the value with 1 Hz and then I set the register to 0.

  1. Whenever the 1 Hz "polling timer" expires, it triggers an interrupt (or a logical event in polling software).
  2. Processing of this interrupt/event may be delayed a little, depending on other software (IRQ: deactivation times elsewhere in the software, Polling: loop duration until event is polled).
  3. Software reads CNT value.
  4. Software resets CNT value to zero, discarding further increments since the CNT value has been read.
  5. TIM3 continues counting (from zero).

This hints that software needs 30 µs between (3.) and (4.), which would be quite a lot of time on an STM32F4.

Edit: I just re-checked the oscilloscope screenshot. The error is visible, but I believe it is smaller than I originally assumed (from counting flanks in the picture).

HelpingHand
  • 1,294
  • 11
  • 27