74HC595 with delay()

i am trying, without good results, to use this chip to control many leds.
the problem come when after turning on a few leds, i use a delay(); but it doesn t work because if i stop the atmega it is unable to call the funcion registerWrite(); which is the funcion that turn on the leds

//74HC595
const int latchPin = 8;
const int clockPin = 12;
const int dataPin = 11;
int bitToSet = ;  //here



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

void loop() {
registerWrite(bitToSet, HIGH);
}



void registerWrite(int whichPin, int whichState) {
  byte bitsToSend = 0;
  digitalWrite(latchPin, LOW);
  bitWrite(bitsToSend, whichPin, whichState);
  shiftOut(dataPin, clockPin, MSBFIRST, bitsToSend);
  digitalWrite(latchPin, HIGH);

}

Your code does not compile due to the below line; what is that equal sign doing there?

int bitToSet = ;  //here

but it doesn t work because if i stop the atmega

What does that mean?

My problem isn t that it doesn t compile.

Do you have a 74h? Have you ever tryed to set due pin high than put a delay and onother two pin high?
It won't work because during the delay period it is unable to call the registerWrite();

aster94:
My problem isn t that it doesn t compile.

OK, but my problem is that it does not compile. And there is no delay in your code; so I don't understand what you mean by 'delay period' below.

aster94:
Do you have a 74h? Have you ever tryed to set due pin high than put a delay and onother two pin high?
It won't work because during the delay period it is unable to call the registerWrite();

registerWrite() only allows you to set or clear a single bit at a time. If you don't want that, you need to modify that function or write another function.

void registerWrite(byte pattern) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, pattern);
  digitalWrite(latchPin, HIGH);
}

And to use it

void loop()
{
  // all leds off
  registerWrite(0);
  delay(500);
  // first led on
  registerWrite(0x01);
  delay(500);
  // couple of leds on
  registerWrite(0x37);
  delay(500);
}

sterretje i didn't post code because i didn t thought that someone would have even take a breadboard and tryed the code

anyway here it is

//74HC595
const int latchPin = 8;
const int clockPin = 12;
const int dataPin = 11;




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

void loop() {
registerWrite(1, HIGH);
registerWrite(2, HIGH);
delay(1000);
registerWrite(3, HIGH);
registerWrite(4, HIGH);
delay(1000);
}



void registerWrite(int whichPin, int whichState) {
  byte bitsToSend = 0;
  digitalWrite(latchPin, LOW);
  bitWrite(bitsToSend, whichPin, whichState);
  shiftOut(dataPin, clockPin, MSBFIRST, bitsToSend);
  digitalWrite(latchPin, HIGH);

}

as you can see it is exactly the same of the sample i d post before but with delay

Some people don't need breadboards :wink: But that was not me; I indeed did build one up to test and play (currently running a KnightRider thingie :wink: )

It's not clear if you still have a problem or that the code in reply #3 is sufficient. Just let us know if you need further assistance.

it works perfect :smiley: ! i m testing it and studing better the theory to understand your function and use it properly, because when i wrote the #1 i just knew that arduino send eight times a 0 or a 1 to control the shift register, no more (at least it is correct, no?)

i have been playing with this, changing the value of bytes, for the last hour

  byte bitsToSend = B01000000;
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, bitsToSend);
  digitalWrite(latchPin, HIGH);

it is strange (i mean for me it is strange but maybe not for you, i started with electronic not so much weeks ago) because in "byte" without the B before even if i write only 0/1 i don t have a binary number but a normal one, why this? i lost 10 minutes to understand it and i realized it only thanks to Serial.println(bitsToSend, BIN)

so until now i understood that with the bitWrite i create the binary value to send, even if i am not completely sure how it works. in the reference it is not explained in a wonderful way

to decide which led to turn on i have to use a 1 but starting counting from the righ and after it (on the left) it is not important to write all the 0 to reach the total eight numbers

but is this 595 that works from the right or if i use LSBFIRST it will work anyway?

i still not sure about the funcion of the latchPin, i think that i understood that it is something that store the value that i send to it and don t let me change it, correct?

