Determining the Number of Shift Registers Connected to Arduino

Hello!

I have a project that involves connecting multiple board modules. Each module has a shift register (74HC595) on it, and the user can connect as many together as they please. The shift register drives a display device. I am currently designing these board modules, and am looking for a way to recognize how many modules/shift-registers the user has attached. That will in effect influence the operations of the Arduino.

Some additional information:
The shift registers (and their parent module boards) are daisy chained. Clock to clock, latch to latch, and the data-lines feed into Q8 pins, except for the first shift register, which connects to the Arduino MEGA 2560.

I have been recommended using I2C chips, but I am wondering if someone could explain a possible solution using just shift registers and the Arduino.

Thanks!

The first thing that comes to mind is jumpers like those used to set hard drives.

Assuming your daisy chaining them together then maybe have a connection from the last shift registers QH' pin back to an arduino input pin.
In setup you clock out enough zeros to cover max possible chain length and then clock out a 1 bit and count how many clocks it takes for the 1 to show up on the QH' input.

Riva:
Assuming your daisy chaining them together then maybe have a connection from the last shift registers QH' pin back to an Arduino input pin.

Sounds cool, but this involves some way to loop that connection back to the processor board.

If you are going to do that, you might as well just set the DIP switches - or tell the software.

The original question needs some clarification - why does it matter how many boards are chained?

Why not just use a few digital lines passing through the boards and use those and forget the shift registers - each wire having a pull up - three boards connected pulls up three digitals . You maybe able to multiplex and get a more neat solution.

the why in short: making a tube calculator that adapts rounding of output based on number of display modules attached

you're right though, getting the loop back to the microcontroller will be difficult with my set-up, though I may be able to get it to work with some finagling.

there might be up to eight modules on either side, so having eight wires ready seems kind of troublesome

Riva:
Assuming your daisy chaining them together then maybe have a connection from the last shift registers QH’ pin back to an arduino input pin.
In setup you clock out enough zeros to cover max possible chain length and then clock out a 1 bit and count how many clocks it takes for the 1 to show up on the QH’ input.

I have a set-up here with 74HC595s on a bread-board connected to an UNO; I am trying to get what you are suggesting to work. I am fairly new with shift-registers, so be patient with me! If I understand correctly, I would digitalWrite(clockPin, LOW) at least as many times as needed for the number of modules, then write in a digitalWrite(clockPin, HIGH), and count the number of clock pulses? How would I count the number of clock pulses using Arduino code?

If you can bring the output of the last shift register back to the Ardiuno, then you can use SPI to write & read at the same time, as MISO is read in while MOSI goes out.

So you can do something like this:

digitalWrite (ssPin, LOW);
misoIn = SPI.tranfer(0xAA);
digitalWrite (ssPin, HIGH);

if (misoIn == 0xAA) then you know the data has gone thru all of them and made it back.
Wrap the transfer in some counter code so you know how many SPI,transfers if took,

csapidus:
you’re right though, getting the loop back to the microcontroller will be difficult with my set-up, though I may be able to get it to work with some finagling.

How are the modules connected to each other. If you had a connector like a 3.5mm jack socket (plus a few other pins) where inserting a plug breaks a connection this would allow daisy chaining without user intervention because the break point would be the QH’ return.
If your using normal PCB edge connectors (like in #1) then a jumper would do the trick on the last module. A user would need to remove the jumper to join another module on the end but the last module would just keep the jumper on.

If you use PCF8574 instead or even PCF8575, you could give each a different address on the eight boards, use the 2 pin I2C bus and the I2C library will tell you which are and are not connected (but not in which order).

Thank you all for the recommendations. Hardware wise, I figured out a way that follows recommendation 2 (essentially a jumper) mentioned in #9. I essentially have termination blocks that need to be put at the end of however many models are attached, in order to feed that info back to the main board/uController.

I am struggling with the code however. This is where I am at. Currently, I am simply getting a reading of zero from input pin readPin. Note, readPin is connected to Q7'

//**************************************************************//
// TESTING SHIFT REGISTER CHAINING
//****************************************************************

int latchPin = 8;
int clockPin = 12;
int dataPin = 11;

int readPin = 2;
int readIT = 0;

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

 //set pins to output so you can control the shift register
 pinMode(latchPin, OUTPUT);
 pinMode(clockPin, OUTPUT);
 pinMode(dataPin, OUTPUT);
 
 pinMode(readPin, INPUT);
}

void loop() {
 
 readPin = digitalRead(reading);
 Serial.print("reading: ");
 Serial.println(readPin);

 digitalWrite(latchPin, LOW);
 shiftOut(dataPin, clockPin, MSBFIRST, 25); 
 digitalWrite(latchPin, HIGH);

 readPin = digitalRead(reading);
 Serial.print("reading: ");
 Serial.println(readPin);

 digitalWrite(latchPin, LOW);
 shiftOut(dataPin, clockPin, MSBFIRST, 50); 
 digitalWrite(latchPin, HIGH);

 readPin = digitalRead(reading);
 Serial.print("reading: ");
 Serial.println(readPin);
}

