QDEC Peripheral with high speed encoder

Hello,

I have been testing an encoder using the QDEC peripheral.

The encoder produces 256 pulses per rotation. Testing without any load assigned to the motor, the encoder rotates at 2000-3000 rpm depending on the input.

Using the zephyr sensor api or the nrfx_qdec driver I have not been able to get correct measurements.

I tried lowering the SAMPLEPER to the lowest possible setting (128us) but it didn't work either.

However, using gpio pin interrupts I have been able to get correct measurements.

Is the QDEC peripheral suitable for such a frequency of pulses ?

Is there any other way to do this to avoid using multiple interrupts (like GPIOTE and TIMER as I read somewhere) ?

Parents
  • Hi 

    If my math is correct you will get pulses every 80us (meaning a rising or falling flank every 40us) when the encoder is rotating at 3000 RPM with 256 pulses pr rotation. This is too fast for the QDEC, even at its fastest setting. 

    It is true that you can use the GPIOTE, TIMER and PPI peripherals to measure the length of a pulse without having to rapidly process an interrupt. A simple way to do this is simply to have a free running timer that will trigger a capture and clear every time you get a pulse, and the CC register will then change depending on how long time it takes between captures. 

    The simple example included below (written for the nRF52 series originally) illustrates this concept. To run on the nRF5340 it will need some rewriting because of the difference between the PPI and DPPI peripherals. 

    #define PC_TIMER		NRF_TIMER0
    #define PC_GPIOTE_CH	0
    #define PC_PPI_CH		0
    
    static void pulse_capture_init(uint32_t pin_num)
    {
    	PC_TIMER->BITMODE = TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos;
    	PC_TIMER->PRESCALER = 0;
    	NRF_GPIOTE->CONFIG[PC_GPIOTE_CH] = 	GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |
    										GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos |
    										pin_num << GPIOTE_CONFIG_PSEL_Pos;
    	NRF_PPI->CH[PC_PPI_CH].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[PC_GPIOTE_CH];
    	NRF_PPI->CH[PC_PPI_CH].TEP = (uint32_t)&PC_TIMER->TASKS_CAPTURE[0];
    	NRF_PPI->FORK[PC_PPI_CH].TEP = (uint32_t)&PC_TIMER->TASKS_CLEAR;
    	NRF_PPI->CHENSET = (1 << PC_PPI_CH);
    
    	PC_TIMER->TASKS_START = 1;
    }
    
    static uint32_t pulse_capture_get()
    {
    	return PC_TIMER->CC[0];
    }
    

    Best regards
    Torbjørn

  • Hello Ovrebekk,

    thank you for your answer and for confirming this.

    Also thank you for sharing this sample.

    I will read more about the TIMER and GPIOTE peripherals since I have never used them before and I will try to implement this.

  • Hi 

    If you need separate events for a rising and falling flank it makes sense that you would need to use 2 channels, the GPIOTE hardware does not support generating different events on a single channel. 

    Is this what you were trying to do?

    Best regards
    Torbjørn

  • Hello,

    no I am expanding this for 2 encoders, so I am using a IN_EVENT configured for 2 different PINs upon a LOTOHI event. 

  • Hi 

    If you have 2 different pins you would also need 2 different GPIOTE channels, but possibly I am misunderstanding the issue. 

    If you are still unsure why you have to configure the GPIOTE driver the way you do, maybe you can share your init code and I can have a look at it?

    Best regards
    Torbjørn

  • Hello,

    you are right. I had a misunderstanding on this.

    I have implemented this and it seems to be working fine.

    I have not added a direction check yet but I will explore adding this too.

    I am now trying to see what is the best way to capture the counter value (considering that if I clear every capture, I might miss some counts. On the other hand if I do not clear, I will need to find a way to handle the possible overflow of the counter register).

    You may consider this answered.

  • Hi

    Assuming you set the timer to 32-bit mode it should take a long time between each overflow. A simple way to detect overflow is to store the previous counter value in a static variable, and compare the previous counter to the current one. If the previous counter is larger than the current then you know that an overflow occurred, and you can calculate the difference between the two.

    Best regards
    Torbjørn

Reply
  • Hi

    Assuming you set the timer to 32-bit mode it should take a long time between each overflow. A simple way to detect overflow is to store the previous counter value in a static variable, and compare the previous counter to the current one. If the previous counter is larger than the current then you know that an overflow occurred, and you can calculate the difference between the two.

    Best regards
    Torbjørn

Children
No Data
Related