Skip to main content

There exists little practical information assisting with the experimentation of '595 shift registers.  This article and below Arduino sketch aim to address this vacuum.  The sketch simulates one or more (chained) 595 shift registers on the serial monitor while simultaneously driving the IC or ICs via the Arduino's pins.  This allows concurrent theoretical and practical experimentation with the 74HC595 first-in-first-out (FIFO) shift register family.

Below is output from an example run of the sketch.  Input instructions as typed are shown in bold.

Simulation and simultaneous drive of 74HC595 shift register.
Ewan Parker 27th May 2016.
$Id: 74HC595_Simulator.ino,v 1.4 2016/06/07 17:37:28 ewan Exp $

* If /OE is HIGH then outputs QA..QH are disabled.
* If /OE is LOW then outputs QA..QH are enabled.
* If /SRCCLR is LOW then the shift register is cleared.
* If SRCLK goes HIGH and /SRCLR is HIGH then the first stage of the shift
  register is set to the value of SER and other stages store the data of the
  previous stage, respectively.
* If RCLK goes HIGH then the shift register data is stored in the storage
  register.

There must be a delay of up to 19 ns after SRCLK goes HIGH and before RCLK goes
HIGH.  This ensures the storage register receives stable data from the shift
register.  If the clocks are tied together then the shift register is one pulse
ahead of the storage register.

   IN#1: /SRCLR=1 SER=1 SRCLK=0 RCLK=0 /OE=0
STATE#1: SHIFT=00000000
  OUT#1: QA..H=00000000 QH'=0 LED=11111111/1

<S[E]R [S]RCLK [R]CLK /[O]E /SR[C]LR [T]ied:SRCLK&RCLK [+]> [HELP]

[E]: Toggle the SER pin.
[S]: Pulse the SRCLK pin.
[R]: Pulse the RCLK pin.
[O]: Toggle the /OE pin.
[C]: Toggle the /SRCLR pin.
[T]: Pulse (as tied) both pins SRCLK and RCLK.
[+]: Daisy chain another shift register.

   IN#1: /SRCLR=1 SER=1 SRCLK=0 RCLK=0 /OE=0
STATE#1: SHIFT=00000000
  OUT#1: QA..H=00000000 QH'=0 LED=11111111/1

<S[E]R [S]RCLK [R]CLK /[O]E /SR[C]LR [T]ied:SRCLK&RCLK [+]> [+] IC

   IN#1: /SRCLR=1 SER=1 SRCLK=0 RCLK=0 /OE=0
STATE#1: SHIFT=00000000
  OUT#1: QA..H=00000000 QH'=0 LED=11111111/1
   IN#2: /SRCLR=1 SER=0 SRCLK=0 RCLK=0 /OE=0
STATE#2: SHIFT=00000000
  OUT#2: QA..H=00000000 QH'=0 LED=11111111/1

<S[E]R [S]RCLK [R]CLK /[O]E /SR[C]LR [T]ied:SRCLK&RCLK [+]>

The full code of the sketch, 74HC595_Simulator.ino, follows.  No libraries are required.  Just copy into your Arduino IDE and go, or better still use an Arduino Makefile.

/*
 * $Id: 74HC595_Simulator.ino,v 1.4 2016/06/07 17:37:28 ewan Exp $

   Simulation and simultaneous drive of 74HC595 shift register integrated
   circuit.

   Ewan Parker 27th May 2016.

   To attach and test a *real* SN74HC595 shift register while running the
   simulation, please wire as follows (Uno R3 shown).

   Vcc ------------------------------> Vcc    IC 16
                                       GND    IC 8 ---------------------> GND
   Vcc --> LED#1 Anode --> 560 Ohm --> QA     IC 15
   Vcc --> LED#2 Anode --> 560 Ohm --> QB     IC 1
   Vcc --> LED#3 Anode --> 560 Ohm --> QC     IC 2
   Vcc --> LED#4 Anode --> 560 Ohm --> QD     IC 3
   Vcc --> LED#5 Anode --> 560 Ohm --> QE     IC 4
   Vcc --> LED#6 Anode --> 560 Ohm --> QF     IC 5
   Vcc --> LED#7 Anode --> 560 Ohm --> QG     IC 6
   Vcc --> LED#8 Anode --> 560 Ohm --> QH     IC 7
   Vcc --> LED#9 Anode --> 560 Ohm --> QH'    IC 9
   Vcc -->                  Uno 10 --> /SRCLR IC 10
                            Uno 12 --> SER    IC 14
                            Uno 13 --> SRCLK  IC 11
                            Uno  9 --> RCLK   IC 12
                            Uno 11 --> /OE    IC 13

   If you wish to add a further real '595 chip then wire it as above but connect
   the previous chip's QH' output to the new chip's SER input.
*/

class IC595 {
  public:
    // Set the IC identifier and the Arduino pins.
    IC595(
      byte id, byte pinCLR, byte pinSER, byte pinOE, byte pinSRCLK,
      byte pinRCLK
    );

