Controlling a common anode 7-segment digit with ULN2803 and 74HC595

I recently salvaged two large 7-segment digits from a discarded toy. The partcode is KEM-18011-BSR-2L but i ddi not find much info on Google. After some experiments i determined it was a common anode display and worked out the pinout map (see attachments).

Controlling a common anode display with a MAX7219 is possible by swapping the digit & segment wiring but this also requires a lot of bit juggling in software. The ULN2803 8-channel Darlington driver chip can sink up to 500mA per pin at 50V and is often used to drive relays, motors etc. but can also be used to switch the digit segments.

The driver could be directly wired up to 7 digital outputs on the Arduino but I put a cascading shift register (74HC595) in between. This reduces the number of pins needed to just 3 and allows more digits to be added without taking up more pins.

The shift register also simplifies software control of the LED segments.

/*
  How to control a KEM-18011-BSR-2L common anode LED digit
  MRS 2016/05/29  
  
 Hardware:
 * UNL2803 8 channel Darlington driver
 * 74HC595 shift register
 * large LED digit
 */
 

#define SEGMENT_A 1
#define SEGMENT_B 2
#define SEGMENT_C 4
#define SEGMENT_D 8
#define SEGMENT_E 16
#define SEGMENT_F 32
#define SEGMENT_G 64

// using a pre-defined lookup table saves time and memory!
const byte digits_lot[] = { SEGMENT_A | SEGMENT_B | SEGMENT_C | SEGMENT_D | SEGMENT_E | SEGMENT_F,  
                      SEGMENT_B | SEGMENT_C,
                      SEGMENT_A | SEGMENT_B | SEGMENT_D | SEGMENT_E | SEGMENT_G,
                      SEGMENT_A | SEGMENT_B | SEGMENT_C | SEGMENT_D | SEGMENT_G,
                      SEGMENT_B | SEGMENT_C | SEGMENT_F | SEGMENT_G,
                      SEGMENT_A | SEGMENT_C | SEGMENT_D | SEGMENT_F | SEGMENT_G,
                      SEGMENT_A | SEGMENT_C | SEGMENT_D | SEGMENT_E | SEGMENT_F | SEGMENT_G,
                      SEGMENT_A | SEGMENT_B | SEGMENT_C,
                      SEGMENT_A | SEGMENT_B | SEGMENT_C | SEGMENT_D | SEGMENT_E | SEGMENT_F | SEGMENT_G,
                      SEGMENT_A | SEGMENT_B | SEGMENT_C | SEGMENT_F | SEGMENT_G,
                      };

// NOTE: you can change these digital pin numbers to your preference
//Pin connected to ST_CP of 74HC595
const int latchPin = 10;
// Pin connected to SH_CP of 74HC595
const int clockPin = 12;
////Pin connected to DS of 74HC595
const int dataPin = 11;

void setup() {
  // set digital pins to output 
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  blinkAll( 3, 50 );
}


void loop() {
  short j;

  shiftOut( 0 ); // clears the digit

  for( j = 0; j < 10; j++ )
  {
    print_digit( j );
    delay(500);
  }
  delay(500);
  for( j = 9; j >= 0; j-- )
  {
    print_digit( j );
    delay(100);
  }

}

/* NOTE: quick and dirty without boundary checks! */
void print_digit( byte digit )
{
  shiftOut( digits_lot[ digit ] );
}


void shiftOut(byte dataOut) 
{
  int i=0;
  int pinState;

  digitalWrite(latchPin, 0);
  //clear everything out just in case
  digitalWrite(dataPin, 0);
  digitalWrite(clockPin, 0);

  // 00000001 or "1" means that pin Q0 will be high and SEGMENT_A lights up.
  for (i=7; i>=0; i--)  
  {
    digitalWrite(clockPin, 0);
    if( dataOut & (1<<i) ) 
    {
      pinState= 1;
    }
    else 
    { 
      pinState= 0;
    }
    digitalWrite(dataPin, pinState);
    digitalWrite(clockPin, 1);
    digitalWrite(dataPin, 0);
  }
  digitalWrite(clockPin, 0);
  digitalWrite(latchPin, 1);
}


//blinks the whole register based on the number of times you want to
//blink "n" and the pause between them "d"
//starts with a moment of darkness to make sure the first blink
//has its full visual effect.
void blinkAll(int n, int d) {
  digitalWrite(latchPin, 0);
  shiftOut(0);
  shiftOut(0);
  digitalWrite(latchPin, 1);
  delay(200);
  for (int x = 0; x < n; x++) {
    digitalWrite(latchPin, 0);
    shiftOut(255);
    shiftOut(255);
    digitalWrite(latchPin, 1);
    delay(d);
    digitalWrite(latchPin, 0);
    shiftOut(0);
    shiftOut(0);
    digitalWrite(latchPin, 1);
    delay(d);
  }
}

kem18011_led_segment.jpg

kem-18011.ino (3.17 KB)

kem18011_led_segment.jpg

kem18011_bb.jpg

Mk2_NL:
Controlling a common anode display with a MAX7219 is possible by swapping the digit & segment wiring but this also requires a lot of bit juggling in software.

The shift register also simplifies software control of the LED segments.

But software is why you have a microcontroller.

You now have two ICs and seven resistors for every digit. With the MAX7219 you have one IC and one resistor for a whole eight digits.

The only advantage of the ULN2x03 is that you can use a higher driving voltage - which may be a good thing because it also loses you at least a volt.

Paul__B:
But software is why you have a microcontroller.

that's true.

Anyway, i never claimed this way is better. It's just another way I stumbled across and wanted to share.

Next time use the TPICx595 family.

A 74HC595 with inbuild "ULN2803", but better (mosfet outputs).
Leo..

Wawa:
Next time use the TPICx595 family.

A 74HC595 with inbuild "ULN2803", but better (mosfet outputs).
Leo..

Good tip! In this case however i wanted to "make do" with parts i had lying around. My budget is as close to zero as you can get - hence the salvaging for parts! - and I can't afford to buy something unless i really really need it.