-
Notifications
You must be signed in to change notification settings - Fork 245
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
add int pin and macro check for Raspberry Pi Pico and W #85
base: master
Are you sure you want to change the base?
Conversation
For a 3D printing filament dryer project, I had to use the encoder library with the Raspberry Pi Pico, so I added the IRQ pins. |
Hi, ive tested this with RP2040 and using the "examples/Basic/Basic.ino" sketch. its working but not properly, it is skipping reads, also sometimes runs away meaning starts increasing for ever without moving the knob. Sometimes it counts in the incorrect way. im using a mechanical encoder. have you tested this? |
Hi @ale-novo, I didn't test it with Basic.ino, I used the EncoderButton library which is kind of a wrapper for this library. |
Hi, ive tested this with RP2040 and using the "examples/Basic/Basic.ino" sketch. all encoders work perfectly with arduino nano pins 2,3 (only interrupt pins) |
I use the KY-040 but connected to 3.3V of the berry pico; my version of the rotary encoder has the 10K ohm pull-ups resistor included, attached I send you a video of my application that controls a GUI. dry-box_encoder_gui.mp4 |
Has anyone else tested? I'm reluctant to merge with "works very unreliable.with a KY-040 encoder". Need more feedback from people actually using RP2040. |
Hi @PaulStoffregen, honestly I don’t check the basic.ino with the Pico Berry, but tomorrow I will test it and upload the results and pictures of the oscilloscope |
@PaulStoffregen, @ale-novo, I tested the encoder connected to both 5V and 3.3V on the Raspberry Pi Pico and the practical result is the same. I also added two capacitors to the data and a clock pin to test if there is an improvement with glitch, but the result is the same. The main difference between basic.ino and my code is that I need to modify the class declaration. This is because the constructor initiates the interrupt of the pins, and in the Arduino Pico porting, this is not allowed because the init() function is executed after the configuration and clears the initialization pin. My solution was to declare the class as a pointer and use new in the setup() function. This way, the memory is allocated dynamically, and the constructor is called after the init(), which solves the problem. /* Encoder Library - Basic Example
#include <Encoder.h> // Change these two numbers to the pins connected to your encoder. void setup() { myEnc = new Encoder(6, 7); Serial.println("Basic Encoder Test:"); long oldPosition = -999; void loop() { On stack overflow and on the arduino forum he explains this behavior. This is a picture of the signal with 3.3V and a capacitor of 0.01uF. This is a picture of the signal with 3.3V. This is a picture of the signal with 5V. To compare the reaction of the rotary encoder I built the device with an Arduino Nano and the unmodified basic.ino, the results as seen in the video are very similar to those of the berry pico. encoder_nano.mp4encoder_pico.mp4 |
@jjsch-dev @PaulStoffregen with Arduino nano it reads perfectly. Also have tested with Teensy LC and works flawlessly With RP PICO however its not the case When i say it works unreliably i mean it misses counts or counts backwards sometimes. i suspect the problem is encoder signal noise. |
Ive realized i had Arduino Mbed OS RP2040 boards 3.4.1 in tools->board->board manager now ive updated to v4.0.2 naked encoder: still unreliable and runs away (please see at the end of video). same encoder works perfect with Arduino nano vid-20230321194520_06B2Sdm7.mp4KY-040 : works much better with 3.3v and 5v
or
but is minimal, but we can call it good. |
Also please consider this alternative implementation ive found online, as its slightly different than the one proposed in this PR im not sure if its better or worse:
in this PR:
|
@ale-novo the I am using the arduino-pico port from Earle F. Philhower. |
ive tried Earle F. Philhower and i cant get a encoder response on the serial monitor for either the naked encoder or KY-040 |
@ale-novo with the Earle F. Philhower porting, you need to change the code as I suggest in a previous reply.
/*
* Encoder Library - Basic Example
* http://www.pjrc.com/teensy/td_libs_Encoder.html
* This example code is in the public domain.
*/
#include <Encoder.h>
// Change these two numbers to the pins connected to your encoder.
// Best Performance: both pins have interrupt capability
// Good Performance: only the first pin has interrupt capability
// Low Performance: neither pin has interrupt capability
Encoder* myEnc; //(6, 7);
// avoid using pins with LEDs attached
void setup() {
Serial.begin(9600);
delay(5000);
myEnc = new Encoder(6, 7);
Serial.println("Basic Encoder Test:");
}
long oldPosition = -999;
void loop() {
long newPosition = myEnc->read();
if (newPosition != oldPosition) {
oldPosition = newPosition;
Serial.println(newPosition);
}
} |
@jjsch-dev i had the same results with Earle F. Philhower and Arduino MBED os RP2040
|
@ale-novo , Thanks for the comments, I only tried with the ky-40 encoder, can you send me a link of the 600 ppr optical encoder, I'll see if I can get one in Argentina to test. |
The optical encoder is I think the problem why it does not work is its because its 5v and RP PICO is 3.3v. i have ordered a logic level converter and i will try when it arrives. |
@ale-novo thanks for the link, it is advertised on this site that the encoder output is open collector type, so you don't need a level converter, with the right pull-up you could do the level adapter. If you say it works with the nano but not with the pico, it's possible that the internal pull-up is the problem (for the nano the value is about 20K ohms, and for the berry pico about 50K ohms). Could you try an external pull-up to 3.3V of 4k7 ohm for the clock and data pins? |
ive tested an optical encoder 600 ppr (5v signal) with a logic level converter to 3.3v and works fine with RP2040 no missed steps. |
@ale-novo I'm glad you were able to solve your problem. |
please merge this pr |
I can confirm that this is working with the Earle F. Philhower board definition and the basic example sketch. @jjsch-dev how would I need to modify a EncoderButton library project to also initialise the constructor after the init function and make it work? |
@Deadeye5589 This is the modified EncoderButton example. /**
* A basic example of using the EncoderButton library.
*/
#include <EncoderButton.h>
/**
* Instatiate an EncoderButton.
* For Arduino Uno, the hardware interrupts are pins 2 & 3
* For Teensy, you can use any digital pin.
* Probably better to pick a more meaningful name than 'eb1'...
* Encoder+button:
* EncoderButton(byte encoderPin1, byte encoderPin2, byte switchPin);
* Encoder only:
* EncoderButton(byte encoderPin1, byte encoderPin2);
* Button only:
* EncoderButton(byte switchPin);
*/
EncoderButton *eb1; //(2, 3, 4);
/**
* A function to handle the 'clicked' event
* Can be called anything but requires EncoderButton&
* as its only parameter.
* I tend to prefix with 'on' and suffix with the
* event type.
*/
void onEb1Clicked(EncoderButton& eb) {
Serial.print("eb1 clickCount: ");
Serial.println(eb.clickCount());
}
/**
* A function to handle the 'encoder' event
*/
void onEb1Encoder(EncoderButton& eb) {
Serial.print("eb1 incremented by: ");
Serial.println(eb.increment());
Serial.print("eb1 position is: ");
Serial.println(eb.position());
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
delay(500);
Serial.println("EncoderButton Basic Example");
eb1 = new EncoderButton(2, 3, 4);
//Link the event(s) to your function
eb1->setClickHandler(onEb1Clicked);
eb1->setEncoderHandler(onEb1Encoder);
}
void loop() {
// put your main code here, to run repeatedly:
// You must call update() for every defined EncoderButton.
// This will update the state of the encoder & button and
// fire the appropriat events.
eb1->update();
} |
Thank you for providing the example code. Works fine as well. No jumps or missing counts, using internal pull up only. |
@Deadeye5589 Thanks for the comment, I'm glad I helped you. |
I'm having what sounds like similar problems using this on a Seeeduino XIAO RP2040 with the earlephilhower core, wondered if there is anything I'm missing? I've got my encoder wired to D2 and D3 of the RP2040, which seem to be GPIO pins 28 and 29.* I've tried the changes in this patch and that I found mentioned in this thread. I've also modified interrupt_pins.h to check for defined(ARDUINO_ARCH_RP2040), and also defined CORE_INT28_PIN and CORE_INT29_PIN. I am also using the 'gpio_get' version of DIRECT_PIN_READ. If I instantiate the Encoder statically then the encoder 'works' but is very unreliable as described here as interrupts don't seem to be used for the reasons described. If I instantiate the Encoder using new in setup(), then the same thing happens no matter what combination of changes I try: no reaction at all to the encoder being turned. However if I configure the CORE_INT28_PIN and CORE_INT29_PIN, use the gpio_get version of DIRECT_PIN_READ, AND if I set a breakpoint in Encoder::update in my debugger, then the first time the knob is turned, the interrupt triggers and the breakpoint is reached twice - once leading to a position +=2, and then again, resulting in no change. Subsequent turns of the encoder give no reaction. Running this same version of the code but without the debugger attached doesn't even trigger the breakpoint once, so I'm not sure if that could be a red herring?
Is there anything else I might be missing here, or something else I could try to see what the problem is? Thanks |
@doctea What kind of encoder are you using, the output is open collector?, the XIAO-RP2040 needs 3.3V at the input.
|
Am using the 3.3V from the XIAO, with some capacitors to ground on each encoder pin to smooth the signal a bit (without these it was behaving completely crazy). I'm not sure how to check what kind of encoder it is. However, this project has been working with the encoder mostly OK for months, if I don't use the Encoder interrupts or if I instantiate, but the encoder clicks don't correspond well to the events received - its usable, but only just. It is only if I use interrupts that I get the behaviour described where the encoder basically doesn't work at all, except for the one trigger of interrupt. I was hoping there might be something about the XIAO pin/interrupt configuration that differs from the Pico, that I might be missing and need to reflect in the Encoder headers in order to get this to work? I've also been using a variation of the same code+circuit on a Teensy 4.1, where the encoder events correspond well to the clicks of the encoder, so I believe this to be some problem with the RP2040 specifically. If worst comes to worst I can attempt to try the same thing using a Pico and see if the same problem occurs, but my PCBs use the XIAO so this is a little bit of a pain - building the circuit on a breadboard was too noisy to tell whether the encoder is working properly :) |
@doctea Sorry for my English, by type of encoder I mean the model of the encoder, please send me a picture or link of the product. I didn't use the XIAO, but it uses the same RP2040 CPU. A capacitor is a good help to reject low frequency noise, use with care because it limits the rotation speed of the encoder. If the encoder is mechanical, it's most likely an open collector device, and you need the pull-up resistor and for 3.3V use in the range from 10K to 4K7. |
@jjsch-dev ah sorry, the encoder is one of these: "EC11 360 Degree Rotary Encoder, Code Switch", if that helps? Just to be sure I understand, the pull-up resistor should go from 3.3V, to each of the signal pins? I shall try this, and see if it either improves reliability in non-interrupt mode, or allows for interrupts to be triggered correctly. Thanks for your help! |
@doctea thanks for the link, and this device, although the RP2040 has internal pulls, it works better with external pull-ups on the clock and data pins, I'll send you a picture, the range for R2 and R3 can be from 4k7 to 10K for 3.3V. Capacitors C3 and C9 are optional, but they definitely help eliminate false triggering. |
Thank you for this! I added 10K pull-ups to the encoder pins as suggested, and now it works perfectly :D. |
@doctea I'm glad it worked. 👍 |
got it working with this code AND enabled pullup: (standard chinese encoders)
|
@madias123 thanks for the feedback. I read the tutorial and it's a good use of the PIO to decode the quadrature encoder and reduce interrupts in the program. And regarding division by 4, what model of encoder are you using? |
I bought years ago 100 pieces of such standard aliexpress rotary encoders, like this: |
FWIW, I've always been dividing the result of read() by 4 in order to get accurate results too - both on RP2040 with the modifications described above, and on my original Teensy project too. I always assumed there was a setting in the library that I'd missed, but it was a simple enough workaround to do |
@madias123 thanks for the link, the EC11 is a standard rotary encoder, they usually have 20 steps per turn. Are you using external pull-up resistors (typically 10Kohm)? 50 Kohm from the internal pull-ups isn't much for this encoder. |
Thanks for the information about (steps/4) I will take it into account for future developments, but in the projects that I have used this library the precision of the steps does not matter, because what I need is to know if it increases or decreases when the user operates the encoder. |
I was able to get this working using the suggestions above. Using the TT Electronics EN05 Series encoder - EN05HS1212NHH
direct_pin_read.h
|
Hi @JKTUNING I'm glad it works for you, I didn't know about the EN05HS1212NHH, it's a very small device. |
Can also confirm that this works, tested with earlephilhower core. Merge would be nice :) |
@kallaspriit, thanks for the feedback. |
Maybe also adding Other than that, it works just fine for me with a pointer to the encoder. |
Calling That's why I would propose adding a So the encoder class looks something like this: class Encoder
{
public:
Encoder(uint8_t pin1, uint8_t pin2, bool usePullups = false)
: pin1(pin1), pin2(pin2), usePullups(usePullups)
{
}
void begin()
{
if (usePullups)
{
pinMode(pin1, INPUT_PULLUP);
pinMode(pin2, INPUT_PULLUP);
}
else
{
pinMode(pin1, INPUT);
digitalWrite(pin1, HIGH);
pinMode(pin2, INPUT);
digitalWrite(pin2, HIGH);
}
encoder.pin1_register = PIN_TO_BASEREG(pin1);
encoder.pin1_bitmask = PIN_TO_BITMASK(pin1);
encoder.pin2_register = PIN_TO_BASEREG(pin2);
encoder.pin2_bitmask = PIN_TO_BITMASK(pin2);
encoder.position = 0;
// allow time for a passive R-C filter to charge
// through the pullup resistors, before reading
// the initial state
delayMicroseconds(2000);
uint8_t s = 0;
if (DIRECT_PIN_READ(encoder.pin1_register, encoder.pin1_bitmask))
s |= 1;
if (DIRECT_PIN_READ(encoder.pin2_register, encoder.pin2_bitmask))
s |= 2;
encoder.state = s;
#ifdef ENCODER_USE_INTERRUPTS
interrupts_in_use = attach_interrupt(pin1, &encoder);
interrupts_in_use += attach_interrupt(pin2, &encoder);
#endif
// update_finishup(); // to force linker to include the code (does not work)
} I have tested this in my modified library on RP2040 earlephilhower core and it works fine, no need for dynamic allocation or adding any defines. This does require calling Maybe it would be possible to retain the original functionality in the constructor and add an optional |
No description provided.