    void flipCLR();  // Toggle the shift register clear value (active low).
    void setCLR(byte);// Set the shift register clear value (active low).
    void flipSER();  // Toggle the current serial data input value.
    void setSER(byte);// Set the current serial data input value.
    void flipOE();   // Toggle the current output enable value (active low).
    void setOE(byte);// Set the current output enable value (active low).
    void setSRCLK(); // Pulse SRCLK high.
    void setRCLK();  // Pulse RCLK high.
    void setCLK();   // Pulse both SRCLK and RCLK high.
    void dumpState();// Dump the state of the switch and outputs to the console.
    void addChain(); // Daisy chain another shift register IC to this one.
  private:
    uint8_t _shift, _storage, _overflow;
    byte _id;
    byte _CLR, _SER, _OE, _SRCLK, _RCLK;
    byte _pinCLR, _pinSER, _pinOE, _pinSRCLK, _pinRCLK;
    IC595 *_nextIC = NULL;
    void _setPins();
};

void IC595::_setPins() { // Set the IC's pins to match the simulator's state.
  // Only do this for the first shift register.  The chained registers will
  // follow automatically.
  if (_id == 1) {
    digitalWrite(_pinOE, _OE);
    digitalWrite(_pinCLR, _CLR);
    digitalWrite(_pinSER, _SER);
    digitalWrite(_pinRCLK, _RCLK);
    digitalWrite(_pinSRCLK, _SRCLK);
  }
}

IC595::IC595(
  byte id, byte pinCLR, byte pinSER, byte pinOE, byte pinSRCLK, byte pinRCLK) {
  // Save the designation of the uC's pins and set up the IC.
  _id = id;
  _pinCLR = pinCLR; _pinSER = pinSER; _pinOE = pinOE; _pinSRCLK = pinSRCLK;
  _pinRCLK = pinRCLK;
  pinMode(_pinCLR, OUTPUT); pinMode(_pinSER, OUTPUT); pinMode(_pinOE, OUTPUT);
  pinMode(_pinSRCLK, OUTPUT); pinMode(_pinRCLK, OUTPUT);

  if (_id == 1) {
    _CLR = HIGH; _SER = HIGH; _OE = LOW;
  }
  _SRCLK = LOW; _RCLK = LOW; _setPins();
  // Clear random data from shift and storage registers.
  flipCLR(); flipCLR(); setRCLK();
}

void IC595::flipCLR() { // Toggle the state of the CLR pin.
  _CLR = 1 - _CLR;
  if (_nextIC != NULL) _nextIC->setCLR(_CLR);
  if (_CLR == LOW) {
    _shift = 0; _overflow = 0;
    if (_nextIC != NULL) _nextIC->setSER(_overflow);
  }
  _setPins();
}

void IC595::setCLR(byte state) { // Set the state of the CLR pin.
  _CLR = state;
  if (_CLR == LOW) {
    _shift = 0; _overflow = 0;
    if (_nextIC != NULL) _nextIC->setSER(_overflow);
  }
}

void IC595::flipSER() { // Toggle the state of the SER pin.
  _SER = 1 - _SER;
  _setPins();
}

void IC595::setSER(byte state) { // Set the state of the SER pin.
  _SER = state;
  _setPins();
}

void IC595::setSRCLK() { // Pulse the state of the SRCLK pin high.
  if (_nextIC != NULL) _nextIC->setSRCLK();
  _SRCLK = HIGH;
  _setPins();
  if (_CLR == HIGH) {
    _shift = (_shift << 1) | _SER;
    _overflow = (_shift & 128) >> 7;
    if (_nextIC != NULL) _nextIC->setSER(_overflow);
  }
  _SRCLK = LOW;
  _setPins();
}

void IC595::setRCLK() { // Pulse the state of the RCLK pin high.
  if (_nextIC != NULL) _nextIC->setRCLK();
  _RCLK = HIGH;
  _setPins();
  _storage = _shift;
  _RCLK = LOW;
  _setPins();
}

void IC595::setCLK() { // Pulse the state of the RCLK and SRCLK pins high.
  setRCLK();
  setSRCLK();
}

void IC595::flipOE() { // Toggle the state of the /OE pin.
  _OE = 1 - _OE;
  if (_nextIC != NULL) _nextIC->setOE(_OE);
  _setPins();
}

void IC595::setOE(byte state) { // Set the state of the OE pin.
  _OE = state;
  if (_nextIC != NULL) _nextIC->setOE(_OE);
}

