EC11 Rotary Encoder with Sensor API

I have an EC11 rotary encoder wired to my nRF52840 and I am trying to get readings from it using the sensor API. However, I'm having issues with (seemingly) garbage data. When I rotate one "click" I get a variety of readings. Sometimes no readings whatsoever, sometimes multiple readings. I referenced the nrf_desktop wheel implementation for my configuration.

I should note that I am relying on software pullups for my configuration, hence the specific gpio definition for the encoder pins.

It is important to me that I do not poll the encoder in order to save power. Am I missing something obvious?

Here's my overlay file:

/ {
    aliases {
        sw0 = &button0;
        sw1 = &button1;
        sw2 = &button2;
        qdec0 = &qdec0;
        qenca = &encoder_a;
        qencb = &encoder_b;
    };
    
    encoder_a: encoder_a_pin {
        gpios = <&gpio1 13 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
        label = "Rotary interrupt A";
    };
    encoder_b: encoder_b_pin {
        gpios = <&gpio1 15 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
        label = "Rotary interrupt B";
    };
};


&pinctrl {
    qdec_pinctrl: qdec_pinctrl {
        group1 {
            psels = <NRF_PSEL(QDEC_A, 1, 13)>,
                    <NRF_PSEL(QDEC_B, 1, 15)>;
        };
    };
};

&qdec0 {
    status = "okay";
    pinctrl-0 = <&qdec_pinctrl>;
    pinctrl-names = "default";
    steps = <80>;
    led-pre = <0>;
};

My interface code:

const struct device *const encoder_device = DEVICE_DT_GET(DT_NODELABEL(qdec0));

static void sample_encoder()
{
    struct sensor_value val;
    int rc;
    rc = sensor_sample_fetch(encoder_device);
    if (rc != 0)
    {
        LOG_INF("Failed to fetch sample (%d)\n", rc);
        return 0;
    }

    rc = sensor_channel_get(encoder_device, SENSOR_CHAN_ROTATION, &val);
    if (rc != 0)
    {
        LOG_INF("Failed to get data (%d)\n", rc);
        return 0;
    }

    if (val.val1 != position)
    {
        position = val.val1;
        LOG_INF("Position = %d degrees\n", position);
    }
}

void init_encoder(){
    if (!device_is_ready(encoder_device))
    {
        LOG_ERR("Cannot get QDEC device");
        return -ENXIO;
    }

    static const struct sensor_trigger qdec_trig = {
        .type = SENSOR_TRIG_DATA_READY,
        .chan = SENSOR_CHAN_ROTATION,
    };

    err = sensor_trigger_set(encoder_device, (struct sensor_trigger *)&qdec_trig,
                             sample_encoder);
 }

Here is an example log of one "click" clockwise:

[00:11:40.740,112] <inf> main_logging: Position = 4 degrees

[00:11:40.822,021] <inf> main_logging: Position = 13 degrees

And here is the logs from three "clicks" counterclockwise:

[00:12:14.900,543] <inf> main_logging: Position = -9 degrees

[00:12:17.931,579] <inf> main_logging: Position = -18 degrees

[00:12:19.979,553] <inf> main_logging: Position = -4 degrees

[00:12:20.061,492] <inf> main_logging: Position = -13 degrees

Parents Reply Children
  • Thanks for your quick response! I am on nRF Connect version 2.6.1. I can try the latest release and see if there is any difference.

    Unfortunately I do not have access to an oscilloscope or logic meter at this time. I will try cross-posting in the Zephyr Discord to see if anyone has any ideas and report back.

  • Hi  have you had any luck identifying the issue with your encoder? I still have not been able to find an answer.

  • Hello, 

    No, I'm afraid I've had no luck so far. 

    Did you have any luck in the Zephyr channel?

    -Øyvind

  • I'm afraid that there is not much help from my side with Zephyr drivers, as we do not have much rotary encoders in our products. 

    Looking at zephyr/dts/bindings/input/gpio-qdec.yaml please make sure that you have configured the rotary encoder with the following

      qdec {
              compatible = "gpio-qdec";
              gpios = <&gpio0 14 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>,
                      <&gpio0 13 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>;
              steps-per-period = <4>;
              zephyr,axis = <INPUT_REL_WHEEL>;
              sample-time-us = <2000>;
              idle-timeout-ms = <200>;
      };

    This is used in zephyr/samples/subsys/display/lvgl/boards/native_posix.overlay

    Does this help with your rotary encoder output? Next step would be to debug using e.g. logic analyzer to ensure the signals are correct

    Looking at the zephyr/samples/subsys/display/lvgl/src/main.c the following snippet should provide some help, and perhaps help with debugging i.e. setting break points

    #ifdef CONFIG_LV_Z_ENCODER_INPUT
    	lv_obj_t *arc;
    	lv_group_t *arc_group;
    
    	arc = lv_arc_create(lv_scr_act());
    	lv_obj_align(arc, LV_ALIGN_CENTER, 0, -15);
    	lv_obj_set_size(arc, 150, 150);
    
    	arc_group = lv_group_create();
    	lv_group_add_obj(arc_group, arc);
    	lv_indev_set_group(lvgl_input_get_indev(lvgl_encoder), arc_group);
    #endif /* CONFIG_LV_Z_ENCODER_INPUT */

    zephyr/modules/lvgl/input/lvgl_encoder_input.c

    And I assume you have been looking at the QDEC sameple (zephyr/samples/sensor/qdec/src/main.c)

Related