Triple Shift Register question

Hello again everyone!

Quick question-

Is it possible to send info across three shift registers using only one "shiftout"? It's not is it?

I have LEDs currently connected to each output on the 74HC595s for a total of 24 lights, and I need to light only single lights at a time, on command. Currently, if I wanted to light only the 5th LED in the middle register I am doing something like this:

 digitalWrite(latch, 0);
  shiftOut(data, clock, MSBFIRST, 0); 
  shiftOut(data, clock, MSBFIRST, 16);
  shiftOut(data, clock, MSBFIRST, 0);
  digitalWrite(latch, 1);

and I suppose what I am hoping for is a solution more like this:

 digitalWrite(latch, 0);
  shiftOut(data, clock, MSBFIRST, 0, 16, 0);
  digitalWrite(latch, 1);

Would it maybe work if I used an array? So possibly...

byte shiftarray[] = {0, 16, 0};

for (byte i = 0, i < 3, i++) {

  digitalWrite(latch, 0);
  shiftOut(data, clock, MSBFIRST, shiftarray[i]);
  digitalWrite(latch, 1); 

}

Quite honestly, I have only just begun to grasp arrays, so maybe I am way off base. Thanks in advance for any help you are able to provide!

As a side note, a few of you have been helping me tremendously along my journey and I just wanted to let you all know I am making lots of progress with the BarBot! I now have all of my pumps and check valves- and I already have had it take an order over wifi from the iPad, and pour precise amounts of liquid! Granted, it was colored water and not actual booze yet... but it works anyways! I picked up some transistor arrays too (so cool they make these! I was planning on having to individually diode/resistor/transistor each one separately... ) And I picked up a big jug of Ferric Chloride to etch my own PCB when the time comes- that sounds fun! (I'm sure it will take a bit of practice though lol)

I gotta hit the sack for now, but I'll check back in the morning! Thanks Guys!

I think I need a BarBot!

Anyway, the nature of shift registers is that you basically have to update the lot.

It only takes 3 x SPI.transfer to reload three shift registers, and they execute very quickly, so I wouldn't get too worried.

How about making your code into a function like this

void shiftThree(int reg1, int reg2, int reg3)
{
  digitalWrite(latch, 0);
  shiftOut(data, clock, MSBFIRST, 0); 
  shiftOut(data, clock, MSBFIRST, 16);
  shiftOut(data, clock, MSBFIRST, 0);
  digitalWrite(latch, 1);
}

Then you could call it like this

shift3(0,16,0);

BarBot

BarBot!

Just what you need at the SuperBowl party.

If you have the shift registers daisy chained, then you need to update all three at once.
If you give each one its own latch line, you can address each one individually.

I have daisychained 20 of them with SPI transfers and sent the data out to them from an array. Very fast.
I have also used 4 MAX7219 for a scrolling message board, a 32 column panel of data. Each column there requires 2 SPI tranfers, address first and then data. So 64 SPI transfers to update the whole thing. I was updating the entire display every 40mS while scrolling, made for good readability.

So 3 daisy chained is very quick, as Nick pointed out.
shiftout is a low slower, since the software has to create every clock pulse. The ATmega has dedicated hardware for SPI.
Make sure you have a 0.1uF cap from shift register Vcc pin to Gnd.

Thanks for the replies!

NickGammon:

I'll post my build of it with code here once I'm done! That way everyone can have one :slight_smile:

SPI transfer is now a new arrow in my quiver, and as Crossroads said, if it is less work for the software I am all over it, since my code is already so bloated with crap for it to do.

After reading the link you provided though, I have two burning questions:

  1. That 10k pulldown resistor on the "D10"/"SS"/"Slave Select" pin... is that pin 11, 12, or 14? I would LOVE to put that on my circuit- so if the power should go out and come back on, my BarBot won't start arbitrarily pouring booze on the ground!

  2. Where do you buy your ribbon cable good sir? I would very much like to get my hands on some like yours.

UkHelibob:

What you suggested is exactly what I am currently doing actually, and it is very clunky for my purposes. Let me elaborate. My current code is as follows:

void pourcount(const byte shift1, const byte shift2, const byte shift3, byte ozcount)
{
  digitalWrite(latch, 0);
  shiftOut(data, clock,MSBFIRST, shift1); 
  shiftOut(data, clock, MSBFIRST, shift2);
  shiftOut(data, clock, MSBFIRST, shift3);
  digitalWrite(latch, 1); 
  Serial.print(F("pumped "));
  Serial.print(ozcount);
  Serial.print(F(" qtr oz, about "));
  Serial.print(ozcount / 4);
  Serial.println(F(" full oz."));
}
void pour(const byte shift1, const byte shift2, const byte shift3, byte ozcount, const int oz)
{
  ozcount = ozcount + 1;
  pourcount(shift1, shift2, shift3, ozcount);
  delay(oz);
}

(Notice, I AM learning from the previous suggestions to my threads, albiet slowly. I have not incorporated arrays yet... I am still learning how they fit into my master plan. Soon... Soon...)

Anyways, with the above code my usage of pour looks like:

    //////////////////////// 1.Vodka on the rocks (lemon twist)  /////////////////////////////////////

    if (packBuff[0]== 'V' && packBuff[1]=='O' && packBuff[2]=='O' && packBuff[3]=='N') 
    {

      pour(vodka1st, vodka2nd, vodka3rd, vodka, oh); End();
    
            udpWrite( F("Vodka on the rocks- Serve with a lemon twist.  Enjoy!"));

    }

That will get very tedious and lame when pouring drinks with seven or eight ingredients. (by the way, the "oh": above is my delay time, which stands for "one half ounce") I would LOVE to find a way to make the code work like this:

pour(vodkashift, vodka, oh);

// or even BETTER would be to do

pour(vodka, oh);

//where "vodka" in this case is a function that contains both vodkashift and the const int vodka.
//however, I have no idea how to tell a function I am creating, that I am going to give it
//a random function name as an input condition...  If what I have just described doing
// (passing a function another function as an input condition) Please tell me how!

Crossroads:

Giving all three separate latch controls IS an attractive idea... However then I would need to pass which latch pin into my input conditions of pour() right? Or could I make pour() do all the work and deduce which latch pin to utilize by using if() then() else() logic?

thanks again guys!

Why is a function call more clunky than what you wished for ?
You wanted

digitalWrite(latch, 0);
  shiftOut(data, clock, MSBFIRST, 0, 16, 0);
  digitalWrite(latch, 1);

That is surely more clunky than the function call that I suggested

shift3(0,16,0);

As others have said, the time taken for the code to run should not be a cosideration in the case of your application

As to passing a function the name of a function to be executed, why bother ? Why not just call the function directly ? Alternatively pass a parameter to the pour() function which indicates the name of the function to be called so that you can easily see what you expect the function to do, which seems to be what you want and have the pour() function use switch/case based on the parameter to determine what should happen. If you put the drink names in an enum you can use meaningful names for the drinks for switch/case.

Something like this

enum drinkList {vodka, gandt, drink3};

void setup() 
{
  Serial.begin(9600);
  enum drinkList drink;

  pour(vodka);
  pour(drink3);
  pour(gandt);
}

void loop() 
{
}

void pour(int drink)
{
  switch (drink)
  {
  case vodka:
    pour_vodka();
    break;

  case gandt:
    pour_gandt();
    break;
  case drink3:
    pour_drink3();
    break;
  }
}

void pour_vodka()
{
  Serial.println("actions to pour vodka go here"); 
}

void pour_gandt()
{
  Serial.println("actions to pour Gin and Tonic go here"); 
}

void pour_drink3()
{
  Serial.println("actions to pour drink 3 go here"); 
}

This being C there is a way to pass a function name directly to a function but I can understand the code as written above because I wrote it ! See How do you pass a function as a parameter in C? - Stack Overflow for passing function names to functions.

US ribbon cable source - dipmicro electronics
Selecting which shift register to control - there are too many ways to get there.
You have to find one that you can reliably control.
For example, you might have 3 bytes of data that represent the 24 outputs you control.
Part of your code might set/clear a particular bit.
A later part of your code might compare all three to a previous value, and when one is determined to have changed, it would update the previous value to match, and send it out to the appropriate shift register.
I tend to write code that reflects my hardware design background. Others with software backgrounds might do it differently with functions and what not.

Giving all three separate latch controls IS an attractive idea.

It doesn't solve your problem: you still need to send the data 3 times to update any bit.

That 10k pulldown resistor on the "D10"/"SS"/"Slave Select" pin... is that pin 11, 12, or 14? I would LOVE to put that on my circuit- so if the power should go out and come back on, my BarBot won't start arbitrarily pouring booze on the ground!

As on the diagram on my page: Gammon Forum : Electronics : Microprocessors : Using a 74HC595 output shift register as a port-expander

  1. Where do you buy your ribbon cable good sir? I would very much like to get my hands on some like yours.

Personally I got it from Jaycar, but most electronics places should have it.

Giving all three separate latch controls IS an attractive idea.

For once I agree with dhenry, that doesn't solve much. Using them in the standard way is fine, you send 3 x 8 bits (3 x SPI.transfer) and then latch. It's simple, don't complicate it.

Keep in memory a "copy" off all 24 bits. Change them individually as required. Then shift out all 24.

Jim_Socks:
Anyways, with the above code my usage of pour looks like:
...
pour(vodka1st, vodka2nd, vodka3rd, vodka, oh); End();
...
... I would LOVE to find a way to make the code work like this:

pour(vodka, oh);

//where "vodka" in this case is a function that contains both vodkashift and the const int vodka.

This is a great observation! I believe what you looking for is a structure, not a function:

struct dispenser {
  int shift1, shit2, shift3;  // "shift address"
  long totalOz;  // total ounces to date
  // other stuff specific to one pump
  char *name;
};

Then
dispenser vodka;  // vodka variable is no longer a mere int, it is now a structure
vodka.shift1= 0; vodka.shit2=16;  vodka.shift3 = 0;  // initialize it in setup()
vodka.name = "Vodka";

...

void pour( dispenser & disp, int amt)
{
   ...digiW
   shiftOut(data, clock, MSBFIRST, disp.shift1);
   shiftOut(data, clock, MSBFIRST, disp.shit2); 
   shiftOut(data, clock, MSBFIRST, disp.shift3);
   ...digiWr
   ... pumpOn
   delay( "amt");
   ... pumpOff
   ...
   Serial.print "Total " << disp.name << " poured to date " << disp.totalOz ..
}

Then you are there!

pour( vodka, oh);

Later maybe we talk about
dispenser vodka("vodka", 0, 16, 0);
and the equivilance of
pour( vodka, oh)

vodka.pour(oh);

:slight_smile:

Cheers,
John

vodka.shift1= 0; vodka.shit2=16;  vodka.shift3 = 0;  // initialize it in setup()

Not sure that second statement is going to work too well.

It'll work just fine. I put that there because he 'tends "the good sh*t".

Johncc:

Whoa, blowin my mind. I have been meaning to learn about structures... perhaps now is the time! I'll try to dig up a tutorial or two so I can wrap my mind around the idea you just laid down. It seems like a cool solution. Now to just understand it fully LOL. I'll get on some tutorial action after work tonight!

Nick Gammon:

I tried the 10k pull down resistor on the latch pin like the diagram shows, but it still lights up random lights on first power-on...

dHenry:

Ah yes... you are correct about that. And I see why now.

You may need to put a pull-down resistor on the chip reset line, to keep the chip in reset. Then when you are ready bring that high (use a processor pin to do that).

Jim_Socks:
I tried the 10k pull down resistor on the latch pin like the diagram shows, but it still lights up random lights on first power-on...

Random drink dispenser - could be interesting.

UKHeliBob:

Jim_Socks:
I tried the 10k pull down resistor on the latch pin like the diagram shows, but it still lights up random lights on first power-on...

Random drink dispenser - could be interesting.

(O.T.)
Yeah you could get a random drink to enjoy while you're having a random tattoo applied by by Chris Eckert's "Auto Ink":

Auto Ink is a three axis numerically controlled sculpture. Once the main switch is triggered, the operator is assigned a religion and its corresponding symbol is tattooed onto the persons arm. The operator does not have control over the assigned symbol. It is assigned either randomly or through divine intervention, depending on your personal beliefs.

EEK!

It sounds like this auto ink creation could use a little less "auto" !

Also of note:

Yesterday while I was out of the house- the house lost power! How ironic that we JUST disscussed this in the thread! And just as we discussed- when power came back on a bunch of random lights came on- causing two liters of water to be pumped into a cardboard box full of clothes and books :fearful:

I returned to find a mess, but it could've been WAY worse if a finished barbot with 24 bottles of booze was connected! I really need to fix this glitch before pressing on any further lol

Read again Reply #14,
That is what you need to implement.