74HC165 shift register with shiftIn() wraps last bit around the beginning

I'm trying to get 74HC165 shift registers work with the shiftIn() function instead of using SPI (I'm using the SPI bus for a different set of shift registers that do something else in my setup) and there's the following strange behavior:

If I use only one chip, the bits are shifted by one place to the left, and the last bit is wrapped around and appears in the first place of the byte. If I use two chips, daisy chained, again the bits are shifted by one place to the left, the very last bit of the two bytes is wrapped around and appears in the first position of both bytes, with the first bit of the second byte being a bit noisy, and the last bit of the first byte is completely absent.

Here's the code:

const int latchPin = 2;
const int dataPin = 3;
const int clockPin = 4;
const int numOfChips = 2;

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, INPUT);
  pinMode(clockPin, OUTPUT);
  
  Serial.begin(115200);
}

void loop() {
  digitalWrite(latchPin, LOW);
  delayMicroseconds(20);
  digitalWrite(latchPin, HIGH);
  for(int i = 0; i < numOfChips; i++){
    byte regByte = shiftIn(dataPin, clockPin, MSBFIRST);
    Serial.print("byte");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(regByte);
  }
  delay(500);
}

First a question

alexandros301:
I'm using the SPI bus for a different set of shift registers that do something else in my setup

Any reason why you can't just use a separate latch and include them in the chain?

alexandros301:
If I use only one chip, the bits are shifted by one place to the left, and the last bit is wrapped around and appears in the first place of the byte.

So you expect for example 182 and get 109? I would say printing it in decimal doesn't make it very clear...

If so, I do suspect a wiring issue... Can you make a simple schematic? Hand drawing is a million times better than a Fritzing breadboard mess.

septillion:
First a questionAny reason why you can't just use a separate latch and include them in the chain?

Mainly because of the setup and long wires which have cause me issues in the past (even with setting the speed of the SPI).

septillion:
So you expect for example 182 and get 109? I would say printing it in decimal doesn't make it very clear...

I'm actually receiving the serial data in Pure Data and parsing them there, where I split the bytes to their eight bits.

septillion:
If so, I do suspect a wiring issue... Can you make a simple schematic? Hand drawing is a million times better than a Fritzing breadboard mess.

Here's a schematic I made in Kicad (don't really know how to make it appear in this post, without having to download it):

alexandros301:
Mainly because of the setup and long wires which have cause me issues in the past (even with setting the speed of the SPI).

Doubt shiftIn() is really slower than SPI at it's slowest setting....

alexandros301:
I'm actually receiving the serial data in Pure Data and parsing them there, where I split the bytes to their eight bits.

Ahhhhhh, would you mind showing those calculations? Because they sound very suspicious to me.

Or easier, print every byte read simply binairy to the monitor:

   Serial.print("byte");
    Serial.print(i);
    Serial.print(": 0b");
    Serial.println(regByte, BIN);


Schematic looks fine, so I'm suspecting your non-Arduino calculations.

septillion:
Doubt shiftIn() is really slower than SPI at it's slowest setting....

I don't mind that as these shift registers will only be used at boot, only once, so speed is not an issue.

septillion:
Ahhhhhh, would you mind showing those calculations? Because they sound very suspicious to me.

In Arduino pseudo-code, the equivalent of what I'm doing in Pd is this (I'm including shiftIn() to make clear where the byte comes from, thisBit is the actual bit value I'm reading):

byte regByte = shiftIn();
int thisBit;
for (int i = 0; i < 8; i++) {
  int bitVal = pow(2, i) & regByte;
  if (bitVal != 0) {
    thisBit = 1;
  }
  else {
    thisBit = 0;
  }
}

septillion:
Or easier, print every byte read simply binairy to the monitor:

   Serial.print("byte");

Serial.print(i);
    Serial.print(": 0b");
    Serial.println(regByte, BIN);

Done, still get the same behavior.

Then show some Serial output please.

Of course complemented with a picture of the settings :slight_smile: For a start I would suggest an all switches on and an all switches off dump :slight_smile:

Because I see nothing wrong in code. And I know shiftIn() and a HC165 work together fine. There most be something wrong in your wiring or in you parsing the result.

I've attached the output of the serial monitor. I'm away from home and I have an eight-position DIP switch and two push buttons, so the first shift register has only its first and last pins connected to a switch, and the second has seven pins connected, cause pin 6 of the DIP switch is borken.
On the right side you can see the right most bit toggling between 0 and 1 (the noisy bit getting input from the last pin of the second chip), the second from left is the broken pin so it's always 0.
On the left side you can see either 1, or 11. A single 1 appears because of the last pin of the second chip reading high, which is wrapped around to the beginning of the first byte. The second 1 appears when I press the push-button wired to the first pin of the first chip, and this pin appears in the second bit of the byte.

Again, sorry for uploading like this, can't seem to find how to include the image in the post.

Oww, might see it.... You have CLKINH/!CE floating... Tie it to GND. Never leave inputs floating...

