Posted by
| Nick Gammon
Australia (23,070 posts) Bio
Forum Administrator |
Message
| This post describes how to multiplex a 4-digit 7-segment LED display without using external hardware (like a MAX7219 or 74HC595 chip).
It works by sourcing current into the segments and sinking it from the digits (or the other way around, depending on whether it is common anode or common cathode).
You connect up the seven segments to pins 2 to 8, each one via a 1.2k resistor, as follows:
- Segment A to pin 2
- Segment B to pin 3
- Segment C to pin 4
- Segment D to pin 5
- Segment E to pin 6
- Segment F to pin 7
- Segment G to pin 8
Then you connect up the (up to) 4 digits to pins 9 onwards, like this:
- Digit 1 to pin 9
- Digit 2 to pin 10
- Digit 3 to pin 11
- Digit 4 to pin 12
The 1.2k resistor (per segment) was chosen to keep the sinking current to within spec for the Arduino output pin. If all 8 segments are on it will need to sink 20 mA as follows:
(5v - 2v) / 1200 * 7 = 0.0175 = 17.5 mA
That is slightly under 20 mA, however the next standard value of resistor (1k) would have been slightly over 20 mA.
This design works with both common anode and common cathode LED displays. Just adjust the sketch by changing one line as shown below.
This is what a typical LED module looks like:
Segment letters:
Example code
// Demonstration of LED multiplexing with Arduino
// Author: Nick Gammon
// Date: 2 December 2013
// Put a suitable resistor in series with each segment LED (eg. 180 ohm)
const byte PATTERN_COUNT = 16;
const byte SEGMENTS = 7;
const byte DIGITS = 4;
const byte columnPins [SEGMENTS] = { 2, 3, 4, 5, 6, 7, 8 }; // a, b, c, d, e, f, g
const byte digitPins [DIGITS] = { 9, 10, 11, 12 }; // DIG1, DIG2, DIG3, DIG4
#define COMMON_ANODE true // make false for common cathode LEDs
#if COMMON_ANODE
// For common ANODE:
const byte SEGMENT_ON = LOW;
const byte SEGMENT_OFF = HIGH;
const byte DIGIT_ON = HIGH;
const byte DIGIT_OFF = LOW;
#else
// For common CATHODE:
const byte SEGMENT_ON = HIGH;
const byte SEGMENT_OFF = LOW;
const byte DIGIT_ON = LOW;
const byte DIGIT_OFF = HIGH;
#endif
// extra segment patterns (you can add more)
const byte SHOW_HYPHEN = 0x0A;
const byte SHOW_E = 0x0B;
const byte SHOW_H = 0x0C;
const byte SHOW_L = 0x0D;
const byte SHOW_P = 0x0E;
const byte SHOW_BLANK = 0x0F;
const PROGMEM byte digitSegments [PATTERN_COUNT] =
{
0b1111110, // 0
0b0110000, // 1
0b1101101, // 2
0b1111001, // 3
0b0110011, // 4
0b1011011, // 5
0b1011111, // 6
0b1110000, // 7
0b1111111, // 8
0b1111011, // 9
0b0000001, // 0x0A -> -
0b1001111, // 0x0B -> E
0b0110111, // 0x0C -> H
0b0001110, // 0x0D -> L
0b1100111, // 0x0E -> P
0b0000000, // 0x0F -> blank
};
volatile byte numberToShow [DIGITS] = { SHOW_H, SHOW_E, SHOW_L, 0 }; // HELO
// timer Interrupt Service Routine (ISR) to update the LEDs
ISR (TIMER2_COMPA_vect)
{
static byte digit = 0;
byte thisDigit = numberToShow [digit];
// check for out of range, if so show a blank
if (thisDigit >= PATTERN_COUNT)
thisDigit = SHOW_BLANK;
// turn off old digit
for (byte i = 0; i < DIGITS; i++)
digitalWrite (digitPins[i], DIGIT_OFF);
// set segments
for (byte j = 0; j < SEGMENTS; j++)
digitalWrite (columnPins [j], // which segment pin
(pgm_read_byte (digitSegments + thisDigit) // get bit pattern
& bit (SEGMENTS - j - 1)) // see if set or not
? SEGMENT_ON : SEGMENT_OFF); // set appropriately (HIGH or LOW)
// activate this digit
digitalWrite (digitPins [digit], DIGIT_ON);
// wrap if necessary
if (++digit >= DIGITS)
digit = 0;
} // end of TIMER2_COMPA_vect
void setup()
{
for (byte i = 0; i < SEGMENTS; i++)
pinMode(columnPins[i], OUTPUT); // make all the segment pins outputs
for (byte i = 0; i < DIGITS; i++)
pinMode(digitPins[i], OUTPUT); // make all the digit pins outputs
// set up to draw the display repeatedly
// Stop timer 2
TCCR2A = 0;
TCCR2B = 0;
// Timer 2 - gives us a constant interrupt to refresh the LED display
TCCR2A = bit (WGM21) ; // CTC mode
OCR2A = 63; // count up to 64 (zero relative!!!!)
// Timer 2 - interrupt on match at about 2 kHz
TIMSK2 = bit (OCIE2A); // enable Timer2 Interrupt
// start Timer 2
TCCR2B = bit (CS20) | bit (CS22) ; // prescaler of 128
delay (1000); // give time to read "HELO" on the display
} // end of setup
void loop ()
{
unsigned long elapsedSeconds = millis () / 1000;
char buf [10];
sprintf (buf, "%04ld", elapsedSeconds);
for (byte i = 0; i < DIGITS; i++)
numberToShow [i] = buf [i] & 0x0F;
} // end of loop
[EDIT] Improved code on 3 December 2013 to put segment patterns into a single byte (per digit) and leave them in PROGMEM to save RAM usage.
If you are using common cathode LEDs, change the line in bold above from:
#define COMMON_ANODE true
to:
#define COMMON_ANODE false
This sketch uses Timer 2 to repeatedly update the display on a timer interrupt. The main loop can be doing other things, and update the numberToShow array from time to time to indicate what should be displayed by the LED module. In the example above I am showing the elapsed time in seconds.
Example in operation
Other methods
You can use the MAX7219 chip to move much of the work to another chip. This saves having to use lots of resistors, and lets your processor concentrate on other work (and uses less pins).
http://www.gammon.com.au/forum/?id=11516
You can also multiplex using 74HC595 chips as shown here:
http://www.gammon.com.au/forum/?id=12298 |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|