First experience with shift register: GOES WRONG

Hey there everybody!

After a long time I decided to recover my board from the shelf where it has stayed for years and I discovered I had an SN74HC595, which I didn’t know about.

Long story short, it looked interesting so I code something with a 4-digit 7-segment display (SMA420564). The idea is to have the display to loop from 0000 to 9999. And it works quite nicely, but after 20-30 secs it turns off. No overheating observed. Find attached the code, the wiring schematics and a video of the ‘failure’. Sorry for the messy wiring, I’m quite a noob :confused:

Thanks in advance for helping me with this! I’d appreciate any feedback

Cheers!
-G

via Imgflip GIF Maker

int latchPin=5;
int dataPin=4;
int clockPin=6;
const int digitPin[4]={3,9,10,11}; // PWM outputs that drive the digit pins
const byte digit_pattern[10]=
{
  B11111100, // 0
  B01100000, // 1
  B11011010, // 2
  B11110010, // 3
  B01100110, // 4
  B10110110, // 5
  B10111110, // 6
  B11100000, // 7
  B11111110, // 8
  B11110110 // 9
};

unsigned int counter = 0;

int thousand;
int hundred;
int ten;
int one;
int t0=0;
int t1=100; // ms that ellapse before a new number is sent

void setup() {
  // put your setup code here, to run once:
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  
for (int counter = 0; counter <= 9999; counter++) { //cycles through all numbers
  
  // decompose the number to get the value for each digit
  thousand=floor(counter/1000);
  hundred=floor((counter-thousand*1000)/100);
  ten=floor((counter-thousand*1000-hundred*100)/10);
  one=(counter-thousand*1000-hundred*100-ten*10);
  
  int  digits[4]={ thousand ,hundred , ten , one};
  
  t0=millis();
  
  while (millis()-t0<=t1) { // if ellapsed time is below t1, don't send a new number
  
  for (int i=0; i <= 3; i++) { //cycles through all 4 digits

    digitalWrite(latchPin,LOW); // pulso BAJO
    digitalWrite(clockPin, LOW);  
    shiftOut (dataPin, clockPin, LSBFIRST, digit_pattern[digits[i]]);
    digitalWrite(latchPin,HIGH); // pulso ALTO

    
    analogWrite(digitPin[i], 0);  
    delay(4);
    analogWrite(digitPin[i], 255);    
    
    }
}
}
}

2020-04-18 21.47.09.jpg

OK, the problem seems to be your ham-fisted emulation of "delay":

int t0=0;
int t1=100; // ms that elapse before a new number is sent

