cancel
Showing results for 
Search instead for 
Did you mean: 

Odd encoder filter behavior

MNels.3
Associate II

I'm using a Nucleo-F746ZG with timer 2 as a quadrature encoder input. I'm feeding it quadrature signals from 2 GPIOs at 250Hz (each phase is 1mS).

The CDR value is 0, so FDTS should be CLK_INT (36MHz for APB1). With both inputs' filter set to 8, the quadrature counter works flawlessly, counting from 0 to 10,000 and back to 0 for 60+ hours. A filter setting of 10 also works flawlessly. On my ancient 20Mhz scope the GPIO signals are very clean with a low drive setting.

Setting the filter to 9 produces random, spurious counts.

I'm not sure why this notch should exist. My calculations are that a filter of 8 takes 1.33uS to match, 9 is 1.78uS, and 10 should take 2.22uS. If 8 < 9 < 10 how does 8 and 10 work and 9 does not? Shouldn't a filter setting of 9 also have a 1000x chances to match the input (2mS / 1.78uS).

I also see the filters >=12 start to prevent accurate counts. Again, these seem very fast compared to a 250Hz input signal and it seems they should all match OK.

Thanks!!

1 ACCEPTED SOLUTION
9 REPLIES 9

> The CDR value is 0,

You mean, TIMx_CR1.CKD?

> With both inputs' filter set to 8,

What do you mean by that, the value written into TIMx_CCMRx.ICxF? How do you set it?

Read out and check/post the TIM registers content.

JW

MNels.3
Associate II

Yes, the CR1.CKD value is zero. Sorry for the confusion.

I'm using the cubeMX generated code, changing the filter value, and recompiling and reflashing. This is from main.c.

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 4294967295;
  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 = 8;
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 8;
  if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

Timer 2 regs. This changes from 0x00008181, 0x00009191, and 0x0000a1a1 for this issue. It's CCMR1 when in input capture mode.

0693W00000AOqypQAD.jpg 

Thanks for the help!

All this looks exactly as expected.

Well, that's then strange indeed, and if the value of CCMR1 register is the only thing which changes between the cases, don't know the reason for this behaviour...

JW

MNels.3
Associate II

Thanks for the reply. I'm lost, too. Electrically, the digital oversampling is a low pass filter, but the quadrature signal should be orders of magnitude below the cut off. Even overshoot and ringing don't make sense since those are usually in nano seconds. It seems to afflict both signals since the count randomly jerks around, but mostly counts down even when the quadrature phase is up. And they're GPIOs on 6" jumpers, not even a real encoder.

This is the quadrature code. It's running under freeRTOS on it's own thread (of two, the other is the UART to dump out data). There's very little going on - though that should only make it take longer. It clocks in some quadrature signals, reads the encoder count, and prints it out.

static void encoder_pulses(bool dir, int cnt)
{
    int cycles;
 
    if (dir)
    {
        for (cycles = 0; cycles < cnt; cycles++)
        {
            HAL_GPIO_WritePin(GPIO_A_GPIO_Port, GPIO_A_Pin, GPIO_PIN_SET);
            osDelay(1);
            HAL_GPIO_WritePin(GPIO_B_GPIO_Port, GPIO_B_Pin, GPIO_PIN_SET);
            osDelay(1);
            HAL_GPIO_WritePin(GPIO_A_GPIO_Port, GPIO_A_Pin, GPIO_PIN_RESET);
            osDelay(1);
            HAL_GPIO_WritePin(GPIO_B_GPIO_Port, GPIO_B_Pin, GPIO_PIN_RESET);
            osDelay(1);
        }
    }
    else
    {
        for (cycles = 0; cycles < cnt; cycles++)
        {
            HAL_GPIO_WritePin(GPIO_B_GPIO_Port, GPIO_B_Pin, GPIO_PIN_SET);
            osDelay(1);
            HAL_GPIO_WritePin(GPIO_A_GPIO_Port, GPIO_A_Pin, GPIO_PIN_SET);
            osDelay(1);
            HAL_GPIO_WritePin(GPIO_B_GPIO_Port, GPIO_B_Pin, GPIO_PIN_RESET);
            osDelay(1);
            HAL_GPIO_WritePin(GPIO_A_GPIO_Port, GPIO_A_Pin, GPIO_PIN_RESET);
            osDelay(1);
        }
    }
}

And what happens if you replace that osDelay with a plain time-wasting loopdelay, e.g.

static inline void LoopDelay(volatile uint32_t nCount) {  // a little dirty loopdelay
    while(nCount--) {   }
  }

(and of course some reasonable parameter, say 100000) ?

JW

MNels.3
Associate II

I think I found it. The GPIO signals were flawless until I plugged GPIO_B in to PA1. Suddenly it has a 50MHz, 1.5Vpp signal on it. We had long since removed Ethernet in cubeMX, but the PHY is still attached to pin PA1 via SB13. It's referenced in Table 12, but a real schematic would have saved a lot of time and trouble.

Thanks for the help!

Mike

Nice catch! Thanks for coming back with the solution. Please select your post as Best so that thread is marked as solved.

I always considered the Nucleo boards - as opposed to the Discos and Evals - to be completely "bare", with almost no opportunity for collison with onboardperipherals(except with the serial-to-VCP to the onboard STLink). I forgot that with the Nucleo144 this is no longer the case.

JW

MNels.3
Associate II

Thanks! I did not think to look under CAD resources. I had found a Nucleo-F767 schematic a bit ago that was helpful (I suspect they're the same for the 144 pin boards.)

Mike