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

Raspberry pi pico external interrupts not working #253

Closed
Chick92 opened this issue Jun 3, 2021 · 10 comments · May be fixed by #256
Closed

Raspberry pi pico external interrupts not working #253

Chick92 opened this issue Jun 3, 2021 · 10 comments · May be fixed by #256

Comments

@Chick92
Copy link

Chick92 commented Jun 3, 2021

Can't get any encoder library to work using interrupts. Ones that are able to, work fine when polling the state of the pins, but this won't be suitable for my task.

Tried with a simple interrupt sketch, also no response at all.

const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

void setup() {
pinMode(ledPin, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}

void loop() {
digitalWrite(ledPin, state);
}

void blink() {
state = !state;
}

@fsievers
Copy link

fsievers commented Jun 4, 2021

You have to change the event type of the interrupt from CHANGE to HIGH or LOW. The 'change' event does not work. I personally attached both events to the same handler for both pins of the rotary encoder. This should be the same as a 'change' event.

attachInterrupt(digitalPinToInterrupt(pin_a), rotaryIsrHandler, HIGH);
attachInterrupt(digitalPinToInterrupt(pin_a), rotaryIsrHandler, LOW);
attachInterrupt(digitalPinToInterrupt(pin_b), rotaryIsrHandler, HIGH);
attachInterrupt(digitalPinToInterrupt(pin_b), rotaryIsrHandler, LOW);

@Chick92
Copy link
Author

Chick92 commented Jun 5, 2021

No luck unfortunately, same behavior. What library are you using?

Polling works fine, but interrupts don't. Tested 2 different encoders (different design). My encoder of choice works fine with an 8-bit pro mini.

Here's a minimum example - https://www.arduino.cc/reference/en/libraries/rotaryencoder/

#include <RotaryEncoder.h>

#define PIN_IN1 2
#define PIN_IN2 3

RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);

void checkPosition(){
encoder.tick(); // just call tick() to check the state.
}

void setup(){
Serial.begin(115200);
while (! Serial);
Serial.println("InterruptRotator example for the RotaryEncoder library.");
pinMode(PIN_IN1, INPUT_PULLUP);
pinMode(PIN_IN2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PIN_IN1), checkPosition, HIGH);
attachInterrupt(digitalPinToInterrupt(PIN_IN1), checkPosition, LOW);
attachInterrupt(digitalPinToInterrupt(PIN_IN2), checkPosition, HIGH);
attachInterrupt(digitalPinToInterrupt(PIN_IN2), checkPosition, LOW);
} // setup()

// Read the current position of the encoder and print out when changed.
void loop()
{
static int pos = 0;
encoder.tick();

int newPos = encoder.getPosition();
if (pos != newPos) {
Serial.print("pos:");
Serial.print(newPos);
Serial.print(" dir:");
Serial.println((int)(encoder.getDirection()));
pos = newPos;
} // if
delay(100);
} // loop ()

@fsievers
Copy link

fsievers commented Jun 5, 2021

I use QDEC for the rotary and AceButton for the button of the rotary encoder.

@Chick92
Copy link
Author

Chick92 commented Jun 5, 2021

I'm at a complete loss here. Tried the library you suggested, with both HIGH and LOW 'links' to the ISR for both pins and get the same result.
Uploaded the code onto a Pro mini, and it works just fine.
I must be missing something.

@Chick92
Copy link
Author

Chick92 commented Jun 5, 2021

Tested the pico with micropython to make sure there's not a problem with my hardware. Was able to get working interrupts using the example provided by Raspberry pi co:

`from machine import Pin

p2 = Pin(2, Pin.IN, Pin.PULL_UP)
p2.irq(lambda pin: print("IRQ with flags:", pin.irq().flags()), Pin.IRQ_FALLING)`

It's a problem with the arduino side of things.

@Chick92
Copy link
Author

Chick92 commented Jun 5, 2021

Tried a simple interrupt sketch, serial port sent a single string before becoming completely unresponsive. Board now flashes "SOS" - I'm assuming thats a failure mode, but can't find anything on it.

I'm using the same encoder, and it has ~3000 pulses per turn. I'm thinking this may be a problem with an interrupt interrupting the ISR itself? It's been a while since i studied computer systems architecture, but i believe that there should be things in place to prevent this as it's typically bad?

In order to bring reflash another sketch to the board, i need to hold bootsel and plug it back in, like you would do the first time uploading a sketch.

simple sketch for reference -

`int pin = 3; //define interrupt pin to 2
volatile int state = LOW; // To make sure variables shared between an ISR
//the main program are updated correctly,declare them as volatile.

void setup() {
pinMode(13, OUTPUT); //set pin 13 as output
attachInterrupt(digitalPinToInterrupt(pin), blink, LOW);
//interrupt at pin 2 blink ISR when pin to change the value
Serial.begin(115200);
}
void loop() {
digitalWrite(13, state); //pin 13 equal the state value
}

void blink() {
//ISR function
state = !state; //toggle the state when the interrupt occurs
Serial.println("ISR);
}`

If i remove the print bit of the ISR, it doesn't do anything when a state change is triggered. I thought maybe it's happening too fast for me to see, so i added a delay to the ISR (I know, ISR's need to run as fast as possible, but trying to figure this out). Board becomes unresponsive and blinks SOS again.

I will try the pico port that was released by someone forgot his name) before the arduino guys released the official one.

@fsievers
Copy link

fsievers commented Jun 7, 2021

Using serial output in an interrupt handler doesn't work for me. I think that's true for all kinds of interrupts on the Pico. I will post a complete example later.

@fsievers
Copy link

