Daisy chaining more than four Shift Registers (SN74HC165N)

Hi all!

I am busy working with the Arduino Playground Shift Registers tutorial: Arduino Playground - ShiftRegSN74HC165N

It works like a charm, except for when I daisy chain more than four shift registers. It will just ignored every shift register after the fourth. I think that is because of what it says in the code:

Of course you can daisy chain as many as you like while still
using only 4 Arduino pins (though you would have to process
them 4 at a time into separate unsigned long variables).

I just can't figure out how to process them 4 at a time in the code. My project consists of 80 buttons that can be pressed, so in the end I want to daisy chain 10 shift registers.

Can anybody help me?
Thanks!!

Code in the tutorial:

/*
 * SN74HC165N_shift_reg
 *
 * Program to shift in the bit values from a SN74HC165N 8-bit
 * parallel-in/serial-out shift register.
 *
 * This sketch demonstrates reading in 16 digital states from a
 * pair of daisy-chained SN74HC165N shift registers while using
 * only 4 digital pins on the Arduino.
 *
 * You can daisy-chain these chips by connecting the serial-out
 * (Q7 pin) on one shift register to the serial-in (Ds pin) of
 * the other.
 * 
 * Of course you can daisy chain as many as you like while still
 * using only 4 Arduino pins (though you would have to process
 * them 4 at a time into separate unsigned long variables).
 * 
*/

/* How many shift register chips are daisy-chained.
*/
#define NUMBER_OF_SHIFT_CHIPS   2

/* Width of data (how many ext lines).
*/
#define DATA_WIDTH   NUMBER_OF_SHIFT_CHIPS * 8

/* Width of pulse to trigger the shift register to read and latch.
*/
#define PULSE_WIDTH_USEC   5

/* Optional delay between shift register reads.
*/
#define POLL_DELAY_MSEC   1

/* You will need to change the "int" to "long" If the
 * NUMBER_OF_SHIFT_CHIPS is higher than 2.
*/
#define BYTES_VAL_T unsigned int

int ploadPin        = 8;  // Connects to Parallel load pin the 165
int clockEnablePin  = 9;  // Connects to Clock Enable pin the 165
int dataPin         = 11; // Connects to the Q7 pin the 165
int clockPin        = 12; // Connects to the Clock pin the 165

BYTES_VAL_T pinValues;
BYTES_VAL_T oldPinValues;

/* This function is essentially a "shift-in" routine reading the
 * serial Data from the shift register chips and representing
 * the state of those pins in an unsigned integer (or long).
*/
BYTES_VAL_T read_shift_regs()
{
    long bitVal;
    BYTES_VAL_T bytesVal = 0;

    /* Trigger a parallel Load to latch the state of the data lines,
    */
    digitalWrite(clockEnablePin, HIGH);
    digitalWrite(ploadPin, LOW);
    delayMicroseconds(PULSE_WIDTH_USEC);
    digitalWrite(ploadPin, HIGH);
    digitalWrite(clockEnablePin, LOW);

    /* Loop to read each bit value from the serial out line
     * of the SN74HC165N.
    */
    for(int i = 0; i < DATA_WIDTH; i++)
    {
        bitVal = digitalRead(dataPin);

        /* Set the corresponding bit in bytesVal.
        */
        bytesVal |= (bitVal << ((DATA_WIDTH-1) - i));

        /* Pulse the Clock (rising edge shifts the next bit).
        */
        digitalWrite(clockPin, HIGH);
        delayMicroseconds(PULSE_WIDTH_USEC);
        digitalWrite(clockPin, LOW);
    }

    return(bytesVal);
}

/* Dump the list of zones along with their current status.
*/
void display_pin_values()
{
    Serial.print("Pin States:\r\n");

    for(int i = 0; i < DATA_WIDTH; i++)
    {
        Serial.print("  Pin-");
        Serial.print(i);
        Serial.print(": ");

        if((pinValues >> i) & 1)
            Serial.print("HIGH");
        else
            Serial.print("LOW");

        Serial.print("\r\n");
    }

    Serial.print("\r\n");
}

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

    /* Initialize our digital pins...
    */
    pinMode(ploadPin, OUTPUT);
    pinMode(clockEnablePin, OUTPUT);
    pinMode(clockPin, OUTPUT);
    pinMode(dataPin, INPUT);

    digitalWrite(clockPin, LOW);
    digitalWrite(ploadPin, HIGH);

    /* Read in and display the pin states at startup.
    */
    pinValues = read_shift_regs();
    display_pin_values();
    oldPinValues = pinValues;
}