septillion:
Oww, might see it.... You have CLKINH/!CE floating... Tie it to GND. Never leave inputs floating...

No it's not that, sorry, I forgot to include it in the schematic. It is tight to GND. Thanks for all the help so far!

Damn :frowning:

You never ever elsewhere in the code toggle the clock pin?

Aka, can you post all of the code used for the last test?

And are you absolutely a 100% sure the wiring is correct? Aka, did you triple check it with a DMM?

septillion:
Damn :frowning:

You never ever elsewhere in the code toggle the clock pin?

Aka, can you post all of the code used for the last test?

And are you absolutely a 100% sure the wiring is correct? Aka, did you triple check it with a DMM?

I'm not toggling the clock pin, only whatever happens inside shiftIn().
I don't have a DMM at hand right now, but I've checked the connections quite thoroughly, as much as I can at least. If there was some short, I guess the behavior would be more unpredictable, and not this constant bit shifting that's happening, no?
Here's the last code I used (there's a commented out "digitalWrite(clockPin, LOW); before calling shiftIn() that I tried using, but the behavior is the same, whether I use it or not):

const int latchPin = 2;
const int dataPin = 3;
const int clockPin = 4;
const int numOfChips = 2;

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, INPUT);
  pinMode(clockPin, OUTPUT);
  
  Serial.begin(115200);
  while (!Serial);
}

void loop() {
  digitalWrite(latchPin, LOW);
  delayMicroseconds(20);
  digitalWrite(latchPin, HIGH);
  for(int i = 0; i < numOfChips; i++){
    //digitalWrite(clockPin, LOW);
    byte regByte = shiftIn(dataPin, clockPin, MSBFIRST);
    Serial.print("byte");
    Serial.print(i);
    Serial.print(": ");
    Serial.print(regByte, BIN);
    Serial.print("\t");
  }
  Serial.println();
  delay(500);
}

One final brain fart! Do you have bypass capacitors close to every HC165?

(And I assume it's all on a single PCB aka traces are <10cm?)

I finally nailed it. As you said, it was a clocking issue. Arduino's built-in shiftIn() function clocks the chips inversely. I copied the function code and inverted the clocking voltages and it works as expected now.

Here's the built-in function (found in hardware/arduino/avr/cores/arduino/wiring_shift.c):

uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
  uint8_t value = 0;
  uint8_t i;

  for (i = 0; i < 8; ++i) {
    digitalWrite(clockPin, HIGH);
    if (bitOrder == LSBFIRST)
      value |= digitalRead(dataPin) << i;
    else
      value |= digitalRead(dataPin) << (7 - i);
    digitalWrite(clockPin, LOW);
  }
  return value;
}

And here's the code that works with the 74HC165 shift register:

uint8_t shiftIn165(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
  uint8_t value = 0;
  uint8_t i;

  for (i = 0; i < 8; ++i) {
    digitalWrite(clockPin, LOW);
    if (bitOrder == LSBFIRST) {
      value |= digitalRead(dataPin) << i;
    }
    else {
      value |= digitalRead(dataPin) << (7 - i);
    }
    digitalWrite(clockPin, HIGH);
  }
  return value;
}

The only thing that changes is that the clock pin is first pulled LOW and then HIGH, instead of going the other way around, as it's written in the original source code.
Pulling the clock pin LOW before calling the built-in function doesn't work properly, as it seems that the clock pin of the 74HC165 shift register must be first pulled low, then all the incoming data must be read, and then the pin must be pulled HIGH. If one simply pulls the clock pin LOW and then calls shiftIn(), the clock pin will be pulled HIGH again before the data are read, and the unexpected behavior described in this thread will occur.

Thanks a million for all the help and brain storming!

1 Like

Now you said it it does makes sense. I did check the datasheet of the 74HC165 but it told me

shifts one place to the right (Q0→ Q1 → Q2, etc.) with each positive-going clock transition.

Which of course, little counter intuitive, you want to sample the data on the LOW going clock.

A lot of people (including myself) have build it. But it seems your chips react a little slower. Aka, the data isn't shifted yet when you read it. Supply voltage a bit low?

septillion:
Now you said it it does makes sense. I did check the datasheet of the 74HC165 but it told me Which of course, little counter intuitive, you want to sample the data on the LOW going clock.

A lot of people (including myself) have build it. But it seems your chips react a little slower. Aka, the data isn't shifted yet when you read it. Supply voltage a bit low?

Not sure what you mean by "supply voltage a bit low?". If the data is shifted before read, wouldn't a delay solve this? I am personally happy with the solution of inverting the clock pulses. It would probably be nice if the Arduino built-in function could use a variable inside shiftIn() to determine those clock pulses.

alexandros301:
Not sure what you mean by "supply voltage a bit low?".

Supply at the hc165 <5V?

alexandros301:
If the data is shifted before read, wouldn't a delay solve this?

Yes, but don't do it. This is better.

alexandros301:
It would probably be nice if the Arduino built-in function could use a variable inside shiftIn() to determine those clock pulses.

Agreed :slight_smile: Could be made just to act like the 4 modes of SPI.