Encoder works poorly due to undefined CORE_INT40_PIN and CORE_INT41_PIN
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.
It is also necessary to say
#define CORE_NUM_INTERRUPT 42
for obvious reasons
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
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();
@MitchBradley Great work, thank you for sharing!
Thanks for the drivers. M5dial is not developed anymore ., and using BtnA functions made my m5dial to reboot forever. I switched to M5Unified, button is now working good, but there's currently no rotary encoder manager 😅 So thangs again to had provided a solution 👌