Nrfx_qdec double trigger for one turn

I am having an issue with the nrfx_qdec driver in zephyr, I am trying to simply detect when the turn happens and the direction of the turn is.
Most of the time this is triggered fine, but sometimes for one turn of the encoder I get 2 triggers, I am trying to figure out how I should best debounce this, whether in the qdec configuration 
or something on the my side in data_ready_handler for example.
I am using NRF connect SDK v2.9.1 in VScode
MCU is nrf52810_xxAA_REV2
qdec dts config 
&qdec {
    status = "okay";
    pinctrl-0 = <&qdec_default>;
    pinctrl-1 = <&qdec_sleep>;
    pinctrl-names = "default", "sleep";

    steps = <20>;
    led-pre = <0>;
};

Here is the code I am using to test out the qdec.

static
void data_ready_handler(const struct device *dev, const struct sensor_trigger *trig)
{
    struct sensor_value value;

    int err = sensor_sample_fetch_chan(qdec_dev, SENSOR_CHAN_ROTATION);

    if (err)
    {
        LOG_ERR("Cannot fetch sensor sample (err %d)", err);
        return;
    }

    err = sensor_channel_get(qdec_dev, SENSOR_CHAN_ROTATION, &value);
    if (err)
    {
        LOG_ERR("Cannot get sensor value");
        return;
    }

    int rc;

    LOG_INF("Position = %d degrees\n", value.val1);
    if (value.val1 > 0)
    {
        LOG_INF("+\n");
        device_state = RUNNING;
        uint8_t brightness = get_brightness();
        if (brightness != increment_brightness())
        {
            // only set if the brightness changed
            set_segment_led();
        }
    }
    else if (value.val1 < 0)
    {
        LOG_INF("-\n");

        device_state = RUNNING;
        uint8_t brightness = get_brightness();
        if (brightness != decrement_brightness())
        {
            // only set if the brightness changed
            set_segment_led();
        }
    }
}

