Morning programmers,
I have been playing around with a piece of code to use a rotary encoder with the Uno utilizing interrupts. With the uno it works perfect. When I transition the same code to the Mega it doesn't work. I looked up the interrupt pins that are available on the mega and have rotated through all three sets. (2/3, 18/19 and 20/21) I even tried 52/53 and that didnt work either. Is there something with the Mega and interrupts that I don't know to enable them somehow. Not sure how to phrase the question.
Here is the code unaltered from elsewhere on the net that works on Uno but not Mega:
const int pinA = 2; // Our first hardware interrupt pin is digital pin 2
const int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
void setup() {
pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
Serial.begin(9600); // start the serial monitor link
Serial.println("-------------rotaryEncoderWorkup_v1.1-------------------");
Serial.println(encoderPos);
}
void PinA(){ //from interrupt above
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos --; //decrement the encoder's position count
if (encoderPos>18){encoderPos=18;}
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinB(){ //from interrupt above
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos ++; //increment the encoder's position count
if (encoderPos>18){encoderPos=0;}
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void loop(){
if(oldEncPos != encoderPos) {
Serial.println(encoderPos);
oldEncPos = encoderPos;
}
}
Thank you for your expertise
amachinetech
update: l also tried a known second mega board just to make sure that the board wasn't damaged somehow so it isn't that
@ larryD
the way that it was explained to me was that the interrupts have to be stopped to allow the function to read the variables. Also I didn't write this one, I shamelessly cut and pasted it from a tutorial and attempted to bend it to my will. And as it works just fine with Uno, why would I fix something that isn't broken.
amachinetech
would be port E with everything stripped off except bits 4 and 5 (count starting with 0 from the left).
That's also
PINE & 0x30
Using binary looks easiest, you'll have to check other uses of hex or binary constants and adjust them accordianly, and look closley at the attach statements.
Srsly I'd do it for ya, but cannot test it anyway, so.
You on the right track. Looks like enough "common" sense should get you there if no one does in the meantime.
Since Im not well traveled in this area of the IDE, the above line means "read the entire port but ignore everything except pins 4 and 5" correct? Or do I have it backwards and it should be
in which case it would read:
"read the entire port but ignore everything except pins 2 and 3"
// https://wokwi.com/projects/357766586004638721
// https://forum.arduino.cc/t/uno-vs-mega-with-a-rotary-encoder/1095187
const int pinA = 2; // Our first hardware interrupt pin is digital pin 2
const int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
void setup() {
pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
// attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
// attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
attachInterrupt(digitalPinToInterrupt(pinA), PinA, RISING);
attachInterrupt(digitalPinToInterrupt(pinB), PinB, RISING);
Serial.begin(9600); // start the serial monitor link
Serial.println("-------------rotaryEncoderWorkup_v1.1-------------------");
Serial.println(encoderPos);
}
void PinA(){ //from interrupt above
cli(); //stop interrupts happening before we read pin values
reading = PINE & 0x30; // read all eight pin values then strip away all but pinA and pinB's values
if(reading == B00110000 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos --; //decrement the encoder's position count
if (encoderPos>18){encoderPos=18;}
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00010000) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinB(){ //from interrupt above
cli(); //stop interrupts happening before we read pin values
reading = PINE & 0x30; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00110000 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos ++; //increment the encoder's position count
if (encoderPos>18){encoderPos=0;}
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00100000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void loop(){
if(oldEncPos != encoderPos) {
Serial.println(encoderPos);
oldEncPos = encoderPos;
}
}
BTW having pinA and PinA crossed my eyes quite a bit. Just sayin'.
I tested this and it works perfectly. As for the error I tried it both ways and I didn't get it to work so I thought it was something I was doing. Once again thank you for helping me understand the little quirks of this hobby we have decided to bash ourselves over the head with.
I was trying to kinda lock in what I found looking at the Mega, and I noticed that pins 18 and 19 are on port D at bit numbers 2 and 3, so I figured that changing to those pins
And until my beach buddy to knocks me up, she who must never be kept waiting, I took all the magic out of the code and moved it up top to show all three sets of pins can be used.
// https://wokwi.com/projects/357828229247849473
// https://forum.arduino.cc/t/uno-vs-mega-with-a-rotary-encoder/1095187
/*
// digital pins 2 and 3
# define PINX PINE
# define maskAB 0x30
# define maskB 0x10
# define maskA 0x20
# define pinA 2
# define pinB 3
*/
/*
// digital pins 18 and 19
# define PINX PIND
# define maskAB 0x0c
# define maskB 0x04
# define maskA 0x08
# define pinA 19
# define pinB 18
*/
/**/
// digital pins 20 and 21
# define PINX PIND
# define maskAB 0x03
# define maskB 0x01
# define maskA 0x02
# define pinA 21
# define pinB 20
/**/
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
void setup() {
pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(digitalPinToInterrupt(pinA),PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(digitalPinToInterrupt(pinB),PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
Serial.begin(9600); // start the serial monitor link
Serial.println("-------------rotaryEncoderWorkup_v1.1-------------------");
Serial.println(encoderPos);
}
void PinA(){ //from interrupt above
cli(); //stop interrupts happening before we read pin values
reading = PINX & maskAB; // read all eight pin values then strip away all but pinA and pinB's values
if(reading == maskAB && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos --; //decrement the encoder's position count
if (encoderPos>18){encoderPos=18;}
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == maskB) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinB(){ //from interrupt above
cli(); //stop interrupts happening before we read pin values
reading = PINX & maskAB; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == maskAB && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos ++; //increment the encoder's position count
if (encoderPos>18){encoderPos=0;}
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == maskA) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void loop(){
if(oldEncPos != encoderPos) {
Serial.println(encoderPos);
oldEncPos = encoderPos;
}
}
The appropriate group of defines can be uncommented. I know it could be modernized and even made slick by old school standards, so knock yourselves out on that. I'm out the door.