for example the second writing won't work

  byte bitsToSend = B1;
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, bitsToSend);
  digitalWrite(latchPin, HIGH);
  Serial.println(bitsToSend, BIN);
  delay(1000);

  bitsToSend = B10;
 // digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, bitsToSend);
  digitalWrite(latchPin, HIGH);
  Serial.println(bitsToSend, BIN);
  delay(1000);

and the clock? it is to like sincronize the two boards? but what does it do really? it send +5, if yes when?

and i also don t understand this kind of notation: 0x01 ox37

would you mind to say me if i wrote something not correct? i know that i wrote a lot ahahah but i discovered this only today

In computers, everything is stored in bits. 4 bits form a so-called nibble, 8 bits form a byte and next it becomes a little complicated (and basically depends on the processor capabilities); I will leave that out for now.

binary, octal, decimal, hexadecimal are (just) representations of a number.

Take a number, e.g. hundred
In decimal, that is 100 (which is one times hundred plus zero times zero plus zero times one).
In hexadecimal, that is 64 (which is six times sixteen plus 4 times one).
In binary, that is 0110 0100 (which is zero times hundred-and-twenty-eight plus one times thirtyt-wo plus zero times sixteen plus zero times eight plus one times four plus one times two plus zero times one).

For the last one, note the spacing into 4 bits for easier reading. Each group of four bits represents a (already mentioned) nibble.

As said, they are just different representations of the same number. If you place a B in front of a number, the compiler will try to interprete it as a binary representation (and the only valid characters that may occur are 0 and 1); if you place 0x (zero ex) or 0X (zero EX) in front of it, the compiler will try to interprete it as a hexadecimal representation and the only valid characters after that are 0-9 and A-F (or a-f)). If you place a 0 (zero) in front of the number, the compiler will try to interprete it as an octal representation (and the only valid characters are 0..7). If you don't place anything in front of the number, the compiler will try to interprete it as decimal (and the only valid characters after that are 0..9).

The octal representation comes from the time where some processors worked with 3 bits (in a nibble) instead of 4 but you can forget about the history. It's however important to know that when you see

int x = 010;

the value of x is eight (first 0 indicating octal, one times eight plus zero times one).

It depends on what you want to represent. For a simple calculation, it's the easiest to use the decimal representation.

byte x = 100;

For the shift register, it's easier to use the binary representation because you can straight away see which bits are set and which bits are not set.

byte x = B01100100;