CrossRoads:
If you can bring the output of the last shift register back to the Ardiuno, then you can use SPI to write & read at the same time, as MISO is read in while MOSI goes out.

So you can do something like this:

digitalWrite (ssPin, LOW);
misoIn = SPI.tranfer(0xAA);
digitalWrite (ssPin, HIGH);

if (misoIn == 0xAA) then you know the data has gone thru all of them and made it back.
Wrap the transfer in some counter code so you know how many SPI,transfers if took,

@CrossRoads, thanks for the suggestion! I actually just managed to get the set-up you recommended using SPI.transfer working. I am currently using those SPI pins elsewhere, but will try to configure a two-slave set-up

csapidus:
I am struggling with the code however. This is where I am at. Currently, I am simply getting a reading of zero from input pin readPin. Note, readPin is connected to Q7'

Can you verify that the output data is getting latched into the individual registers?

dougp:
Can you verify that the output data is getting latched into the individual registers?

Actually, the elegance of this is that while determining the number of shift registers, you do not latch any data from the registers, so the outputs - apart from the carry - are not altered.

You would start by shifting out the initialisation - presumably zeros - to more then the anticipated number of registers and latching that to the outputs, then shift 0x01 or 0xff through until it is detected at the final carry output, but without latching. After that, always shift out as many bytes as there are registers (unless it is appropriate to ripple the displays).

Paul__B:
then shift 0x01 or 0xff through until it is detected at the final carry output, but without latching.

This is what I was, clumsily, driving at. I can’t see how the posted code shifts a value which would turn on the second stage QH with two bytes sent.

Probably missing something... but how are you going to guarantee that the last shift register, of a variable daisy chain, is linked back? Say you have connectors for up to four shift registers. Normally they daisy chain to one another - so if you have four of them, the fourth sends the data back to the Arduino.

But now if you connect only the first three. Then with the last register missing, the connection to the Arduino is also missing. Or if you connect only the last three, the first is not there, and there's nothing reading the data to begin with so the second to fourth never get any data.

The code previously posted did not use the SPI.Transfer method. I am trying to get an alternative method working without SPI.Transfer, as it complicates the design of my circuit by requiring SPI, when I have an ancillary keypad already using SPI for data transfer.

I will provide the functioning SPI.Transfer code later today, along with posting the Serial outputs

SPI is a bus and can easily be shared between multiple devices. That's what it's designed to do. Just make sure you have a separate CS line for each device.

However to share, the devices must disable MISO when not selected, which the 74HC595 will not do.

Paul__B:
However to share, the devices must disable MISO when not selected, which the 74HC595 will not do.

Ah, that will make things quite difficult. The keypad with display ancillary device requires SPI, so I can’t get around that. I would be interested in hearing if software SPI would be of use. Does hardware SPI and software SPI share the same effective SPI bus?

Secondly, an interrupt is required for recognizing when a new shift register is added, but I am not sure how best to realize this with attachInterrupt(x, y, z).

Here is the functional code using SPI Transfer:

//**************************************************************//
// TESTING SHIFT REGISTER CHAINING
//****************************************************************

// Serial output for various parameters, where SR = number of chained shift registers
// 187, 85, 170 0xAA 1SR

// 187, 255, 204 0xCC 1SR
//  187, 255, 255, 204, 204 0xCC 2SR

#include <SPI.h>
uint16_t SPI_message = 0xCC;

int latchPin = 8;
int blankPin = 9; // connected to OE
int dataPin = 11; // MOSI
int readPin = 12; // MISO
int clockPin = 13; // SCK

int count = 0;

uint16_t SPI_returned = 0xBB;

void setup() {
  Serial.begin(9600);
  
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV2); //Run the data in at 16MHz/2 - 8MHz

  pinMode(latchPin, OUTPUT);
  pinMode(blankPin, OUTPUT);
  // pinMode(readPin, INPUT); // Not necessary 
  
  digitalWrite(blankPin, HIGH);
  digitalWrite(latchPin, LOW);

  Serial.print("COUNT: ");
  Serial.println(count);
  Serial.print("READING: ");
  Serial.println(SPI_returned);
}

void loop() {  
  digitalWrite(blankPin, HIGH);
  SPI_returned = SPI.transfer(SPI_message);
  digitalWrite(latchPin, HIGH);
  digitalWrite(latchPin, LOW);
  digitalWrite(blankPin, LOW);

  if (SPI_returned == 255) {
      count++;
  }
      
  Serial.println("COUNT: ");
  Serial.println(count);
  Serial.print("READING: ");
  Serial.println(SPI_returned);
  
  delay(1000);
}