SPI Daisy chained shift register

Hi,

I cannot figure out how to use SPI with 2 shift registers daisy chained together. Wiring them up is not the problem.

The setup:

I want to individually control 16 leds connected connected to 2 shift registers.

I understand how to transfer 8 byte to one shift register, but I just cannot understand how to do it with 2. I have read so many threads but I don't understand the answers that were given. A lot of tutorials were about making patterns with the outputs, but I just want to control individual outputs.

I understand that you can send 2 bytes like this:

byte shiftregister1
byte shiftregister2

digitalWrite(LATCH,LOW);

SPI.transfer(shiftregister1);
SPI.transfer(shiftregister2);
digitalWrite(LATCH,HIGH);

But how for example can I use a for loop to select 1 of the 16 bits and switch them?

That code will do it.
You always transfer all 16 bits every time. To change just one output you just change the single bit in the variables you send out with all the other bits the same as last time. You can use the bit set and bit clear calls if you want although the real way to do it is with AND & OR bit operations.

unsigned int shiftregister;

  for (byte i = 0; i < 16; i++) {
    bitWrite(shiftregister, i, random(1));
  }

  digitalWrite(LATCH,LOW); 
  SPI.transfer(highByte(shiftregister));
  SPI.transfer(lowByte(shiftregister));      
  digitalWrite(LATCH,HIGH);

Grumpy_Mike:
That code will do it.
You always transfer all 16 bits every time. To change just one output you just change the single bit in the variables you send out with all the other bits the same as last time. You can use the bit set and bit clear calls if you want although the real way to do it is with AND & OR bit operations.

PaulRB:

unsigned int shiftregister;

for (byte i = 0; i < 16; i++) {
    bitWrite(shiftregister, i, random(1));
  }

digitalWrite(LATCH,LOW);
  SPI.transfer(highByte(shiftregister));
  SPI.transfer(lowByte(shiftregister));     
  digitalWrite(LATCH,HIGH);

Aha that was easy. So you can use the same commands as if you were using the shiftout command. Must have missed that part. Thanks for the quick help.

Using the AND & OR bit operations will take some extra studying to get that working. Is there any benefit in using that over Bitwrite/Bitset/Bitclear?

So you can use the same commands as if you were using the shiftout command. Must have missed that part.

Not sure what you mean. None of the functions I used are specific to shiftOut(), they are all general purpose. Where did you read about using certain functions only with shiftOut()?

Using the AND & OR bit operations will take some extra studying to get that working. Is there any benefit in using that over Bitwrite/Bitset/Bitclear

Maybe a small performance benefit to using AND, OR etc. More flexible when manipulating groups of bits together. Also they are standard C operators rather than functions specific to the Arduino language.

PaulRB:
Not sure what you mean. None of the functions I used are specific to shiftOut(), they are all general purpose. Where did you read about using certain functions only with shiftOut()?
Maybe a small performance benefit to using AND, OR etc. More flexible when manipulating groups of bits together. Also they are standard C operators rather than functions specific to the Arduino language.

I meaned that if I would use shiftOut() instead of SPI to control my shift register I would also be using Bitwrite to control individual bits.

I meaned that if I would use shiftOut() instead of SPI to control my shift register I would also be using Bitwrite to control individual bits.

No.

You can use shift out or SPI with either of these commands.

Using the AND & OR bit operations will take some extra studying to get that working. Is there any benefit in using that over Bitwrite/Bitset/Bitclear?

As Paul said groups are easily manipulated also you get to play with XOR which is a selective invert function, great for flashing.

And keep in mind you need to initialize SPI before using it :slight_smile:

Grumpy_Mike:
No.

You can use shift out or SPI with either of these commands.
As Paul said groups are easily manipulated also you get to play with XOR which is a selective invert function, great for flashing.

I think we are meaning the same thing but it's my poor choice of words that is causing the confusion. Ok thanks I will look into XOR.

septillion:
And keep in mind you need to initialize SPI before using it :slight_smile:

Yes :slight_smile:

#include <SPI.h>

int latchpin = 10;

void setup() {

SPI.begin();
}