fsievers commented Jun 7, 2021

This is the code I use to get the rotary encoder interrupts working for me. This program can be used for Pico and Arduino Nano (by changing the ARDUINO_AVR_NANO check, you could also get it to work for other platforms.

#include <Arduino.h>

#include <qdec.h>
#include <AceButton.h>

#if defined(ARDUINO_ARCH_RP2040)
const int ROTARY_PIN_A = 14;
const int ROTARY_PIN_B = 15;
const int ROTARY_BUTTON_PIN = 13;
#elif defined(ARDUINO_AVR_NANO)
const int ROTARY_PIN_A = 2;
const int ROTARY_PIN_B = 3;
const int ROTARY_BUTTON_PIN = 5;
#endif

const int LED_PIN = LED_BUILTIN;
const int LED_ON = HIGH;
const int LED_OFF = LOW;

SimpleHacks::QDecoder qdec(ROTARY_PIN_A, ROTARY_PIN_B, false);
ace_button::AceButton button(ROTARY_BUTTON_PIN);

volatile int rotaryCount = 0;
volatile int _rotaryCount = 0;
int lastLoopDisplayedRotaryCount = 0;

void handleEvent(ace_button::AceButton *button, uint8_t eventType, uint8_t buttonState)
{
    switch (eventType)
    {
    case ace_button::AceButton::kEventPressed:
        digitalWrite(LED_PIN, LED_ON);
        break;
    case ace_button::AceButton::kEventReleased:
        digitalWrite(LED_PIN, LED_OFF);
        break;
    case ace_button::AceButton::kEventLongPressed:
        // noop
        break;
    }
}

void IsrForQDEC(void)
{
    SimpleHacks::QDECODER_EVENT event = qdec.update();
    if (event & SimpleHacks::QDECODER_EVENT_CW)
    {
        if (_rotaryCount < 0)
            _rotaryCount = 0;
        _rotaryCount++;

        if (_rotaryCount == 2)
        {
            _rotaryCount = 0;
            rotaryCount = rotaryCount + 1;
            if (rotaryCount > 10)
                rotaryCount = 0;
        }
    }
    else if (event & SimpleHacks::QDECODER_EVENT_CCW)
    {
        if (_rotaryCount > 0)
            _rotaryCount = 0;
        _rotaryCount--;

        if (_rotaryCount == -2)
        {
            _rotaryCount = 0;
            rotaryCount = rotaryCount - 1;
            if (rotaryCount < 0)
                rotaryCount = 10;
        }
    }
    return;
}

void setup()
{
    Serial.begin(9600);
    delay(2000);

    qdec.begin();

    attachInterrupt(digitalPinToInterrupt(ROTARY_PIN_A), IsrForQDEC, HIGH);
    attachInterrupt(digitalPinToInterrupt(ROTARY_PIN_A), IsrForQDEC, LOW);
    attachInterrupt(digitalPinToInterrupt(ROTARY_PIN_B), IsrForQDEC, HIGH);
    attachInterrupt(digitalPinToInterrupt(ROTARY_PIN_B), IsrForQDEC, LOW);

    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LED_OFF);
    pinMode(ROTARY_BUTTON_PIN, INPUT_PULLUP);

    ace_button::ButtonConfig *buttonConfig = button.getButtonConfig();
    buttonConfig->setEventHandler(handleEvent);
    buttonConfig->setFeature(ace_button::ButtonConfig::kFeatureClick);
    buttonConfig->setFeature(ace_button::ButtonConfig::kFeatureDoubleClick);
    buttonConfig->setFeature(ace_button::ButtonConfig::kFeatureLongPress);
    buttonConfig->setFeature(ace_button::ButtonConfig::kFeatureRepeatPress);
}

void loop()
{
    button.check();

    int newValue = rotaryCount;
    if (newValue != lastLoopDisplayedRotaryCount)
    {
        lastLoopDisplayedRotaryCount = newValue;
        Serial.println(newValue);
    }
}

I also needed the delay before the qdec.begin() or I won't get output from the loop. I did not investigate on this and where the problem without the delay came from. My rotary encoder is a KY-040 (I think it's some cheap china version).

EDIT: I use the following libraries in this project:

simplehacks/QDEC@^1.0.2
bxparks/AceButton@^1.8.3

@facchinm
Copy link
Member

facchinm commented Jun 7, 2021

Hi @Chick92 and @fsievers ,
the issue here is about the pull mode being overridden when attachInterrupt is being called.
In fact, mbed removes the pull configuration here https://github.com/ARMmbed/mbed-os/blob/master/drivers/source/InterruptIn.cpp#L34 ; we try to restore it here based on some heuristics but it doesn't perfectly match the normal behaviour.
I'll take a close look at other possible workarounds; in the meantime, moving pinMode(INPUT, PULLUP) after attachInterrupt(CHANGE) will do the trick (ugly stuff, I must admit)

facchinm added a commit to facchinm/ArduinoCore-mbed that referenced this issue Jun 7, 2021
@Chick92
Copy link
Author

Chick92 commented Jun 7, 2021

@facchinm That's fixed it! Thankyou so much! the Pico is an amazing (and very nicely priced) board, i was starting to worry i wouldn't have time to wait for this to be solved before starting my design. Thanks again!

@fsievers The above explains why yours worked and my didn't, thanks for helping. As well, you pasting your code has (I think) helped me out massively with another issue! - PaulStoffregen/Encoder#69
I spent days trying to workout what the compiler referred to the processor as, to no avail. Stack overflow issues and github issues!

Both of you, thankyou!

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

Successfully merging a pull request may close this issue.

3 participants