You asked about MSBFIRST and LSBFIRST. This defines the direction in which the data is shifted out. MSB stands in this for most significant bit and LSB stands for least significant bit. (the B can also stand for byte if one is talking about integers, longs and so on.

It will work in both cases but the result is reversed. With LSBFIRST, leds (counting from 1) 3, 6 and 7 will be on; with MSB first, LEDS 2, 3 and 6 will be on.

The 595 as worth a post on its own.

The 74(HC)595 basically consists of two sections; one that handles the shifting in of data and one that handles the latching. We start with the shifting section (forget about the latch for now).

bit positions          7   6   5   4   3   2   1   0 
                     +---+---+---+---+---+---+---+---+
              clock--|   |   |   |   |   |   |   |   | shift section
               data--|   |   |   |   |   |   |   |   |
                     +---+---+---+---+---+---+---+---+
                       |   |   |   |   |   |   |   |
                                 OUTPUTS
                                 
                       0   0   0   0   0   0   0   0

The initial value of the outputs is set to all zeroes; the bit positions are zero-based (0..7)

On every clock, one bit will be moved from the Arduino into the shift section; lets take 5, B00000101 to switch LEDS 1 and 3 on (and the rest off and LSB (least significant) first

The below table shows what will happen.

not clocked (prev value)   0   0   0   0   0   0   0   0
after one clock pulse      1   0   0   0   0   0   0   0
after two clock pulses     0   1   0   0   0   0   0   0
after three clock pulses   1   0   1   0   0   0   0   0
after four clock pulses    0   1   0   1   0   0   0   0
after five clock pulses    0   0   1   0   1   0   0   0
after six clock pulses     0   0   0   1   0   1   0   0
after seven clock pulses   0   0   0   0   1   0   1   0
after eight clock pulses   0   0   0   0   0   1   0   1

As you can see from the table, after one clock pulses bit 7 will be switched on, after two clock pulses bit 7 will be off again and bit 6 will be on, after three clock pulses bits 7 and 5 will be on and so on. So the shifting affects all outputs and that is (often) not what one wants. Therefore the 595 has a second section that can freeze the outputs in their current state while the shifting is in progress.

bit positions          7   6   5   4   3   2   1   0 
                     +---+---+---+---+---+---+---+---+
              clock--|   |   |   |   |   |   |   |   | shift section
               data--|   |   |   |   |   |   |   |   |
                     +---+---+---+---+---+---+---+---+
                       |   |   |   |   |   |   |   |
                     +---+---+---+---+---+---+---+---+
              latch--|   |   |   |   |   |   |   |   | latch section
                     +---+---+---+---+---+---+---+---+
                       |   |   |   |   |   |   |   |
                                 OUTPUTS

Now the shifting will have no effect on the outputs; only when the latch pin goes from low to high, the data from the shift section is 'copied' to the latch section and the content of the shift section is now visible on the outputs (and will not change regardless of what happens in the shift section).

It's advisable to download the datasheet of the 74HC595 to check the specifications (e.g. the max. current in through the device is only 70mA so 8 leds with 10mA permanently on will be outside the specification). If you want to know more about that, better start a new thread.

sterretje i think that you wrote an explanation of how the 595, and shift registers, works better than many sites i have read today

i understood everything even if english is not my first language

but just to know, when you had put 0x38, you didn't know which pin would turned on, did you? i mean without "translating" it

since you like to test and play if you want to enyoj the 595 again here there is all the code i wrote
considering that you helped me a lot i will say you the password, it is 8 :zipper_mouth_face: :wink:

//74HC595
const int latchPin = 8;
const int clockPin = 12;
const int dataPin = 11;

//others
int buttonPin = 2;
boolean state1;
boolean state2;
boolean pressed;
int led = 7; //led di debugg
int password = 8;
int input;

//segmenti
byte a = B100000;
byte b = B10000;
byte c = B1000;
byte d = B100;
byte e = B10;
byte f = B1000000;
byte g = B10000000;
byte dot; //not in use

//numeri
byte n1 = b + c;
byte n2 = a + b + d + e + g;
byte n3 = a + b + d + c + g;
byte n4 = b + c + f + g;
byte n5 = a + f + d + c + g;
byte n6 = a + c + d + e + f + g;
byte n7 = a + b + c;
byte n8 = a + b + c + d + e + f + g;
byte n9 = a + b + c + d + f + g;
byte n0 = B0;


void setup() {

  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  Serial.begin(9600);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  registerWrite(n0); //cleaning the display

}

void loop() {

  //lettura pulsante con debugg
  state1 = !digitalRead(buttonPin);
  delayMicroseconds(200);
  state2 = !digitalRead(buttonPin);

  if (state1 == 1 && state2 == 1) {
    pressed = true;
  } else {
    pressed = false;
  }

  if (pressed == 1) {

    countdown();

    input = Serial.read() - 48; //it works only in ASCII

    if (input == password) {
      Serial.println("YOU SAVED THE WORLD");
      delay(3000);
    } else {
      Serial.println("WE ARE DEATH");
      delay(3000);
    }
  }
}

void countdown() {
  registerWrite(n9);
  delay(1000);
  registerWrite(n8);
  delay(1000);
  registerWrite(n7);
  delay(1000);
  registerWrite(n6);
  delay(1000);
  registerWrite(n5);
  delay(1000);
  registerWrite(n4);
  delay(1000);
  registerWrite(n3);
  delay(1000);
  registerWrite(n2);
  delay(1000);
  registerWrite(n1);
  delay(1000);
  registerWrite(n0);
  delay(1000);
}


void registerWrite(int bitsToSend) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, bitsToSend);
  digitalWrite(latchPin, HIGH);
}

just speaking, i didn t found the clock speed of the chip, so i think it will use the arduino one , 16Mhz if i m not wrong

so if i write something like 8 time digitalWrite(clockPin, HIGH); and delay(clockTime); between the digitalwrite

i should obtain all the leds on?

clockTime, using 16Mhz should be 6×10-2 Microseconds

I made a small mistake in the explanation of the 595. The first bit that arrives in the 595 is the most significant bit. See the schematic in the datasheet. The principle stays the same though.