...

  t0=millis();
  while (millis()-t0<=t1) { // if elapsed time is below t1, don't send a new number

just try delay(100); instead of these two lines. :grinning:

But before you do that, have you tried leaving it running with the problematic code for several minutes? Maybe ten minutes, to see if anything happens? :astonished:

In IDE ->file/preferences, what is the compiler warning level?

You are comparing unlike number types.

What value resistors are you using? They should be at least 680R. Anything lower could be damaging the Arduino with too much current into the pins connected to the digit commons. Could also be damaging the 74hc595.

For example if the resistors are 220R, then the current flowing through a segment would be around (5V - 1.8V) / 220 = 14.5mA. If the digit is showing "8", then the total current would be about 100mA. This would be exceeding the max current of the 74hc595, which is 70mA. If would also be exceeding the max current for an Arduino digital pin, which is 40mA.

I guess it stops after 32.768 seconds:

t0=millis() ;

You should use unsigned long for t0, not int.

Now that is giving the game away! :grinning:

Wow, thanks for all the feedback. I got it to work.

Paul__B:
2020-04-18 21.47.09.jpg

OK, the problem seems to be your ham-fisted emulation of "delay":

int t0=0;

int t1=100; // ms that elapse before a new number is sent

...

t0=millis();
  while (millis()-t0<=t1) { // if elapsed time is below t1, don't send a new number



just try delay(100); instead of these two lines. :grinning: 

But **before** you do that, have you tried leaving it running with the problematic code for several minutes? Maybe ten minutes, to see if anything happens? :astonished:

The thing is that it's not a delay emulation. I want to repeat the digit for loop until some time has passed so the display keeps refreshing, otherwise it won't. Maybe there's a more straightforward way but I don't know about it.

dougp:
In IDE ->file/preferences, what is the compiler warning level?

You are comparing unlike number types.

Indeed, I set the warning level to all and got that error. I defined all the implied variables as unsigned, solved. However, it still failed (after some more time tho).

6v6gt:
I guess it stops after 32.768 seconds:

t0=millis() ;

You should use unsigned long for t0, not int.

Here's the key I think, an overflow (by the way, that crashed a $370M rocket+spacecraft). It is so stupidly simple and has nothing to do with the shift register, but with my poor programming skills :smiley: Thanks a lot 6v6gt.

Anyway, I changed the code without declaring variables as long and it works. So, now out of curiosity, why doesn't it fail after the overflow (at 60k something ms)?

int latchPin=5;
int dataPin=4;
int clockPin=6;
const int digitPin[4]={3,9,10,11}; // PWM outputs that drive the digit pins
const byte digit_pattern[10]=
{
  B11111100, // 0
  B01100000, // 1
  B11011010, // 2
  B11110010, // 3
  B01100110, // 4
  B10110110, // 5
  B10111110, // 6
  B11100000, // 7
  B11111110, // 8
  B11110110 // 9
};

unsigned int counter = 0;
int thousand;
int hundred;
int ten;
int one;
unsigned int t0;
unsigned int t1;

int freq=60; // display refresh rate in Hz
unsigned int numberdelay=100; // min time each number is displayed in ms

void setup() {
  // put your setup code here, to run once:
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  
for (int counter = 0; counter <= 9999; counter++) { //cycles through all numbers
  
  // decompose the number to get the value for each digit
  thousand=floor(counter/1000);
  hundred=floor((counter-thousand*1000)/100);
  ten=floor((counter-thousand*1000-hundred*100)/10);
  one=(counter-thousand*1000-hundred*100-ten*10);
  
  int  digits[4]={ thousand ,hundred , ten , one};
  
  t0=millis();
  t1=t0;
while (t1-t0<numberdelay) { // if ellapsed time is below t1, don't send a new number

  for (int i=0; i <= 3; i++) { //cycles through all 4 digits
    
    digitalWrite(latchPin,LOW); // pulso BAJO
    digitalWrite(clockPin, LOW);  
    shiftOut (dataPin, clockPin, LSBFIRST, digit_pattern[digits[i]]);
    digitalWrite(latchPin,HIGH); // pulso ALTO
    
    analogWrite(digitPin[i], 0);  
    delay(250/freq); 
    analogWrite(digitPin[i], 255);
    }
    t1=millis();
}
}
}

-G

germangg95:
The thing is that it's not a delay emulation. I want to repeat the digit for loop until some time has passed so the display keeps refreshing, otherwise it won't. Maybe there's a more straightforward way but I don't know about it.

But it is an emulation of delay(). It just sits there and cycles until millis() advances by numberdelay. Same as delay(numberdelay);

germangg95:
Indeed, I set the warning level to all and got that error. I defined all the implied variables as unsigned, solved. However, it still failed (after some more time tho).

because millis() is an unsigned long.

germangg95:
So, now out of curiosity, why doesn't it fail after the overflow (at 60k something ms)?

It failed when the signed t0 turned negative at 32,768 and subtracting a large negative number from a positive gives a large positive number which can never be less than 100.

Paul__B:
But it is an emulation of delay(). It just sits there and cycles until millis() advances by numberdelay. Same as delay(numberdelay);

Its not exactly an emulation.

delay(100);

and

while(timeWaited < 100){
   doSomeMultipexlingStuff();
}

differ slightly.

OK, my bad - it’s not a delay(). :roll_eyes:

Kind of the reverse.

Depends on whether there is code in the while section or out of it.

Must be time to go to bed. :astonished:

Paul__B:
It failed when the signed t0 turned negative at 32,768 and subtracting a large negative number from a positive gives a large positive number which can never be less than 100.

Yes, indeed. The thing is, now it works having declared t0 and t1 as unsigned integers (not long). And it should fail! hahaha anyway, thanks a lot for the help.

By the way,

PaulRB:
What value resistors are you using? They should be at least 680R. Anything lower could be damaging the Arduino with too much current into the pins connected to the digit commons. Could also be damaging the 74hc595.

For example if the resistors are 220R, then the current flowing through a segment would be around (5V - 1.8V) / 220 = 14.5mA. If the digit is showing “8”, then the total current would be about 100mA. This would be exceeding the max current of the 74hc595, which is 70mA. If would also be exceeding the max current for an Arduino digital pin, which is 40mA.

I’m using 220 Ohm resistors, used in all the examples I looked to do this. I think that’s the right thing to and I don’t see why would the currents sum up. If anyone can shed some light upon this…

-G

unsigned int t0;
unsigned int t1;

(...)


t0=millis();
t1=t0;
while (t1-t0<numberdelay) {

    (...)

    t1=millis();
}

In your code the assignment removes the higher bits in both cases. This probably makes it somehow working again.

I would expect this to fail after 65 seconds:

unsigned int t0;

(...)

t0=millis();
while (millis()-t0<numberdelay) {

    (...)
}

But unsigned long should fix it.

...I don't see why would the currents sum up.

Why do you think the currents would not sum up? You are sending data to the shift register which will light between 1 and 7 (8?) segments at the same time. That's true, isn't it? Or did I miss something? How does that current get to ground, except through an Arduino pin?

germangg95:
Yes, indeed. The thing is, now it works having declared t0 and t1 as unsigned integers (not long). And it should fail! hahaha anyway, thanks a lot for the help.

Well, it works insofar as you have constrained the timing to 65½ seconds. If it behaves itself as it rolls over that interval - as it should if it truncates correctly and you use the appropriate comparison operator - that that is probably sufficient.

germangg95:
I'm using 220 Ohm resistors, used in all the examples I looked to do this. I think that's the right thing to and I don't see why would the currents sum up. If anyone can shed some light upon this...

The point is that you are not using those resistors on the common digit lines which share the current from all the segments. If you did put resistors on those, the brightness of the digits would vary depending on how many segments were lit (as it may already).

You are exceeding the driving ability of the hardware you are using. A perhaps interesting experiment, but not a practical working design. If you need such a display, there are plenty available on eBay, very cheap (if you can currently get them shipped) using the MAX7219 driver which is designed to do the job properly.

But that said, there are also total rubbish modules sold (or have been in the past, can't say I have looked recently!) which are based on the 74HC595 in similar manner to what you demonstrate - and which don't even have the resistors! And there are websites such as "instructables" where enthusiastic people post designs which "work" but have just this sort of design flaws, so great caution is needed. :roll_eyes:

PaulRB:
Why do you think the currents would not sum up? You are sending data to the shift register which will light between 1 and 7 (8?) segments at the same time. That's true, isn't it? Or did I miss something? How does that current get to ground, except through an Arduino pin?

Paul__B:
The point is that you are not using those resistors on the common digit lines which share the current from all the segments. If you did put resistors on those, the brightness of the digits would vary depending on how many segments were lit (as it may already).

You are exceeding the driving ability of the hardware you are using.

oooops of course I am, I didn't thought about that. Sorry :cold_sweat: hahahaha. Ok, so the most straightforward way would be to use a dedicated driver, but let's say I wanted to do it without it. I could also increase the resistor value, losing brightness. Is there any way of making a relatively simple set up (using transistors or sth) that can make it work properly without losing brightness (the LED's are protected with current situation I guess)?

As always, thanks a lot for the info and sorry for my infinite ignorance. I will be careful with those 'instructables' tutorials from now on.

-G

You can increase the resistors to 680R. That will limit the digit current to about 32mA, but the display will be less bright.

You can increase the resistors to 330R and add a transistor such as bc337 for each digit. Collector pin to digit common pin, emitter to ground, base to Arduino pin via 2K2 or 4K7 resistor.