Convert to PinChangeInterrupt

I am using the following example of a menu using an encoder:

This works great using the hardware interrupt pins 2 and 3.
However, I must change this using "#include <PinChangeInterrupt.h>" using pins 5 and 6.

The original code using hardware interrupts:

static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3

In 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)
 
Interrupts Handlers:
//Rotary encoder interrupt service routine for one encoder pin
void PinA(){
  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
    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
}

//Rotary encoder interrupt service routine for the other encoder pin
void PinB(){
  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
    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
}

I have tried to change this to:

static int pinEncoderA = 5; 
static int pinEncoderB = 6; 

  pinMode(pinEncoderA, INPUT_PULLUP);
  pinMode(pinEncoderB, INPUT_PULLUP); 

//Rotary encoder interrupt service routine for one encoder pin
void EncoderA() {
  cli();                                       //stop interrupts happening before we read pin values
  reading = PIND & 0xC;                        // read all eight pin values then strip away all but pinEncoderA and pinEncoderB'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
   if (reading == B01100000 && aFlag) { 
    encoderPos--;                              //decrement the encoder's position count
    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 pinEncoderB to signal the transition to detent from free rotation
 } else if (reading == B01000000) bFlag = 1;
  sei();                                       //restart interrupts
}

//Rotary encoder interrupt service routine for the other encoder pin
void EncoderB() {
  cli();                                       //stop interrupts happening before we read pin values
  reading = PIND & 0xC;                        //read all eight pin values then strip away all but pinEncoderA and pinEncoderB'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
    if (reading == B01100000 && aFlag) { 
    encoderPos++;                              //increment the encoder's position count
    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 pinEncoderA to signal the transition to detent from free rotation
  sei();                                       //restart interrupts
}

But unfortunately nothing working.
Can anyone explain the correct usage of:
if (reading == B00001100 && bFlag)
etc.

Couldn't you use the encoder library? it will do the right thing for you depending on the pins you choose.


To your questions

the current code looks at rising front

With PinChangeInterrupt, you'll get (as the name indicates) an interrupt every time there is a change, so both for the up and down front. Your ISR will need to deal with that.


PIND on an Arduino UNO is a register that holds the current digital input values of the PORTD pins. These correspond to digital pins 0 to 7 on the 328P

  • Bit 0 → digital pin 0
  • Bit 1 → digital pin 1
  • ...
  • Bit 7 → digital pin 7

so

reading = PIND & 0xC;  // 0xC is 1100

will keep only the status of the pins 2 and 3 and

if(reading == B00001100) 

will check if both pin 2 and 3 were high

the previous code uses and extra flag to see (aFlag, bFlag) to see where they are in the quadrature encoding.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.