8 digts 7 segment LED display

I'm moving my first steps with ad Arduino UNO, please be patient...

I would like to drive a 8 digits 7 segment display (8 segment to be precise as THere's also the dot).
I've read a couple of things about the 74HC595 wich seemed to be used on the little shield I get from Internet (8 x Seven-Segment Displays Module for (For Arduino) (595 Driver) 411869 2022 – $8.10) and it seemed complex but not so crazy.

At the first try I get unexpeted and random results.
I was thinking I had to drive a 64 positions register each one related to each segment but... now it's clear that's not the game I have to play.
In addition, taking a look at the two 4 digit display I can see that each has just 12 pins under each block of segment (24 in total) so there should be some kind of multiplexing I have to deal with to light the segment I cannot figure out.

-- Do anyone has already worked with this shield? (I cannot find out any tech document)

-- Do you know how many bits I have to deal with while eriting to the register before sending HIGH to the RCLK pin?

-- Is there any documentation around I can use to understand how to send data to the register as lo light the segment I want lo light?

Thanks to all.
(Please, simple english as mine is a disaster :-(( )


Is there any documentation on the part?
5 control pins - +5, Gnd, Clock, Data, Latch?
One shift register likely controlling the segments, one controlling which digit is selected.
So the game:
send out 2 bytes, one will have segment info, one will have digit control.

Is there any documentation on the part?

5 control pins - +5, Gnd, Clock, Data, Latch?

One shift register likely controlling the segments, one controlling which digit is selected.
So the game: >send out 2 bytes, one will have segment info, one will have digit control.

Not understood.
If I try to send to send 24 bit (just fgured out thinkng of the 24 pin I can see under the digits), when I send High to 9 to 16 position I light the full segment (each bit lights an entire segment) but nothing happens if, at the same time, I set any of the bits from 1 to 8.
Is this related to what yore saying?


Sort of.
You will send 2 bytes to the part every 4 mS.
1 byte will determine what is displayed, the 2nd will determine where it is displayed.

The “what” is displayed usually comes from an array:

byte fontArray[] = {
B00111111, // 0   bit 0 = a, 1=b, 2=c, 3=d, 4=e, 5=f, 6=g, 7=decimal    High bit sources current for the selected segment
B00000110, // 1          a
B01011011, // 2    f            b
etc                                g
B01100111, // 9    e           c
};                                   d

The “where” comes from a 2nd array:

byte digitArray[] = {
B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111,}; // 0 sinks current for the digit selected

So you send out 2 bytes:

digitalWrite(latchPin, LOW);
shiftout(dataPin, clockPin, MSBFIRST,fontArray[x]); // these 2 lines may need to be swapped
shiftout(dataPin, clockPin, MSBFIRTS, digitArray[y]); // around, depends on how board is wired
digitalWrite(latchPin, HIGH);

This code would be in a loop, where one bye of fontArray data was sent out with one byte of digitArray.
Say you wanted to display the numbers 0 to 7 on the display. Then the loop might be:

for (x = 0; x<8; x=x+1){ // count from 0 to 7
digitalWrite(latchPin, LOW); // bring the shift register latch pin low
shiftout(dataPin, clockPin, MSBFIRST,fontArray[x]);  // shift out the segment info
shiftout(dataPin, clockPin, MSBFIRTS, digitArray[x]);  // shift out the digit select info
digitalWrite(latchPin, HIGH);   // bring the shift register latch pin high, moves the data to the outputs
delay (4);  // delay to let the data be seen at each digit (there are better ways to create this pause)


This evening I’m going to read what you wrote in peace but…
… do this means that I can write one digit a time so I need a lot of speed writing all the digits and rely on the persistence of my eye to get the whole 8 digits???

(Hope my english is understandable)


Yes. One digit at a time, with persistence of vision.

With 4mS/digit, and a 16 MHz clock, you have 64,000 clock cycles for every digit, so the speed writing is easily done.

Here is a way I do the time part so delay() is not needed.

unsigned long currentmillis();  // millis() and micros() are type unsigned long, 0x00000000 to 0xFFFFFFFF
unsigned long previousmillis();
unsigned long duration = 4;  // time in mS to display each digit
// declare other variables, and arrays discussed earlier

void setup(){
// do all the setup stuff, pinModes & stuff.

void loop(){
currentmillis = millis();  // capture the "time"
if ( (currentmillis - previousmillis)>=duration){  // 4 mS gone by yet?
previousmillis = previousmillis  + duration;  // reset for next time check

x=x+1;  // break down for:next loop so it can run within the time check
if (x==8){
x=0;  // reset  after passing 7
digitalWrite(latchPin, LOW); // bring the shift register latch pin low
shiftout(dataPin, clockPin, MSBFIRST,fontArray[x]);  // shift out the segment info
shiftout(dataPin, clockPin, MSBFIRTS, digitArray[x]);  // shift out the digit select info
digitalWrite(latchPin, HIGH);   // bring the shift register latch pin high, moves the data to the outputs
} // end time check
// do other stuff if desired while waiting for next time check
} // end void loop

Make sense?

Well... even if with everything is still to put in order... IT WORKS!

Still using the bitRead() fuction (understood it's not the func for this job), still few problems with the delay, still to reengineer as to work in an environment where the main objective is something else, still to reorganize as to be used as a 'black box library' but... my main problem is behind.

Let's say the first time you come to Italy (if not already here) you get a serious bottle of wine.

Thanks a lot.


Yes, not too bad just from looking at a picture 8)
We are cooking a turkey for dinner (it was still quite frozen on Christmas Day), I will think of your wine as we enjoy our meal :slight_smile:

Thanks to all.

I would take a generic approach:

  1. set up a timer isr that fires periodically. I think a 100hz refresh rate more than works for everyone. That means you need to refresh each digit about 1-2ms.
  2. in the timer isr, keep track of which digit you are refreshing and pick up the font / segment information from a display buffer.

Once that is setup, the whole thing runs entirely in the background, regardless of what you do in the loop(). All you need to do there is to stuff the segment information into the display buffer, and the isr will take care of refreshing the leds.

Your leds will never flicker.

set up a timer isr

I don't have a precise project to work on but just spending time trying to learn something so I'm I'd be happy to try different approach.
As I neither know what a timer isr is I'll google around at first... any link to example or whatever can bu usefull for a beginner?



P.S. @CrossRoads : really not bad from just a picture. :slight_smile:

Not a problem.

This is what I put together: the original code has a timer piece, a led piece and a user application which calls the timer piece which in turn calls the led piece to work.

//demo code to run ledx8 display in a timer isr
//for common anode 7segment displays. 8 digits

//pin configuration
//define segment pins
#define SEG_PORT  PORTD  //connection: p0 -> a, p1 -> b, ..., p6 -> g, p7 -> dp
#define SEG_DDR   DDRD
#define SEGs      0xff //all pins used
//output a value to the segment, active low
#define SEG_OUT(val)  SEG_PORT = (val)  //output a value on segport, common anode
//#define SEG_OUT(val)  SEG_PORT =~(val)  //output a value on segport, common cathode

//define digits
#define DIG0      A0  //most significant digit in the display
#define DIG1      A1
#define DIG2      A2
#define DIG3      A3
#define DIG4      A4
#define DIG5      A5
#define DIG6      12
#define DIG7      13  //least significant digit in the display

//macros to turn on / off a digit. active high (common anode)
//may need to adjust for your applications
#define DIG_ON(dig)  digitalWrite(dig, HIGH)
#define DIG_OFF(dig) digitalWrite(dig, LOW)

//timing constants - max of 64k ticks
#define TMR_1s    (F_CPU)  // 1 second
#define TMR_1ms   (TMR_1s / 1000) //1ms
#define TMR_2ms   (TMR_1ms * 2)    //2ms
#define TMR_3ms   (TMR_1ms * 3)    //2ms
#define TMR_4ms   (TMR_1ms * 4)    //2ms
#define TMR_5ms   (TMR_1ms * 5)   //5ms
#define TMR_10ms  (TMR_1ms * 10)  //10ms

//gloval variables
//display font for common anode display - active low
unsigned char const led8_segs[]= {
	//common anode segment table for 7-segment display
	//7seg connection:
	//0->A: ~0x01
	//1->B: ~0x02
	//2->C: ~0x04
	//3->D: ~0x08
	//4->E: ~0x10
	//5->F: ~0x20
	//6->G: ~0x40
		0xC0,							//'0'
		0xF9,							//'1'
		0xA4,							//'2'
		0xB0,							//'3'
		0x99,							//'4'
		0x92,							//'5'
		0x82,							//'6'
		0xF8,							//'7'
		0x80,							//'8'
		0x98,							//'9'
		0x80 | 0x08,					//'A'
		0x80 | 0x01 | 0x02,				//'b'
		0x80 | 0x02 | 0x04 | 0x40,		//'C'
		0x80 | 0x01 | 0x20,				//'d'
		0x80 | 0x02 | 0x04,				//'E'
		0x80 | 0x02 | 0x04 | 0x08		//'F'

unsigned char lRAM[8]; //display buffer. active high

//timer isr
  led8_display();    //update the display
  //digitalWrite(DIG0, !digitalRead(DIG0));  //flip dig0 for debugging purposes

//initialize timer
void tmr1_init(unsigned short period) {
	TCCR1B =	TCCR1B & (~0x07);			//turn off tmr1
	TCCR1A =	(0<<COM1A1) | (0<<COM1A0) |	//output compare a pins normal operation
				(0<<COM1B1) | (0<<COM1B0) |	//output compare b pins normal operation
				//(0<<COM1C1) | (0<<COM1C0) |	//output compare c pins normal operation
				(0<<WGM11) | (0<<WGM10)		//wgm13..0 = 0b0100 -> ctc, top at ocr1a
	TCCR1B =	(0<<ICNC1) |				//nput capture noise canceller disabled
				(0<<ICES1) |			//input capture edge selection on falling edge
				(0<<WGM13) | (1<<WGM12) |	//wgm13..0 = 0b0100 -> ctc, top at ocr1a
				(0x01)	//prescaler = 1:1
	TCCR1C =	(0<<FOC1A) |				//forced output on ch a disabled
				(0<<FOC1B) //|				//forced output on ch b disabled
				//(0<<FOC1C)					//forced output on ch c disabled
	OCR1A = period;
	TCNT1 = 0;								//reset the timer / counter
	TIMSK1 =	(0<<ICIE1) |				//input capture isr: disabled
				//(0<<OCIE1C) |				//output compare isr for ch c: disabled
				(0<<OCIE1B) |				//output compare isr for ch b: disabled
				(1<<OCIE1A) |				//output compare isr for ch a: disabled
				(0<<TOIE1)					//tmr overflow interrupt: enabled

//initialize the display
void led8_init(void) {
  //initialize the segments
  SEG_OUT(0x00);  //clear the segment
  SEG_DDR |= SEGs;  //segments as output
  //initialize the digits
  DIG_OFF(DIG0); pinMode(DIG0, OUTPUT);
  DIG_OFF(DIG1); pinMode(DIG1, OUTPUT);
  DIG_OFF(DIG2); pinMode(DIG2, OUTPUT);
  DIG_OFF(DIG3); pinMode(DIG3, OUTPUT);
  DIG_OFF(DIG4); pinMode(DIG4, OUTPUT);
  DIG_OFF(DIG5); pinMode(DIG5, OUTPUT);
  DIG_OFF(DIG6); pinMode(DIG6, OUTPUT);
  DIG_OFF(DIG7); pinMode(DIG7, OUTPUT);

//update the display
void led8_display(void) {
  static unsigned char dig=0; //current digit to be turned on
  //optional: blanking
  //turn on respective digits
  switch (dig) {
    case 0: DIG_OFF(DIG7); DIG_ON(DIG0); dig = 1; break;
    case 1: DIG_OFF(DIG0); DIG_ON(DIG1); dig = 2; break;
    case 2: DIG_OFF(DIG1); DIG_ON(DIG2); dig = 3; break;
    case 3: DIG_OFF(DIG2); DIG_ON(DIG3); dig = 4; break;
    case 4: DIG_OFF(DIG3); DIG_ON(DIG4); dig = 5; break;
    case 5: DIG_OFF(DIG4); DIG_ON(DIG5); dig = 6; break;
    case 6: DIG_OFF(DIG5); DIG_ON(DIG6); dig = 7; break;
    case 7: DIG_OFF(DIG6); DIG_ON(DIG7); dig = 0; break;
    default: dig = 0; //dig reset to 0
  SEG_OUT(lRAM[dig]); //output the segment data, for common anode

//convert a number to segment information
void ul2segs(unsigned char *str, unsigned long ul, unsigned char length) {
  while (length) {
    str[--length]=led8_segs[ul % 10]; ul /= 10;    //perform the conversion -> leading 0s

void setup(void) {
  led8_init();  //reset the led module
  tmr1_init(TMR_1ms);  //set up tmr1 to fire periodically
  sei();  //enable interrupt

//user code
void loop(void) {
  static unsigned short i=0; //index to be displayed
  ul2segs(lRAM, i++, 8); //convert i to 8-digit segment information
  delay(1000); //increment i periodically

As configured, it drives a 8-digit 7-segment common anode display. The digits are updated once every 1ms (user configurable). All user needs to do is to fill lRAM with the data and it will be updated on the led display. You can generate a library (two actually, one for the timer and one for the display) from this to make your life easier.

You can easily change it to suit your own display types. You will notice that I used a very long delay() in the loop() to show how it is unaffected.

In case you want to know, led8_display() can also be called in the loop to update the display - if you don’t want to enable interrupts.

After your post I begun reading about Interrupts, Timer, ISR and some other of this stuff...

I'm quite ready to begin my first test but, it's incledibile, my worst nightmare now is how to installa the AVR-libc library... seems something I have to compile on my own and, anyway, the documentation isnt very friendly (to my knowledge).

Any link to tutorials about this?

Again thanks.. (and in the meanwhile go on googleing)


An easy way to set up a regular timer interrupt is to use the MsTimer2 library.

No tutorial, no new lib needed: your arduino comes with a full installation of gcc-avr so the code I gave earlier will compile.

The best tutorial you can have is the device datasheet.