Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encoder works poorly due to undefined CORE_INT40_PIN and CORE_INT41_PIN #8

Open
MitchBradley opened this issue Dec 16, 2023 · 4 comments

Comments

@MitchBradley
Copy link

MitchBradley commented Dec 16, 2023

I am a developer on the same project cited in issue #5. We have discovered an issue whereby, when you spin the dial rapidly in one direction, the sequence of encoder delta values often changes sign. For example, you might see the sequence -1,-1,-1,-2,-1,2,-1 . The (+)2 is spurious; the dial is only spinning in one direction. Furthermore, the magnitude of the delta never exceeds 2.

I have tracked it down to the fact that attach_interrupt in Encoder.h is failing. The reason is that M5Dial.h has

#define DIAL_ENCODER_PIN_A 41
#define DIAL_ENCODER_PIN_B 40

but in Encoder.h, the list of interrupt pin defines ends at 39, i.e.

...
#define CORE_INT36_PIN 36
#define CORE_INT39_PIN 39

I suspect that this is because, on plain ESP32, the highest numbered GPIO is 39, whereas ESP32-S3 has GPIO numbers up to 48, and the code was not updated to reflect that.

When the interrupt does not attach, the update() method runs only when Encoder.read() is called, and that is not necessarily frequently enough to catch all encoder edges during fast rotation.

I manually added the following lines to Encoder.h to fix the problem:

#define CORE_INT40_PIN 40
#define CORE_INT41_PIN 41

A complete fix might include other GPIO numbers as well, although 40 and 41 suffice for the M5Dial hardware that uses those pins.

@MitchBradley
Copy link
Author

It is also necessary to say

#define CORE_NUM_INTERRUPT 42

for obvious reasons

@aerogarage
Copy link

Thanks. I applied this fix but it was not perfect quite yet. I have found that this problem is related to the display. Using a M5Canvas function with M5GFX and creating a specific draw function solved the problem. I took this from Volos project examples on the M5Dial, which I recommend. https://github.com/VolosR/M5Dial/tree/main/M5Dial

@MitchBradley
Copy link
Author

We also continued to have encoder problems with the fix, albeit less frequently. We solved it by creating a custom encoder driver that uses the ESP32 PCNT hardware and thus does not need interrupts. With the PCNT driver, the encoder works perfectly. Here is our Encoder.cpp file

#include "sdkconfig.h"
#include "driver/pcnt.h"
#include "driver/gpio.h"
#include "Encoder.h"

/* clang-format: off */
void init_encoder() {
    pcnt_config_t enc_config = {
        .pulse_gpio_num = GPIO_NUM_40,  //Rotary Encoder Chan A
        .ctrl_gpio_num  = GPIO_NUM_41,  //Rotary Encoder Chan B

        .lctrl_mode = PCNT_MODE_KEEP,     // Rising A on HIGH B = CW Step
        .hctrl_mode = PCNT_MODE_REVERSE,  // Rising A on LOW B = CCW Step
        .pos_mode   = PCNT_COUNT_INC,     //Count Only On Rising-Edges
        .neg_mode   = PCNT_COUNT_DEC,     // Discard Falling-Edge

        .counter_h_lim = INT16_MAX,
        .counter_l_lim = INT16_MIN,

        .unit    = PCNT_UNIT_0,
        .channel = PCNT_CHANNEL_0,
    };
    pcnt_unit_config(&enc_config);

    enc_config.pulse_gpio_num = GPIO_NUM_41;
    enc_config.ctrl_gpio_num  = GPIO_NUM_40;
    enc_config.channel        = PCNT_CHANNEL_1;
    enc_config.pos_mode       = PCNT_COUNT_DEC;  //Count Only On Falling-Edges
    enc_config.neg_mode       = PCNT_COUNT_INC;  // Discard Rising-Edge
    pcnt_unit_config(&enc_config);

    pcnt_set_filter_value(PCNT_UNIT_0, 250);  // Filter Runt Pulses

    pcnt_filter_enable(PCNT_UNIT_0);

    gpio_pullup_en(GPIO_NUM_40);
    gpio_pullup_en(GPIO_NUM_41);

    pcnt_counter_pause(PCNT_UNIT_0);  // Initial PCNT init
    pcnt_counter_clear(PCNT_UNIT_0);
    pcnt_counter_resume(PCNT_UNIT_0);
}

int16_t get_encoder() {
    int16_t count;
    pcnt_get_counter_value(PCNT_UNIT_0, &count);
    return count;
}

and the Encoder.h file

#pragma once

#include <Arduino.h>

void    init_encoder();
int16_t get_encoder();

@manuelzi
Copy link

@MitchBradley Great work, thank you for sharing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants