Go Down

Topic: Are Four 74HC595 Sift-registers in a row too many? (Read 18265 times) previous topic - next topic

runaway_pancake

Let's deal with one, first.
Let's say you sent out bits to turn on #1.  You clock out 0000 0001.
Later you want to turn #5 on and leave #1 on.  You clock out 0001 0001.
Later you want to turn off #1 and leave #5 on.  You clock out 0001 0000.
Finally, you want to turn on #4 and #6 and everything else off.  You clock out 00101000
You have to send/re-send the whole stream each time.  See?

"Who is like unto the beast? who is able to make war with him?"
When all else fails, check your wiring!

PaulJakob

thanks , sounds clear. i'll try it later and'll get back to you as it works.

CrossRoads

These are the SPI pins:
MOSI: (Master Out Slave In) Datapin (Arduino: 11)    -> goes to SI (14) pin of the 74HC595
SCK: Clockpin (Arduino: 13)  -> goes to SCK (11) pin of the 74HC595
SS: Slave Select (Arduino: 10 )    -> goes to RCK (12) pin of the 74HC595

You will need  this at the top of your sketch
#include <SPI.h>
byte SS = 10;

and this within void setup()
pinMode (SS, OUTPUT);
SPI.begin();
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

PaulJakob

Hi Guys,

sorry but I really don't get something fundamental here. So I was trying with basically just entering "0000 0001" to SPI.transfer(); to see if that works, it didn't.
Then I tried some others and e.g. the code below sometimes shoots of the first, sometimes the 9th, sometimes the 25th....even though it shouldn't even start the function, should it?
What do I not get here?

Thanks...

Code: [Select]

#include <SPI.h>

int ss = 10;

void setup() {

  pinMode (ss, OUTPUT);
  SPI.begin();
//  SPI.setBitOrder(MSBFIRST);
 
}

void loop(){
  for(int i = 0; i < 0; i++){
    sendToRegister(0);
    delay(2000);
  }
}

int sendToRegister(int valueForSolenoid) {

  digitalWrite(ss,LOW);
  SPI.transfer(valueForSolenoid);
  digitalWrite(ss,HIGH);
 
}

PaulJakob

ok, another approach, again with shiftOut(); which doesn't work at all...
(I modified the code I found here: http://www.makeuseof.com/tag/arduino-programming-playing-shift-registers-aka-leds/ )


Code: [Select]


int data = 11; // where we send the bits to control outputs
int clock = 12; // keeps the data in sync
int latch = 8; // tells the shift register when to activate the output sequence

void setup()
{
   // set the three control pins to output
  pinMode(data, OUTPUT);
  pinMode(clock, OUTPUT); 
  pinMode(latch, OUTPUT); 

  Serial.begin(9600);

}

void loop(){

outputBytes(2,1); // just to have some values which later can be addressed dynamically from  Processing
   
}


void outputBytes(int whichRegister, int whichPin){
 
    // start to set every output to 0
    byte dataValues1 = B00000000;
    byte dataValues2 = B00000000;
    byte dataValues3 = B00000000;
    byte dataValues4 = B00000000;
   

    if(whichRegister == 1){
      dataValues1 = B00000001; // set to first pin of the register
      dataValues1 = dataValues1 << whichPin; // move to the actual pin
    }else if(whichRegister == 2){
      dataValues1 = B00000001;
      dataValues2 = dataValues2 << whichPin;
    }else if(whichRegister == 3){
      dataValues3 = B00000001;
      dataValues3 = dataValues3 << whichPin;
    }else if(whichRegister == 4){
      dataValues4 = B00000001;
      dataValues4 = dataValues4 << whichPin;
    }
   

      // go through the registers and address the one chosen pin, set all other pins to 0 (no output)

      digitalWrite(latch, LOW);     

      shiftOut(data, clock, MSBFIRST, dataValues1);
      shiftOut(data, clock, MSBFIRST, dataValues2);
      shiftOut(data, clock, MSBFIRST, dataValues3);
      shiftOut(data, clock, MSBFIRST, dataValues4);

      digitalWrite(latch, HIGH);   

       
       delay(1000);


       // set everything back to 0

       dataValues1 = B00000000;
       dataValues2 = B00000000;
       dataValues3 = B00000000;
       dataValues4 = B00000000;

       digitalWrite(latch, LOW);     

      shiftOut(data, clock, MSBFIRST, dataValues1);
      shiftOut(data, clock, MSBFIRST, dataValues2);
      shiftOut(data, clock, MSBFIRST, dataValues3);
      shiftOut(data, clock, MSBFIRST, dataValues4);

       digitalWrite(latch, HIGH);
       
       delay(1000); */

}



this doesn't work like this, though it seems to work when I just go through the pins using a for() loop:

Code: [Select]

void outputBytes(){

    byte dataValues = B00000001; // change this to adjust the starting pattern


    for (int i=0;i<8;i++){
      digitalWrite(latch, LOW);     
      Serial.println(dataValues, BIN);  // Debug, sending output to the serial monitor
      shiftOut(data, clock, MSBFIRST, dataValues);
      digitalWrite(latch, HIGH);   
      dataValues = dataValues << 1; // Shift the bits one place to the left -  change to >> to adjust direction
      delay(100);   
    }

}



What am I not getting?

Is it also stupid of me to just attach 8 LEDs (instead of solenoids at the moment) to one register and then move them to the others to test one register at the time while there are no LEDs connected to the other registers?
I mean is it necessary to connect all 32 LEDs (solenoids) to the 32 outputs of the registers in order to get the code working right?

Cheers!



Grumpy_Mike

#20
Mar 11, 2012, 04:09 pm Last Edit: Mar 11, 2012, 04:13 pm by Grumpy_Mike Reason: 1
Quote
is it necessary to connect all 32 LEDs (solenoids) to the 32 outputs of the registers in order to get the code working right?

No the code will work anyway.

The code always works. Whether it does what you want is another matter.  

void outputBytes(){
Is totally wrong.
Code: [Select]
shiftOut(data, clock, MSBFIRST, dataValues);
shifts out all 8 bits of data.
It is in a for loop that does it 8 times, so that is 64 bits you are outputting.
Then each time through the loop you do this:-
Code: [Select]
dataValues = dataValues << 1; // Shift the bits one place to the left
which alters the data you put out.

runaway_pancake

I don't live on the arduino reservation, I don't know $^*@ from libraries and I can live without "shields".  It's a nice platform, but you can go your own way, and I do.

I think that the Latch should be kept low.  When it's desired to transfer the data to the storage register then the Latch should be brought high and right back low (ting, ting.)

Here is some code that I wrote early on while working out my display board's programming.  The orthodoxy may get loud and chide me for not using "shiftOut", and that's OK (I just don't want to.)
Maybe it'll confuse you more than help.
I used different pins for datapin, clockpin, and latchpin.

So, for what it's worth (hold your cat calls) ?
Code: [Select]

byte infobyte = 0;   // byte to "shiftout"  ! ! !
byte idxbit = 0;     // indexed bit to mask out
byte infobit = 0;

const int datapin = 3;   // to IC p14
const int clockpin = 5;  // to IC p11
const int latchpin = 7;  // to IC p12

void setup()
{
 pinMode(datapin, OUTPUT);  
 pinMode(clockpin, OUTPUT);
 pinMode(latchpin, OUTPUT);
 digitalWrite(datapin, LOW);
 digitalWrite(clockpin, LOW);
 digitalWrite(latchpin, LOW);
}

void loop()
{
  idxbit = 0;
  infobyte = B01010101;
  while (idxbit <= 7)
  {
    shiftroutine();
  }
  latchcycle();
  // ---0---0---0---0---0---
  idxbit = 0;
  infobyte = B10101010;
  while (idxbit <= 7)
  {
    shiftroutine();
  }
  latchcycle();
  // ---0---0---0---0---0---
  idxbit = 0;
  infobyte = B11000011;
  while (idxbit <= 7)
  {
    shiftroutine();
  }
  latchcycle();
  // ---0---0---0---0---0---
  idxbit = 0;
  infobyte = B00111100;
  while (idxbit <= 7)
  {
    shiftroutine();
  }
  latchcycle();
  // ---0---0---0---0---0---
}

void clocktoggle()
 {  
   digitalWrite(clockpin, HIGH);
   digitalWrite(clockpin, LOW);    
 }

void latchcycle()
 {
   digitalWrite(latchpin, HIGH);
   digitalWrite(latchpin, LOW);
   delay(500);        // *** this is the dwell time ***
 }
 
void shiftroutine()
 {
   infobit = bitRead(infobyte, idxbit);
   digitalWrite(datapin, infobit);
   clocktoggle();
   idxbit++;
 }
"Who is like unto the beast? who is able to make war with him?"
When all else fails, check your wiring!

takao21106

I have working code here: http://pic.hitechworld.org/716pcb
(it's a directory, the source is thermo.c)

This is not Arduino source code, however, only the code which outputs a char (or byte)
is hardware dependent. You only need to replace this routine with Arduino code.

If you can read C language, then here you have a working example,
shifting out 24 bits, 8 at a time.

And i am also getting an Arduino PCB soon :)

CrossRoads

Try this PaulJakob:

Code: [Select]

/* code for sending data to 4 shift register which are daisy chained together
MOSI goes to Serial In on chip 1 (pin 14), Q7/ (pin 9) goes to Serial In on chip 2, etc.
SCK goes to ShiftClock on all 4 devices (pin 11)
ss goes to RegisterClock on all 4 devices (pin 12)
SerialClear (pin 10) is pulled high on all 4 devices
Output Enable (pin 13) is pulled low on all 4 devices
0.1uF capacitor from each chip's Vcc to GND
*/

// used Sketch:Import Library:SPI to make this show up
#include <SPI.h>

int ss = 10;
int i = 0;
int shiftreg = 0;
byte byte1 = 1;
byte byte2 = 1;
byte byte3 = 1;
byte byte4 = 1;


void setup() {

  pinMode (ss, OUTPUT);
  SPI.begin();  // library takes care of D11 MOSI, D12 MISO, D13 SCK

}

void loop(){
  // loop thru 8 times, walk a 1 across all 4 registers
  for(i = 0; i < 8; i=i+1){
    digitalWrite (ss, LOW);
    SPI.transfer (byte1);
    SPI.transfer (byte2);
    SPI.transfer (byte3);
    SPI.transfer (byte4);
    digitalWrite (ss, HIGH); // all outputs change on ss going low to high
    delay(1000);
    byte1 = byte1 <<1; // update the data
    byte2 = byte2 <<1;
    byte3 = byte3 <<1;
    byte4 = byte4 <<1;
  }
  // after all 8 outputs are high one time, reset for the next pass
  byte1 = 1; // update the data
  byte2 = 1;
  byte3 = 1;
  byte4 = 1; 
}
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

PaulJakob

Nice, thanks a lot!

I found a solution as well, the only weird thing now is, that as soon as I am opening a Serial communication and check for a Serial.available() the whole thing gets messed up completely and goes crazy, meaning the wrong pins get activated, and stay activated and before even sending something over the serial port it sort of starts...
How can the serial communication influence some pins of some registers?

Code: [Select]

#include <SPI.h>

int ss = 10;

int nrOfRegisters = 4;
byte dataValues[4];

void setup() {
 
 Serial.begin(9600);
 
 pinMode (ss, OUTPUT);
 SPI.begin();
 SPI.setBitOrder(MSBFIRST);
 
 // set all to 0 in order to start
 for(int i = 0; i < nrOfRegisters; i++){
   dataValues[i] = B00000000;
 }
}

void loop(){
 
//   if (Serial.available() > 0) {

   sendToRegister(0, 0);
   
//  }
}

int sendToRegister(int whichRegister, int valueForSolenoid) {
 
 digitalWrite(ss,LOW);
 
 for(int i = 0; i < nrOfRegisters; i++){
   // set all and everything to 0
   dataValues[i] = B00000000;
   // set the pin for the unique register
   if(i == whichRegister){
     dataValues[i] = B00000001;
     if(valueForSolenoid >= 1){
       dataValues[i] = dataValues[i] << valueForSolenoid;
     }
   }
   
   SPI.transfer(dataValues[i]);
   
 }
 
 digitalWrite(ss,HIGH);
 
 delay(200);
 
 digitalWrite(ss,LOW);
 
 for(int i = 0; i < nrOfRegisters; i++){
   // set all and everything again to 0
   dataValues[i] = B00000000;
   SPI.transfer(dataValues[i]);  // find registers
 }

 digitalWrite(ss,HIGH);
 
 delay(200);
 
}


Grumpy_Mike

Quote
How can the serial communication influence some pins of some registers?

Because what ever you receive you send out to the shift register.
The serial input uses ASCII characters not numbers so if you send a 1 you will actually receive 49 and so send the bit pattern 0011 0001 to the shift registers.

CrossRoads

You said you had 4 shift registers.
Whatever you clock in always get pushed along to the next one when you clock in new data.
It sounds like what you want to do is be able to address the 4 of them individually.
In that case, define & use 4 ss pins (ss1, ss2, ss3, ss4) so you shift data into 1 part at a time.
Code: [Select]

// can make them an array even
ssPin[ ] = {7,8,9,10}; // the 4 ss pins
dataByte [ ] = {0,0,0,0};
// so you can address them as a variable
void setup(){
  for (int x =0; x<4; x=x+1){
    pinMode (ssPin[x], OUTPUT);
    digitalWrite (ssPin[x], LOW);
    }
}
// then in void loop(), or your function call:
digitalWrite (ssPin[x], LOW);
SPI.transfer (dataByte[x]);
digitalWrite(ssPin[x], HIGH);
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

PaulJakob

ok, thanks Grumpy_Mike, but when just opening the serial port nothing is sent yet, right?

Anyways, the same happens when i replace the SPI thing and use shiftOut(); instead....

How could I work around that in order to send something from Processing which then triggers a specific pin on one of the registers?

PaulJakob

thanks CrossRoads, but i guess this would change also the whole wiring, right?

It works actually fine, the only issue I'm having now is that the Serial thing messes with my idea of sending an ascii character from Processing in order to trigger one specific solenoid. And I guess this wouldn't be too hard, but since the whole thing already starts acting strangly when opening the Serial.available() in arduino I'm asking myself if there are other solutions to communicate with processing or to use serial communication without influencing the arduino/register thing...

CrossRoads

If you currently have the 4 of them daisychained, then yes, it changes the wiring.

So keeping it the same:
Now that you've stated what you want to do a little more, you can take this approach:
when the command comes in, have a specific byte changed in the data array, but be sure to shift out all 4 bytes every time:
Code: [Select]

if (Serial.available()>1){  // look for 2 bytes - 1st is register, 2nd is the data it gets
register = Serial.read();
newData = Serial.read();
dataByte[register] = newData;
updateOutput = 1;
}

then clock all 4 out when a change happens
Code: [Select]

if (updateOutput ==1){
updateOutput = 0; // clear for next time
for (int x = 0; x<4; x=x+1){
digitalWrite (ssPin[x], LOW);
SPI.transfer (dataByte[x]);
digitalWrite(ssPin[x], HIGH);
}

or do similar, but look for 4 bytes to come in every time:
Code: [Select]

if (Serial.available()>3){  // look for 4 bytes - update the data array

dataByte[0] = Serial.read();
dataByte[1] = Serial.read();
dataByte[2] = Serial.read();
dataByte[3] = Serial.read();
updateOutput = 1;
}
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Go Up