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) ?

  • Hi 

    There is no direct way to find out if an interrupt is delayed unfortunately. You might be able to do some benchmarking and testing to verify whether or not there are any interrupts in the system that could delay you long enough to miss the value of PINB. 

    Alternatively you could implement a more complex system where you connect both the PINA and PINB inputs to capture on different CC registers of a timer, and use this to analyze if PINB goes low or high following a rising flank on PINA. 

    pekon94 said:
    would it be a solution to increase the APP core frequency (to 128Mhz) ?

    Not a solution necessarily, but increasing the app core frequency should reduce the runtime of the various interrupts, and also reduce the interrupt latency. 

    Best regards
    Torbjørn

  • Hello,

    I finally got a piece of code working (tested with another encoder) and I intend to use it today with the encoder mentioned above.

    However I have the following question:

    As you can see, in order to initialize the PIN in GPIOTE IN_EVENT mode I had to declare an event handler function. I have left it empty, but isn't this also causing a CPU interrupt ?

    static void event_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
    }
    
    static void cust_qdec_init(void)
    {
    	IRQ_CONNECT(DT_IRQN(DT_NODELABEL(gpiote)),
    		    DT_IRQ(DT_NODELABEL(gpiote), priority),
    		    nrfx_isr, nrfx_gpiote_irq_handler, 0);
    
    	printk("Helloooooooooo\n");
    	timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32 ;
    	timer_cfg.mode = NRF_TIMER_MODE_COUNTER ;
    	pina_config.pull = NRF_GPIO_PIN_NOPULL;
    
    	printk("1111111111111111\n");
    	err_code = nrfx_timer_init(&qdec_time_counter, &timer_cfg, NULL);
    	if (err_code != NRFX_SUCCESS) {
    		printk("Error in timer initialization !!!! \n");
    		return;
    	}
    	nrfx_timer_enable(&qdec_time_counter);
    	printk("222222222222222\n");
    	if (!nrfx_gpiote_is_init()) {
    		err_code = nrfx_gpiote_init(0);
    		if (err_code != NRFX_SUCCESS) {
    			printk("Error in NRFX GPIOTE initialization !!!! \n");
    			return;
    		}
    	}
    	err_code = nrfx_gpiote_in_init(PINA, &pina_config, event_handler);
      if (err_code != NRFX_SUCCESS) {
        printk("Error in GPIOTE IN_EVENT configuration Initialization");
        return;
      }
    
    	err_code = nrfx_dppi_channel_alloc(&cust_qdec_ppi_chann);
      if (err_code != NRFX_SUCCESS) {
        printk ("Error in allocating DPPI channel \n");
        return;
      }
    	nrfx_gppi_channel_endpoints_setup(cust_qdec_ppi_chann,
                                         nrfx_gpiote_in_event_addr_get(PINA),
                                         nrfx_timer_task_address_get(&qdec_time_counter, NRF_TIMER_TASK_COUNT));
      if (err_code != NRFX_SUCCESS) {
        printk("Eroor in configuring GPPI endpoints \n");
        return;
      }
    	err_code = nrfx_dppi_channel_enable(cust_qdec_ppi_chann);
      if (err_code != NRFX_SUCCESS) {
        printk("Error enabling the DPPI Channel for qdec \n");
        return;
      }
    	nrfx_gpiote_in_event_enable(PINA,true);
    }

    Is there another way to initialize the PIN in GPIOTE IN_EVENT mode ? I tried leaving the event_handler as NULL but this caused the CPU to crash and reboot upon any event.

    I was also wondering if the GPIOTE and TIMER initialization and enable sequence is the correct one.

    Am I correct to assume that I should initialize them before connecting them with dppi but enable them after connecting them with dppi ?

  • Hi

    pekon94 said:
    Is there another way to initialize the PIN in GPIOTE IN_EVENT mode ? I tried leaving the event_handler as NULL but this caused the CPU to crash and reboot upon any event.

    The nrfx_gpiote_in_init(..) function has been deprecated, and you should use the nrfx_gpiote_input_configure(..) function instead. 

    With this function it should be possible to set the p_handler_config argument to NULL, in order to disable the interrupt. 

    pekon94 said:

    I was also wondering if the GPIOTE and TIMER initialization and enable sequence is the correct one.

    Am I correct to assume that I should initialize them before connecting them with dppi but enable them after connecting them with dppi ?

    The natural order is to configure the peripherals first, then make the various DPPI connections, and starting peripherals that need to be started at the very end. 

    Best regards
    Torbjørn

  • Hello,

    thank you for this.

    I used the nrfx_gpiote_input_configure() function and the nrfx_gpiote_trigger_enable() function. In the second one I had the int_enable parameter set to false.

    I noticed though that I had to allocate 2x GPIOTE channels (could not let the nrfx_gpiote_trigger_config_t p_in_channel parameter set to NULL) and add CONFIG_NRFX_GPIOTE_NUM_OF_EVT_HANDLERS=2 in the prj.conf file to make it work.

  • 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

Related