void loop()
{
    /* Read the state of all zones.
    */
    pinValues = read_shift_regs();

    /* If there was a chage in state, display which ones changed.
    */
    if(pinValues != oldPinValues)
    {
        Serial.print("*Pin value change detected*\r\n");
        display_pin_values();
        oldPinValues = pinValues;
    }

    delay(POLL_DELAY_MSEC);
}

No, that's because read_shift_register return a single integer, so can only handle 16 (Uno/Mega), or 32 (Due/??)
values.

You need to call read_shift_register repeatedly and stick the results in an array I guess.

Oh, what a kludge of code!
So much cleaner to do with SPI.transfer, something like this.

#include <SPI.h>
byte buttonPin = 2;
byte latchPin = 10;
numShift = 8; // the number of shift registers you have
byte dataArray[numShift];
byte x; // counter variable
void setup(){
pinMode (latchPin, OUTPUT);
digitalWrite (latchPin, HIGH);
Serial.begin(9600);
SPI.begin();
}
void loop(){
digitalWrite (latchPin, LOW);
digitalWrite (latchPin, HIGH); // capture the inputs at the shift registers
for (x=0; x<numShift; x=x+1){
dataArray[x] = SPI.transfer(0); // 0's go out on MOSI while data is read in on MISO
}
for (x=0; x<numShift; x=x+1){
Serial.println (dataArray[x]);
}
}

I was going to only read on a button press, but got distracted. You can add that in.

missyskae:
I am busy working with the Arduino Playground Shift Registers tutorial: http://playground.arduino.cc/Code/ShiftRegSN74HC165N

http://playground.arduino.cc/Code/ShiftRegSN74HC165N

Hi missyskae, I'm just wondering if you are using that breadboard layout verbatim? I really hate layout diagrams like that and would much prefer a proper schematic. However after forcing myself to look closely, there appears to be a major oversight in the hardware!

Pin 16 on the 74HC165 (that's the top left pin of each chip on the layout diagram) MUST be connected to the 5 volt bus (Vcc)!

To be honest, the real puzzle is how that circuit even works at all. Presumably there must just enough feed though current coming from the input protection diodes (courtesy of the pullup resistors) to allow it to still operate (though probably only marginally). This is very dubious, so please connect the Vcc pins on every chip.

Hi all,

Thanks for helping!

I'm not a programming/electronics hero, but I think I managed to work something out with your help.
I found this tutorial/explanation using the SPI-method: https://www.gammon.com.au/forum/?id=11979
So far it's working with 2 shift registers, so I'm optimistic it will work with 10 :slight_smile:
This is the code I'm working with:

// Demo sketch to read from a 74HC165 input shift register
// Author: Nick Gammon
// Date:   23 March 2013

// Pin connections for Uno and similar:

// Chip pin 1 (/PL)  goes to LATCH (D9)  (or any other pin by changing LATCH below)
// Chip pin 2 (CP)   goes to SCK   (D13)
// Chip pin 9 (Q7)   goes to MISO  (D12)

// Pin connections for Mega2560:

// Chip pin 1 (/PL)  goes to LATCH (D9)  (or any other pin by changing LATCH below)
// Chip pin 2 (CP)   goes to SCK   (D52)
// Chip pin 9 (Q7)   goes to MISO  (D50)


#include <SPI.h>

const byte LATCH = 9;

void setup ()
{
  SPI.begin ();
  Serial.begin (115200);
  Serial.println ("Begin switch test.");
  pinMode (LATCH, OUTPUT);
  digitalWrite (LATCH, HIGH);
}  // end of setup

byte optionSwitch;
byte oldOptionSwitch; // previous state

void loop ()
{
  digitalWrite (LATCH, LOW);    // pulse the parallel load latch
  digitalWrite (LATCH, HIGH);
  optionSwitch = SPI.transfer (0);
  
  byte mask = 1;
  for (int i = 1; i <= 8; i++)
    {
    if ((optionSwitch & mask) != (oldOptionSwitch & mask))
      {
      Serial.print ("Switch ");
      Serial.print (i);
      Serial.print (" now ");
      Serial.println ((optionSwitch & mask) ? "closed" : "open");
      }  // end of bit has changed
    mask <<= 1;  
    }  // end of for each bit
  
  oldOptionSwitch = optionSwitch;
  delay (10);   // debounce
}  // end of loop

@stuart0
I have been wondering why it was somewhat unreliable and I think you gave the answer! (I think you mean the top right pin, right? That is the VCC pin). It is so much more stable right now.

I am so happy with your help guys, thanks!