2X TPIC6B595DWRG4 drive relay

I add 2nd relay driver TPIC6B595DWRG4 in my project and I want to drive the relay on 2nd chip with my existing sketch, but I don’t have much knowledge about shift register.

I need help to understand how this shift register works and update the sketch.

Below Sketch working fine for 08 relays only.


int latchPin = 10;      // (11) ST_CP [RCK] on 74HC595
int clockPin = 9;      // (9) SH_CP [SCK] on 74HC595
int dataPin = 12;     // (12) DS [S1] on 74HC595

const int OE        = 11;

byte leds = 0;
void updateShiftRegister()
{
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, leds);
  digitalWrite(latchPin, HIGH);
}


void setup()
{

  pinMode(OE, OUTPUT);
  digitalWrite(OE, LOW);

  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  Serial.begin(9600);
}


void loop()
{
  updateShiftRegister();   bitSet(leds, 0);
  delay(1000);
  updateShiftRegister();   bitClear(leds, 0);
  delay(1000);


    updateShiftRegister();   bitSet(leds, 1);
  delay(1000);
  updateShiftRegister();   bitClear(leds, 1);
  delay(1000);


    updateShiftRegister();   bitSet(leds, 2);
  delay(1000);
  updateShiftRegister();   bitClear(leds, 2);
  delay(1000);


    updateShiftRegister();   bitSet(leds, 3);
  delay(1000);
  updateShiftRegister();   bitClear(leds, 3);
  delay(1000);

      updateShiftRegister();   bitSet(leds, 4);
  delay(1000);
  updateShiftRegister();   bitClear(leds, 4);
  delay(1000);

      updateShiftRegister();   bitSet(leds, 5);
  delay(1000);
  updateShiftRegister();   bitClear(leds, 5);
  delay(1000);

      updateShiftRegister();   bitSet(leds, 6);
  delay(1000);
  updateShiftRegister();   bitClear(leds, 6);
  delay(1000);

      updateShiftRegister();   bitSet(leds, 7);
  delay(1000);
  updateShiftRegister();   bitClear(leds, 7);
  delay(1000);

}

After searching and update, I am able to drive all 16 relays but still not understand how its work. specifically shift in / out on second chip.


int latchPin = 10;      // (11) ST_CP [RCK] on 74HC595
int clockPin = 9;      // (9) SH_CP [SCK] on 74HC595
int dataPin = 12;     // (12) DS [S1] on 74HC595

const int OE        = 11;

byte bitsToSend0 = 0;
byte bitsToSend1 = 0;
void updateShiftRegister()
{
  digitalWrite(latchPin, LOW);
    // shift the bits out for all three registers. Note the opposite order.
  shiftOut(dataPin, clockPin, LSBFIRST, bitsToSend0);
  shiftOut(dataPin, clockPin, LSBFIRST, bitsToSend1);


  digitalWrite(latchPin, HIGH);
}


void setup()
{

  pinMode(OE, OUTPUT);
  digitalWrite(OE, LOW);

  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  Serial.begin(9600);
}


void loop()
{
  updateShiftRegister();   bitSet(bitsToSend1, 0);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend1, 0);
  delay(500);

    updateShiftRegister();   bitSet(bitsToSend1, 1);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend1, 1);
  delay(500);


    updateShiftRegister();   bitSet(bitsToSend1, 2);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend1, 2);
  delay(500);

    updateShiftRegister();   bitSet(bitsToSend1, 3);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend1, 3);
  delay(500);

      updateShiftRegister();   bitSet(bitsToSend1, 4);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend1, 4);
  delay(500);

      updateShiftRegister();   bitSet(bitsToSend1, 5);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend1, 5);
  delay(500);

      updateShiftRegister();   bitSet(bitsToSend1, 6);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend1, 6);
  delay(500);

      updateShiftRegister();   bitSet(bitsToSend1, 7);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend1, 7);
  delay(500);

  updateShiftRegister();   bitSet(bitsToSend0, 0);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend0, 0);
  delay(500);

    updateShiftRegister();   bitSet(bitsToSend0, 1);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend0, 1);
  delay(500);

    updateShiftRegister();   bitSet(bitsToSend0, 2);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend0, 2);
  delay(500);

    updateShiftRegister();   bitSet(bitsToSend0, 3);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend0, 3);
  delay(500);

      updateShiftRegister();   bitSet(bitsToSend0, 4);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend0, 4);
  delay(500);

      updateShiftRegister();   bitSet(bitsToSend0, 5);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend0, 5);
  delay(500);

      updateShiftRegister();   bitSet(bitsToSend0, 6);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend0, 6);
  delay(500);

      updateShiftRegister();   bitSet(bitsToSend0, 7);
  delay(500);
  updateShiftRegister();   bitClear(bitsToSend0, 7);
  delay(500);


}

