Encoder interrupts not working right when I use a shift register...

I'm trying to use a mechanical 2 channel rotary encoder as a user interface device. I had one of the switches connected to interrupt 0 with the input pullup turned on, and the other one connected to pin 3, which is set up as a regular input pullup (without the interrupt). Pin 3 is read within the interrupt function, using an "if-else" statement to decide whether to increase or decrease the counting variable.

Up until recently, I had it set up with one button to select stuff, but the project in question (an alarm clock using a character LCD) will need more I/O pins than the ATMEGA328P itself has. I figured shift registers would do the trick. So, I set up the "supporting code" for an input shift register in a separate function that is called at the beginning of the main loop. Now, the interrupt service routine still gets triggered, but it never seems to read the second switch on the encoder as it should within the interrupt function. As a result, I can only turn the knob in one direction.

Since I did do quite a bit of rewiring, I checked the wires to the encoder as well as it's ground connections. Then, to rule out the relatively small chance of the ATmega328 itself being broken, I connected the wire for the second switch directly to ground and spun the knob with only the first one connected. Then, it turned only in one direction, but opposite of what it was doing before, so this proved that both the software and the hardware were still able to read pin 3-but maybe not reliably enough to catch a momentary pulse...

I did comment out the shift register function to ensure that the encoder, itself, was still working properly (Hardware and software wise, since I had to mess with both to add the shift register) Without the shift register code, the encoder works as it should, reading rotation in either direction.

Although I have things in my shift register code that I don't have elsewhere (A few For loops and bitRead() and bitWrite() ) I've read the references to those functions and see nothing that suggests that they will interfere with interrupts.

At this point, I'm lost. All I know is that it must be some kind of software issue because commenting out the shift register code makes the encoder work again. If the encoder didn't use an interrupt, I would suspect that my code was "too long" for the processor to catch the encoder pulses...

At this point, I'm lost.

Me too. Your explanation is a bit hard to follow. Can you post the code you are using, and a schematic of your circuit.

If your encoder only works in one direction, I get the impression from your first two paragraphs that you don't fully understand how to read the signals from an encoder.

It is also unclear from your explanation, what you are doing with your shift register exactly.

If you are short of pins because of your LCD, I'd recommend getting one of the adapters so you can communicate with your LCD over I2C, using many fewer pins, and then you will be able to connect your encoder properly.

Here’s the ISR for the encoder.

void encoder()
{

if( digitalRead(ENCB) == LOW )
{
Pos++; //if B and A are on at the same time, the knob is moving one way
}
else
{
Pos–; //else it’s moving the other way
}

if(sendCnt)
{

if(Pos > 1)
{
Cnt++;
Pos = 0;
//increase Count by 1 and reset position to 0
}
else if(Pos < -1)
{
Cnt–;
Pos = 0;
//decrease Count by 1 and reset position to 0
}

}

}

END ENCODER CODE

Here’s the code for the shift register:

void ReadSws()
{

CSIS = SrIn();

for(int z = 0; z < 8; z++)
{
Sw[z] = bitRead(CSIS, z);
}
if(PSIS != CSIS)
{
if(millis() - lastDb > DbTime)
{
if(debug == true)
{
Serial.print("Debounced switch status: ");
}
for(int z = 0; z < 8; z++)
{
DbSw[z] = Sw[z];
if(debug == true)
{
Serial.print(DbSw[z]);
}

}
if(debug == true)
{
Serial.println(" ");
}
PSIS = CSIS;
lastDb = millis();

if(DbSw[Knb] == false) //If active low switch is on
{
KnobPressed = true;
DbSw[Knb] = true; //Reset DB variable to off
}
}
}
else
{
lastDb = millis();
}

}

byte SrIn(){
byte x;
digitalWrite(CE, HIGH);
digitalWrite(PL, LOW); //Tells shift register to read inputs
delayMicroseconds(UsecD1);
digitalWrite(PL, HIGH); //Tells shift register to store input status
digitalWrite(CE, LOW);
for(int z = 0; z < 8; z++)
{

bitWrite(x, z, digitalRead(Din) );
digitalWrite(ClkIn, HIGH);
digitalWrite(ClkIn, LOW);

}
return x;
}

END SHIFT REGISTER CODE

PartClockMenu3 is a functional build, which does not include code for the shift register.

Nw_ShiftRegClock is the one that is having the problem with the encoder only reading in one direction. It includes the shift
register code. The shift register must be somehow related because when I comment out the call to the shift register read
function, the knob works fine.

I don’t have the actual LCD programmed in yet, but I was planning ahead with the shift register so that I wouldn’t have to program multiple things in at once. (The shift register and the LCD) In other words, I was planning on adding the LCD when I was certain that everything else was working as expected. The way I am viewing the results of the program at the moment is via Serial.print()

PartClockMenu3.ino (18.1 KB)

Nw_ShiftRegClock.ino (20.1 KB)

Here’s a schematic as well.

Yes, someone who has a similar goal as me who is stuck in "one-direction" problem with the encoder!

Hello! For about a month, I have been trying to figure out how to make the optical encoder to register so I could control a motor fowards and backwards with no luck.

It only moves one way no matter which way I move the encoder strip.

I figured that it was a power problem but placing a .1microfarad didn't help at all...

I feel like the interrupts aren't just quick enough to register the Highs and Lows or something but there are tons of people who have accomplished this feat with no problem.

Just wondering, is your encoder 4 pin or 6 pin package. If it is 6 pin, I read somewhere that some people got success on working them correctly by putting pull up and pull down resistors and some resistors on the LED pins itself.

Since my project doesn’t really NEED an encoder, I decided instead to use Up and Down keys along with “Enter” and “Cancel” buttons. The reason I originally wanted a knob was so that I could move quickly by spinning the knob fast, and get right on the money by turning it slow (in other words, a pseudo-analog type of control). But, I discovered I could do that by having the incrementing of the minutes speed up the longer the button is held.

One solution I contemplated, but never actually tried, was to have an ATtiny84, whose job was basically just reading the encoder and talking back to the ATmega.

In a nutshell, the ATtiny would count pulses from the encoder, hold the “Forward” or “backward” pin HIGH until it gets an ACK from the ATmega, then it would subtract a count and momentarily turn off the direction pin, then turn it back on when the ACK input goes low.

This would prevent timing issues with the ATmega from interfering with the encoder process, as well as negating the need for interrupts (Not that they’re necessarily bad, but there’s quite a few specific things to keep track of that newcomers might find daunting. As I said, I did get it to work, but it quit when I added on to my program).

However, if you’re reading an encoder for feedback purposes rather than a user interface (i.e. some kind of automated machine with precise positioning needs) you’d probably need either a faster processor, more efficient code, or both. One could also have a separate MCU for reading the encoder(s) and controlling the appropriate motor(s), which would simply listen for commands from the main computer.