void IC595::dumpState() { // Dump the current IC state to the console.
  Serial.print("   IN#"); Serial.print(_id); Serial.print(":");
  Serial.print(" /SRCLR="); Serial.print(_CLR);
  Serial.print(" SER="); Serial.print(_SER);
  Serial.print(" SRCLK="); Serial.print(_SRCLK);
  Serial.print(" RCLK="); Serial.print(_RCLK);
  Serial.print(" /OE="); Serial.print(_OE);
  Serial.println();

  Serial.print("STATE#"); Serial.print(_id); Serial.print(":");
  Serial.print(" SHIFT=");
  for (int i = 0; i < 8; i++) Serial.print((_shift >> i) & 1);
  Serial.println();

  Serial.print("  OUT#"); Serial.print(_id); Serial.print(":");
  Serial.print(" QA..H=");
  for (int i = 0; i < 8; i++) Serial.print((_storage >> i) & 1);
  Serial.print(" QH'="); Serial.print(_overflow);
  Serial.print(" LED=");
  if (_OE == LOW)
    for (int i = 0; i < 8; i++) Serial.print(1 - ((_storage >> i) & 1));
  else Serial.print("--------");
  Serial.print("/");
  Serial.print(1 - _overflow);
  Serial.println();

  // Show any chained shift register states.
  if (_nextIC != NULL) _nextIC->dumpState();
}

void IC595::addChain() { // Chain another shift register IC.
  // Clear random data from shift and storage registers.
  flipCLR(); flipCLR(); setRCLK();

  // Daisy chain the new IC to this one, if we can.
  if (_nextIC == NULL) {
    _nextIC = new IC595(_id + 1, _pinCLR, 0, _pinOE, _pinSRCLK, _pinRCLK);
    _nextIC->setSER(_overflow);
    _nextIC->setCLR(_CLR);
    _nextIC->setOE(_OE);
  }

  // Daisy chain the new IC to the end of the chain.
  else _nextIC->addChain();
}

// Create a single instance of the '595 IC.
IC595 IC(1, 10, 12, 11, 13, 9);

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

  Serial.println(); Serial.println();
  Serial.println("Simulation and simultaneous drive of 74HC595 shift register.");
  Serial.println("Ewan Parker 27th May 2016.");
  Serial.println("$Id: 74HC595_Simulator.ino,v 1.4 2016/06/07 17:37:28 ewan Exp $");
  Serial.println();

  Serial.println("* If /OE is HIGH then outputs QA..QH are disabled.");
  Serial.println("* If /OE is LOW then outputs QA..QH are enabled.");
  Serial.println("* If /SRCCLR is LOW then the shift register is cleared.");
  Serial.println("* If SRCLK goes HIGH and /SRCLR is HIGH then the first stage of the shift");
  Serial.println("  register is set to the value of SER and other stages store the data of the");
  Serial.println("  previous stage, respectively.");
  Serial.println("* If RCLK goes HIGH then the shift register data is stored in the storage");
  Serial.println("  register.");
  Serial.println();

  Serial.println("There must be a delay of up to 19 ns after SRCLK goes HIGH and before RCLK goes");
  Serial.println("HIGH.  This ensures the storage register receives stable data from the shift");
  Serial.println("register.  If the clocks are tied together then the shift register is one pulse");
  Serial.println("ahead of the storage register.");

  Serial.println();
  // Show the initial state of the simulator.
  IC.dumpState();
}

void loop() {
  Serial.println();
  // Show options.
  Serial.print("<S[E]R [S]RCLK [R]CLK /[O]E /SR[C]LR [T]ied:SRCLK&RCLK [+]> ");
  while (! Serial.available()) {}
  // Wait for a character to be typed, then process the required action.
  char in = Serial.read(); if (in >= 'a' && in <= 'z') in += ('A' - 'a');
  switch (in) {
    case 'E' : Serial.println("Toggle S[E]R"); IC.flipSER(); break;
    case 'S' : Serial.println("Pulse [S]RCLK"); IC.setSRCLK(); break;
    case 'R' : Serial.println("Pulse [R]CLK"); IC.setRCLK(); break;
    case 'O' : Serial.println("Toggle /[O]E"); IC.flipOE(); break;
    case 'C' : Serial.println("Toggle [C]LR"); IC.flipCLR(); break;
    case 'T' : Serial.println("Pulse [T]ied CLKs"); IC.setCLK(); break;
    case '+' : Serial.println("[+] IC"); IC.addChain(); break;
    default  : Serial.println("[HELP]");
               Serial.println();
               Serial.println("[E]: Toggle the SER pin.");
               Serial.println("[S]: Pulse the SRCLK pin.");
               Serial.println("[R]: Pulse the RCLK pin.");
               Serial.println("[O]: Toggle the /OE pin.");
               Serial.println("[C]: Toggle the /SRCLR pin.");
               Serial.println("[T]: Pulse (as tied) both pins SRCLK and RCLK.");
               Serial.println("[+]: Daisy chain another shift register.");
               break;
  }
  Serial.println();
  // Show the new state of the simulator.
  IC.dumpState();
}

You should not sink more than 7mA per Q pin to avoid exceeding the maximum current for the '595.  This limitation could be overcome by the use of (say) a 2N3904 transistor with its base connected to the Q pin via a 2k resistor, its collector connected to the LED's cathode via a 150k resistor, and its emitter connected to ground.  Now, by sourcing current instead of sinking, you can turn the LED on when the Q pin is HIGH and off otherwise.  With the existing circuit the LEDs show the reverse state of the Q pins at all times.

Classifications