I'm reasonable fluent in converting hex to binary. It becomes kind of a second nature similar to speaking a foreign language; if you speak e.g French often on a daily basis, there is mostly no translation from native language to foreign language.

If you set the datapin high and toggle the clockpin 8 times using digitalWrite(clockPin, HIGH) followed by digitalWrite (clockPin, LOW), you indeed shift all 1s in and all outputd are HIGH. You will never achieve 16 MHz though. First of all it takes a high and low or vice versa to clock, so that will bring it down to 8 MHz. Next point is that digitalWrite takes more than one clockCycle as it does not translate to a single 328 instruction. You can use direct port manipulation (do a search) to speed up the process; not sure what is achievable.

I will have a look at your code tomorrow.

PS The datasheet has a spec for the max clock frequency; there are three numbers for e.g. Vcc = 4.5V.
@25 deg Celcius, SN54HC595 (military grade version) and SN74HC595 (consumer version). To play it safe, I personally usually use the lowest number, so 21 MHz.

"not sure what is achievable"

i was speaking hypothetically just to be sure that i had understood it correct!

"direct port manipulation (do a search)"

i read a little bit about it after seing a code with it but for now i decided not to use it since in my applications i wouldn t need it and i still have a lot of stuff to know before

Cool :wink:

Looks like you're using a 7-segment display.

Anyway, have you considered to put your numbers in an array?

byte numeri[10] = 
{
  B00000000,  // all off
  B00011000,  // 1
  ...
  ...
  B11111110, // 8
  B11111100  // 9
}

You have to fill in the dots :wink: And check the values that I came up with :smiley:

You can now use e.g. a for-loop in the countdown

void countdown() {
  for(cnt=9;cnt>=0;cnt--)
  {
    registerWrite(numeri[cnt]);
    delay(1000);
  }
}

And next you might want to read the BlinkWithoutDelay example that comes with the IDE and apply the principles in a future version where you want allow the user to change the input during the countdown. But that is a later story.

sterretje:
Cool :wink:

Looks like you're using a 7-segment display.

Anyway, have you considered to put your numbers in an array?

byte numeri[10] = 

{
 B00000000,  // all off
 B00011000,  // 1
 ...
 ...
 B11111110, // 8
 B11111100  // 9
}



You have to fill in the dots ;) And check the values that I came up with :D

You can now use e.g. a for-loop in the countdown


void countdown() {
 for(cnt=9;cnt>=0;cnt--)
 {
   registerWrite(numeri[cnt]);
   delay(1000);
 }
}




And next you might want to read the BlinkWithoutDelay example that comes with the IDE and apply the principles in a future version where you want allow the user to change the input during the countdown. But that is a later story.

yessss yesterday i updated my sketch with an array and a function very similar to yours
and two days ago i red for the first time about millis(); and right now i can t wait to use it! but for now i m cloning an ir signal later i will try the power of no delay sketchs :smiley:

You don't need any delays to send data to the shift registers.
For example, this board has 12 shift registers. To load them with data pretty quickly, I do this:

digitalWrite (ssPin, LOW);
for (x=0; x<12; x=x+1){
SPI.transfer(dataArray[x]);
}
digitalWrite (ssPin, HIGH); // outputs update on this rising edge

There are ways to speed it up; set the SPI clock speed to 8 MHz, use direct port manipulation instead of digitalWrite, don't use the SPI library, don't use the for loop:

PORTB = PORTB & 0b11111011; // clear (make 0) D10 (SPI's SS pin)
spdr = dataArra[0];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[1];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[2];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[3];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[4];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[5];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[6];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[7];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[8];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[9];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[10];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = dataArra[11];nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
PORTB = PORTB | 0b00000100; // set (make 1) D10

This will send out 12 bytes in just over 12uS; 17 clocks per transfer x 12 transfers x 1/16,000,000 = 12.75uS
I have sent out 45 bytes at a time this way in 47.8uS.
I also turned off interrupts while sending them out so that the millis()/micros() interrupt did not impact the data stream.

CrossRoads:
You don't need any delays to send data to the shift registers.

I think you see the picture :wink:

It's a countdown timer and the progress is indicated by the LEDs and hence the delay. Now you can obviously say that delay() is not the correct approach but as long as it gives the desired result, nobody will complain (for now).