Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to "verify" your details, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Electronics ➜ Microprocessors ➜ LED multiplexing with Arduino alone

LED multiplexing with Arduino alone

Postings by administrators only.

Refresh page


Posted by Nick Gammon   Australia  (23,070 posts)  Bio   Forum Administrator
Date Mon 02 Dec 2013 06:27 AM (UTC)

Amended on Mon 02 Dec 2013 08:42 PM (UTC) by Nick Gammon

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

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


14,612 views.

Postings by administrators only.

Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.