void qdec_setup()
{
    struct sensor_value val;
    int rc;

#ifdef QUAD_ENC
    static const struct sensor_trigger qdec_trig = {
        .type = SENSOR_TRIG_DATA_READY,
        .chan = SENSOR_CHAN_ROTATION,
    };
    // setup the callback for when the qdec is triggered
    int err = sensor_trigger_set(qdec_dev, (struct sensor_trigger *)&qdec_trig,
                                 data_ready_handler);
    if (err)
    {
        LOG_ERR("Cannot setup trigger");
    }
    return;
#endif
}
Should I be reading the sensor differently or configuring it to debounce? It seems that the double turns happen within under 100ms of each other.
Parents
  • Did you solve the issue? 
    I am having the same issue. It is triggering two times for 1 step. My encoder is total 24 steps (I counted manually by hand after a full rotation). And I have a 100nF capacitor installed already for de bounce. And the behavior is consistent it always results in two triggers. 

    /*
     * Copyright 2023 Nordic Semiconductor ASA
     * SPDX-License-Identifier: Apache-2.0
     */
    
    / {
    	aliases {
    		qdec0 = &qdec1;
    	};
    };
    
    &qspi {
    	status = "disabled";
    };
    
    &uart0 {
    	status = "disabled";
    };
    
    &pinctrl {
    	qdec_pinctrl: qdec_pinctrl {
    		group1 {
    			psels = <NRF_PSEL(QDEC_A, 0, 18)>,   /* Ardiuno A0 */
    				<NRF_PSEL(QDEC_B, 0, 19)>;   /* Arduino A2 */
    		};
    	};
    };
    
    &qdec1 {
    	status = "okay";
    	pinctrl-0 = <&qdec_pinctrl>;
    	pinctrl-names = "default";
    	steps = < 24 >;
    	led-pre = < 500 >;
    };
    


    And my code

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/sensor.h>
    #include <zephyr/sys/printk.h>
    
    
    int main(void)
    {
    	struct sensor_value val;
    	int rc;
    	const struct device *const dev = DEVICE_DT_GET(DT_ALIAS(qdec0));
    
    	if (!device_is_ready(dev)) {
    		printk("Qdec device is not ready\n");
    		return 0;
    	}
    
    	printk("Quadrature decoder sensor test\n");
    
    
    	while (true) {
    		/* sleep first to gather position from first period */
    		k_msleep(1);
    
    		rc = sensor_sample_fetch(dev);
    		if (rc != 0) {
    			printk("Failed to fetch sample (%d)\n", rc);
    			return 0;
    		}
    
    		rc = sensor_channel_get(dev, SENSOR_CHAN_ROTATION, &val);
    		if (rc != 0) {
    			printk("Failed to get data (%d)\n", rc);
    			return 0;
    		}
    
    		if (val.val1 > 0) {
    			printk("Rotation: %d\n", val.val1);
    		} else if (val.val1 < 0) {
    			printk("Rotation: %d\n", val.val1);
    		}
    	}
    	return 0;
    }


    If I increase the sleep time to 1000ms then for 1 step I get single trigger and 30 degrees rotation which is not correct, it should be 15 degrees, meaning that it is still double triggering but just giving single output.

  • I guess the problem is my misunderstanding of the QDEC peripheral. The table in this shows how the increments or decrements are measured. And I now realized that this behavior of double triggering is actually consistent with the table 1. 

  • Hello mirhamza, 
    Finally got back to this issue and believe I have also nailed it down. In my case I noticed that you can get 1-4 events for a single turn of the encoder (18 degrees in my case for 20 steps), these events will report a position that is a multiple of 18 degrees, could be 18,36 etc.. In order to correctly react to a single increment of the encoder you should keep count of the reported rotation value and sum it, in my case a single increment would only happen after +/-72 degrees (18 * 4). You should then process an increment on the qdec when >=72 or <=-72, and subtract or add 72 as appropriate. This almost got me all the way there but when rotating the qdec very quickly I would end up in the situation that I did not increment 72 from a turn. To remedy this case, when the qdec is in steady state (nothing has happened for over 500ms) I would zero out my turn counter. After this the qdec works great!

    Basically the sensor has to have reported 4x the real degree amount turned for a turn to have really happened.

    Here is my code for that setup

    static int trans_count = 0;
    static uint64_t qdec_last_reading_time = 0;
    static void data_ready_handler(const struct device *dev, const struct sensor_trigger *trig)
    {
     
        uint64_t now = k_uptime_get();
        if (qdec_last_reading_time + 500 < now)
        {
            trans_count = 0; // we are at steady state zero out the transition counting to get rid
                             // of slop
        }
        struct sensor_value value;
     
        struct qdec_nrfx_config *config = dev->config;
     
        int err = sensor_sample_fetch_chan(dev, SENSOR_CHAN_ROTATION);
     
        if (err)
        {
            LOG_ERR("Cannot fetch sensor sample (err %d)", err);
            return;
        }
     
        err = sensor_channel_get(dev, SENSOR_CHAN_ROTATION, &value);
        if (err)
        {
            LOG_ERR("Cannot get sensor value");
            return;
        }
     
        LOG_INF("Position = %d degrees\n", value.val1);
        if (value.val1 > 0)
        {
            LOG_INF("+\n");
    
            trans_count += value.val1;
        }
        else if (value.val1 < 0)
        {
            LOG_INF("-\n");
     
            trans_count += value.val1;
        }
    	
        if (trans_count >= (4 * (360 / config->steps)))
        {
            trans_count -= 4 * (360 / config->steps);
    		
    		//react to increment
    
        }
     
        if (trans_count <= (-4 * (360 / config->steps)))
        {
            trans_count += 4 * (360 / config->steps);
     
    		// react to decrement
        }
        qdec_last_reading_time = now;
    }
     
    void qdec_setup()
    {
     
        // turn off qdec
     
        struct qdec_nrfx_config *config = dev->config;
        nrfx_qdec_disable(&config->qdec);
        config->config.sampleper = NRF_QDEC_SAMPLEPER_512US;
        config->config.reportper = NRF_QDEC_REPORTPER_40;
        // config->config.dbfen = true;
        nrfx_err_t nerr;
        nerr = nrfx_qdec_reconfigure(&config->qdec, &config->config);
        nrfx_qdec_enable(&config->qdec);
     
        if (nerr != NRFX_SUCCESS)
        {
            LOG_ERR("Cannot reconfigure qdec");
        }
     
    #ifdef QUAD_ENC
        static const struct sensor_trigger qdec_trig = {
            .type = SENSOR_TRIG_DATA_READY,
            .chan = SENSOR_CHAN_ROTATION,
        };
        // setup the callback for when the qdec is triggered
        int err = sensor_trigger_set(dev, (struct sensor_trigger *)&qdec_trig,
                                     data_ready_handler);
        if (err)
        {
            LOG_ERR("Cannot setup trigger");
        }
        return;
    #endif
    }

    I do have one last question in regards to lowering the sample rate under 512us (to 256 or 128), I found that doing this would make the qdec not report events at all, not sure if anyone has thoughts about this.

  • Thanks, I have done a similar approach to solve this problem.

Reply Children
No Data
Related