void loop() {
unsigned int shiftregister;
for (byte i = 0; i < 12; i++) //I currently have 12 leds connected.

{
bitWrite(shiftregister, i, 1);

digitalWrite(latchpin,LOW);
SPI.transfer(highByte(shiftregister));
SPI.transfer(lowByte(shiftregister));
digitalWrite(latchpin,HIGH);

delay(500);
}

That works just fine.

However let's say I do not want to daisy chain the registers but control both shift registers individually.

  • I connect MOSI (pin 11) to both of the shift register Data In
  • I connect SCK (pin 13) to Clock on both shift registers
  • I connect the shift registers Latch port separately to their own SS (Pin 8 and 9)

#include <SPI.h>

int latchpin = 9;
int latchpin2 = 8;

void setup() {

SPI.begin();
}

void loop() {
unsigned int shiftregister;
unsigned int shiftregister2;
for (byte i = 0; i < 8; i++) //8 leds connected
{
bitWrite(shiftregister, i, 1);
digitalWrite(latchpin,LOW);
SPI.transfer(shiftregister);
digitalWrite(latchpin,HIGH);

delay(500);
}

for (byte i = 0; i < 4; i++) // 4 leds connected
{
bitWrite(shiftregister2, i, 1);
digitalWrite(latchpin2,LOW);
SPI.transfer(shiftregister2);
digitalWrite(latchpin2,HIGH);

delay(500);
}

}

However I expect them to run separately but both of the register are getting the same input at the same time. So both the shift registers leds light up at the same time. What am I missing?

What am I missing?

  • I connect SCK (pin 13) to Clock on both shift registers

No, that will load both shift registers.

"However I expect them to run separately but both of the register are getting the same input at the same time. So both the shift registers leds light up at the same time. What am I missing?"
Assuming 74HC595/TPIC6B595 shift registers, the outputs do not change until there is a rising edge on the RCLK pin (latchPin in your code).
Clock in all the data you want, without the latch signal it doesn't matter. It's the same as having a unique slave select pin with multiple SPI slaves connected in parallel.

Grumpy_Mike:
No, that will load both shift registers.

CrossRoads:
"However I expect them to run separately but both of the register are getting the same input at the same time. So both the shift registers leds light up at the same time. What am I missing?"
Assuming 74HC595/TPIC6B595 shift registers, the outputs do not change until there is a rising edge on the RCLK pin (latchPin in your code).
Clock in all the data you want, without the latch signal it doesn't matter. It's the same as having a unique slave select pin with multiple SPI slaves connected in parallel.

So you guys are saying that it isn't possible without daisy chaining them together? I thought that if I just would hold the lach pin high the data wouldn't be copied into the storage register.

I'm using the TLC5917 8-Channel Constant-Current LED Sink Drivers. But it's the same principle as the 74HC595.

Datasheet

So you guys are saying that it isn't possible without daisy chaining them together?

No, we are saying you have to wire it up and drive it correctly.

No you can do it the way you describe. Your code should work, so I suspect a wiring error.

That said, why do you believe you want to do this instead of chaining? The cost is an Arduino pin, what do you see as the benefit?

You are choosing words badly again, as Mike says, both shift registers will be getting the same input, but as Crossroads says, that does not matter because that data only gets loaded to the output register when that register's latch line goes high.

Please use code tags for code, not quotation tags. It's the </> icon.

Two methods:
Wiring in parallel - SCK to all devices, MOSI to all devices, and unique latch to all devices. 6 wires, I've sent data to 4 shift registers (actually four MAX7219) this way. More code to run, but can access an individual shift register if really want to.

digitalWrite (ssPin1, LOW);
SPI.transfer(data1); // however the data is created - discrete variables, highByte/lowByte of an int, array[0]
digitalWrite (ssPin1, HIGH; // outputs update on this rising edge
digitalWrite (ssPin2, LOW);
SPI.transfer(data2); // however the data is created - discrete variables, highByte/lowByte of an int, array[0]
digitalWrite (ssPin2, HIGH; // outputs update on this rising edge
digitalWrite (ssPin3, LOW);
SPI.transfer(data3); // however the data is created - discrete variables, highByte/lowByte of an int, array[0]
digitalWrite (ssPin3, HIGH; // outputs update on this rising edge
digitalWrite (ssPin4, LOW);
SPI.transfer(data4); // however the data is created - discrete variables, highByte/lowByte of an int, array[0]
digitalWrite (ssPin4, HIGH; // outputs update on this rising edge
// can arrange this using a couple of arrays too; one for data, one for latchPin assignments, and use a for:loop to go thru them, the for loop will be slower to execute.

Wiring in parallel - SCK to all devices, latch to all devices, MOSI to device one, data out to data in of the next device. I've sent data to 45 shift registers like this at 8 Mz clock rate (SPI clock divisor at 2, vs default of 4), refreshing all outputs at 20 KHz rate.
Faster method, just 3 IO pins needed.

digitalWrite (ssPin, LOW);
SPI.transfer(data0); // however the data is created - discrete variables, highByte/lowByte of an int, array[0]
SPI.transfer(data1); // however the data is created - discrete variables, highByte/lowByte of an int, array[1]
SPI.transfer(data2); // however the data is created - discrete variables, highByte/lowByte of an int, array[2]
SPI.transfer(data3); // however the data is created - discrete variables, highByte/lowByte of an int, array[3]
// can do the SPI.transfers in a for:loop also, but is slower
digitalWrite (ssPin, HIGH; // all shift register (4 or 45) outputs update on this rising edge

Do you have 0.1uF cap from Vcc of each device to Gnd, located near the Vcc pin?
MSCLR to +5, and OE/ to Gnd (or a PWM pin) are required as well.

PaulRB:
No you can do it the way you describe. Your code should work, so I suspect a wiring error.

That said, why do you believe you want to do this instead of chaining? The cost is an Arduino pin, what do you see as the benefit?

You are choosing words badly again, as Mike says, both shift registers will be getting the same input, but as Crossroads says, that does not matter because that data only gets loaded to the output register when that register's latch line goes high.

Please use code tags for code, not quotation tags. It's the </> icon.

The reason I want to try this is because I want to learn. I want to learn what the pro's and con's are. Most tutorials explain wiring up only one shift register. Almost none of the tutorials use SPI. There are also tutorials that are just to complex for me to understand (yet). That's why I want to try these things myself. If I just keep trying long enough then eventually I will get it to work.

Grumpy_Mike:
No, we are saying you have to wire it up and drive it correctly.

CrossRoads:
Two methods:
Wiring in parallel - SCK to all devices, MOSI to all devices, and unique latch to all devices. 6 wires, I've sent data to 4 shift registers (actually four MAX7219) this way. More code to run, but can access an individual shift register if really want to.

I cannot get it to work in this way.

I rewired my setup and attached a hand drawn schematic. The Output enable pins and the external resistor are both connected to ground.

CrossRoads:
Do you have 0.1uF cap from Vcc of each device to Gnd, located near the Vcc pin?
MSCLR to +5, and OE/ to Gnd (or a PWM pin) are required as well.

No I did not attach 0.1uF caps. I can get the tomorrow or the day after. The Output enable pins and the external resistor are both connected to ground. I don't understand what you mean by 'MSCLR to +5'.

The SRCLR pin (10 on 74HC595) can't be left unconnected (i.e. 'floating'). Connect it to +5V.
Same with OE (pin 13 on 74HC595), it can't be left unconnected. Connect it to Gnd.
The 0.1uF caps are needed for high speed operation, be sure to add them.

Here's an example of two shift registers with unique latchPin signals.

Not sure what you mean by "external resistor"?

"Almost none of the tutorials use SPI." Or the libraries. A great waste of a great hardware resource in my opinion.

CrossRoads:
The SRCLR pin (10 on 74HC595) can't be left unconnected (i.e. 'floating'). Connect it to +5V.
Same with OE (pin 13 on 74HC595), it can't be left unconnected. Connect it to Gnd.
The 0.1uF caps are needed for high speed operation, be sure to add them.

Here's an example of two shift registers with unique latchPin signals.

Not sure what you mean by "external resistor"?

I think you must have just missed it. It probably happened because I have to wait 5 minutes to edit a message.

I'm using the TLC5917 8-Channel Constant-Current LED Sink Drivers. But it's the same principle as the 74HC595.

Datasheet

CrossRoads:
"Almost none of the tutorials use SPI." Or the libraries. A great waste of a great hardware resource in my opinion.

Well actually I learned it from you that I could use SPI. You mentioned that in another thread. So I tried that instead of shiftout.