void registerWrite(int whichPin, int whichState) {
  digitalWrite(latchPin, LOW);
  // Set bit pattern
  bitWrite(bitsToSend1, whichPin, whichState);
  if( whichPin > 7 ){
    bitWrite(bitsToSend0, whichPin-8, whichState);
  }}

a 74595 shift register transfers a logic level thru a series of flip-flops with each clock pulse. a final pulse captures their states to an output latch.

shiftOut() sets an output pin and toggles a clock output to drive a shift registers pins for each bit (8) being transfered

image

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;
}

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
        uint8_t i;

        for (i = 0; i < 8; i++)  {
                if (bitOrder == LSBFIRST) {
                        digitalWrite(dataPin, val & 1);
                        val >>= 1;
                } else {
                        digitalWrite(dataPin, (val & 128) != 0);
                        val <<= 1;
                }

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

A shiftregister basically consists of two sections. The first section (marked in red) receives the serial data and the second section (marked in blue) indirectly drives the output pins.

So you put the first bit on the serial input (SER IN) and generate a clock pulse on SRCK; that will put that bit on the output of the left top D flipflop. You repeat that 8 times for all 8 databits; each time the output of the flipflops in the red column will be moved to the next output and the new bit will be on the output of the left top D flipflop.

Once all 8 bits have been moved in, you can latch the data with a pulese on RCK; that will copy the bits from the flipflops in the left part (red) to the flipflops in the right parts (blue).

And that is basically it.

At the right of the section marked in blue are 8 AND ports. If you make the G pin HIGH the output pins will be LOW, regardless of the data on the outputs of the flipflops in the blue section; if you make that pin LOW, the outputs will follow the data on the the outputs of the flipflops in the blue section.

I forgot to address this

The last flipflop in the red section is connected to the serial input of the second TPIC. The data on that pin is inverted (so it reflects the Q output of that last flipflop) and on the next clock pulse is clocked in the first receive flipflop of the second TPIC. And so on.

Below an example of a typical D flipflop

It has 2 outputs, Q and /Q. If Q is HIGH, /Q is LOW and vice versa.

I think there is some room for improvement.

This is the shiftOut() function that you're using for the Pro Mini. You can find it on your PC or here: https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring_shift.c.

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
  uint8_t i;

  for (i = 0; i < 8; i++)
  {
    if (bitOrder == LSBFIRST)
    {
      digitalWrite(dataPin, val & 1);
      val >>= 1;
    }
    else
    {
      digitalWrite(dataPin, (val & 128) != 0);
      val <<= 1;
    }

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

You can easily modify that to shift 16 bits.

void shiftOut16(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint16_t val)
{
  uint8_t i;

  for (i = 0; i < 16; i++)
  {
    if (bitOrder == LSBFIRST)
    {
      digitalWrite(dataPin, val & 1);
      val >>= 1;
    }
    else
    {
      digitalWrite(dataPin, (val & 128) != 0);
      val <<= 1;
    }

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

There are, except for the name change, two changes. The one is the type of the value that you pass, the second one is the for-loop.

You can copy this function shiftOut16() into your sketch.

Instead of using two variables bitsToSend0 and bitsToSend1 you can create one variable uint16_t bitsToSend; and pass that to the new function.

The below

can then change to

  shiftOut16(dataPin, clockPin, LSBFIRST, bitsToSend);

Simple demo on wokwi: 1414262_shiftOut16_demo. Layout was copied from the wokwi example https://wokwi.com/projects/301213976182653448

Note that it does not follow your code, it's just a demo.

Attached a zip because I will more than likely not keep it on wokwi for months.

1414262_shiftOut16_demo.zip (10.2 KB)