This guide will explain the basics of controlling multiple 7 segment displays using an Arduino and a couple of shift registers. This project is ideal for displaying numerical data or managing a set of LEDs. As a beginner, like me, I had no clue on how to tackle this task. After trying out different methods and overcoming obstacles, I now have a stronger understanding of multiplexing and the best strategies for utilizing it with an Arduino.
Firstly, what is multiplexing exactly? What do you think of Charlieplexing? Do discrepancies exist?
In actuality, they are the same… Charlieplexing takes multiplexing to a higher level. Both approaches target reducing the quantity of microcontroller pins needed and lowering power demands substantially. However, this means sacrificing either time or brightness.
Multiplexing entails showing all the digits or rows of LEDs at once. After a certain period, the entire digit or row is turned off and the next digit/row is turned on, and so on… Simple!
However, Charlieplexing is slightly more complex as it goes beyond the basics of multiplexing. Instead of turning on or off an entire number or line, only one segment or individual LED is turned on or off. After a period of time, the segment/LED turns off and the next segment/individual LED is turned on consecutively, continuing this pattern. After finishing one digit/row, the sequence starts again with the next digit/row. When charlieplexing a 7-segment display, a maximum of 20mA is used, as opposed to 160mA in multiplexing, since only one segment is active at a time. The main drawback is the slowdown in displaying data and reduction in luminosity because the system must cycle through all 7 segments + decimal or each LED before advancing to the next digit or row. When you add more displays/LEDs, you might notice a slight flicker.
Use the information above to compare multiplexing and charlieplexing. Can you notice how it takes more time to show a number using charlieplexing?
Before beginning your multiplexing project, ensure careful planning by conducting extensive research. If not, you will end up wasting both time and money and become so frustrated that you will want to tear your hair out.
Step 1: Plan the hardware
To multiplex 7 segment displays, you will need the following:
1. 7 segment displays — I’m using 3 x 4.0 Inch Super Red 7 Segments from Kingbright (SA40-19SRWA)
I strongly suggest you purchase COMMON ANODE displays. Common anode means all the anodes (+) pins are connected. You apply + voltage to the anode and use shift registers to ground the segments and form a complete circuit. Very simple!
However, with common cathode, all the ground (-) pins are connected. You then use shift registers to divert power to the anodes of the segments. However, the problem with this setup, as I’ve learned the hard way, is that you need to worry about sourcing AND sinking current. Most uControllers and shift registers cannot source nor sink a lot of current. Otherwise, you’d burn it out. If you require more voltage or current, you’ll then need to worry about transistors or darlington arrays (external drivers) since you’re using shift registers to tell them which segments need power (high voltage or current) and when to ground it. In other words, the hardware and code get more complicated and drives up cost.
2. Microcontroller
I strongly suggest getting an Arduino. The environment is much more intuitive and there is a huge pool of resources out there if you get stuck. If you’re prone to making mistakes, get the Ruggeduino. It’s only $10 more than Arduino Uno and protects you and your precious uController from stupid mistakes.
3. Serial-In Parallel-Out Shift Register
If you have the money, buy from the TPIC6x595 or TPIC6x596 family of shift registers by Texas Instruments. I use the TPIC6B596 in this instructable. The difference between its siblings (A, B, and C series) is the current handling capacity. In addition, the 596 family provides better reliability in cascading applications. When choosing shift registers, always make sure you do not exceed their current-handling limits.
Side notes:
– I would avoid the popular 74HC series as it can only source/sink a max of 70mA through the chip and cannot handle high voltages.
– I would also avoid using the common cathode / MAX7219/7221 setup with high voltage displays. Trust me… It’s not worth it! You don’t want to know the trouble I’ve been through with this setup. Even though there’s a good library out there, it’s best to understand and have control of the underlying mechanism behind shift registers and multiplexing.
4. Regulated DC Power Adapter
If you intend to use high-power displays, make sure to purchase a regulated dc adapter with a voltage higher than the forward voltage of your display. A DC adapter that is regulated ensures consistent voltage regardless of the load. Ensure that its current rating exceeds the necessary minimum. Typically, ratings exceeding 500mA are sufficient (more is preferable).
5. Resistors
Resistors are necessary in order to reduce the current passing through the LEDs.
The equation for determining necessary resistor is:
(Difference between Supply Voltage and Minimum or Typical Forward Voltage per segment) divided by Desired forward current in Amps.
Make sure to choose a current slightly lower than the maximum forward current specified in the datasheet to enhance the longevity of the LEDs. Remember to perform a distinct calculation for the decimal point, as it may have a lower forward voltage needed.
Also, be careful with datasheets of large displays. If they show low forward voltages (< 5V), that rating may be for the individual LED in a segment rather than the entire segment. So, for example, if there are 5 LEDs in series per segment and the datasheet shows a forward voltage of 2.6V for a 5 inch display, you probably need to multiply it by 5 to get the correct forward voltage for the entire segment.Here is one such example. It’s more complicated if it’s series/parallel arrangement which is beyond the scope of this instructable.
6. Breadboard and jumper wires
I recommend buying large solderless breadboards and a lot of jumper wires of various configurations (Male to Male, Male to Female). They are also known as dupont cables.
Step 2: Draw the Schematic
Now that you’ve got your hardware, you need to lay down and draw the connections between all the components. It may seem overwhelming, but it’s real easy once you understand the general pattern.
Before we begin, we need to know what each pin of the TPIC6B596 shift register does and where it connects to (I’ll also give aliases that other manufacturers use):
Vcc: The chip’s power supply (Connects to +5V)
SER IN: The input pin where data is received from the uController. (Connects to MOSI or Arduino user-defined pin). Aka Serial In, SER, MOSI, DIN…
Drain 0 to 7: The pins where segments may connect to ground (Connects to segments A-DP respectively). Aka Source/Sink outputs, Qa-Qh, D0-D7…
SRCLR: When high (+5), data can be transferred to the output buffer. When low (0V), all registers are cleared. (Connects to Vcc or +5 so it’s always high). Aka Serial Clear, MR (master reset)
G(bar): Enables/Disables drain pins. When low (0V), register data is transparent to the output buffer. When high (+5), drain pins are disabled but data is retained in the storage register. (Connects to Arduino pin with a pull-up resistor to +5V. We do this to ensure that no garbage will be seen during boot up.) Aka OE (output enable)
SER OUT: The output pin where data is transmitted to the next shift register (Connects to SER IN of the next shift register). Aka Serial Out, DOUT, Qh’…
SRCK: The pin where the clock signal is received. (Connects to SCK or Arduino user-defined pin and all SRCK pins). Aka Serial Clock, CLK, SRCLK
RCK: The pin where the latch signal is received. (Connects to SS or Arduino user-defined pin and all RCK pins). Aka Register Clock, RCLK, LATCH, SS, LOAD, CS
GND: Connects to ground (0V). (Note that there are 3 ground pins. You can connect one of them to ground as they are internally connected. If you have problems later on, try connecting each to ground). Aka 0V, PGND, LGND
Now that you know where each pin connects to, you can now draw up your schematic. The schematic I give is simpler and easier to understand than others out there. However, you may need to change the resistor values and voltage going to the anodes of your displays.
Step 3: Time to Code!
At this stage, you probably have a strategy for programming your program, motivated by your diagram.
Here is a simple example demonstrating multiplexing by sequentially displaying the number 456 with a decimal point. I also made sure that it can easily adjust to various configurations you may have. The origin can also be accessed for download at the bottom. Ensure you understand the code before transferring it to a fresh sketch by copying and pasting. Afterwards, make sure to adjust the variables and pin assignments as needed for your particular situation. Once you are done, send the file over to the Arduino.
An essential factor to take into account is the manner in which the data is being transferred. If you remember my diagram, the shift register on the left side will take in the first byte and release it through the SER OUT when another byte arrives. So, in order to shift 11111111 to the rightmost digit, you must first shift out 11111111, then 00000000, and finally 00000000. Don’t worry, the code handles this automatically.
Rephrase the text utilizing the same input language and maintaining the original word count:
START CODE
/*
Code for interfacing with 7 segment displays
using the multiplexing method
and the TPIC6B595 Shift Register (1 per digit)
By K.O.
*/
//Pin Assignments (You should change these)
const int CLK = 9; //Connected to TPIC pin 13: SRCLK (aka Clock)
const int LATCH = 10; //Connected to TPIC pin 12: RCLK (aka Latch/load/CS/SS…)
const int OE = 11; //Connected to TPIC pin 9: OE (Output Enable)
const int DOUT = 12; //Connected to TPIC pin 3: SER (aka MOSI)
//Number Patterns (0-9)
//***Drains 0-7 must be connected to segments A-DP respectively***
const byte numTable[] =
{
B11111100,
B01100000,
B11011010,
B11110010,
B01100110,
B10110110,
B10111110,
B11100000,
B11111110,
B11110110
};
//Global Variables
int numDevices = 1; //The number of x-digit display modules you plan to use
int maxDisplays = 3; //The maximum displays that could be accommodated (see note 1)
int maxDigits = 3; //The maximum digits you plan on displaying per display module (each SR can handle a max of 8 digits)
int SRData[3][3]; //The storage location for the digit information. We must specify a fixed array at compile time (see note 2)
boolean debug = true; //Change to true to print messages
int delayTime = 1000; //Optional (just for demonstrating multiplexing)
/*
Notes
1. It is recommended to use an external power supply to avoid oversource/sinking the microcontroller
or if you need to power high voltage, high current displays. This code will turn on/off all segments in a digit for ***each*** display.
So, if using 2x 3-digit displays all displaying an 8 + DP, the max consumption will be:
20mA (desired forward current) * 8 (segments that are on) * 2 (displays showing identical info) = 320mA
2. The first dimension should equal maxDisplays. The second dimension should equal the number of digits
*/
void setup()
{
Serial.begin(9600);
//Set pin modes
pinMode(CLK,OUTPUT);
pinMode(LATCH,OUTPUT);
pinMode(DOUT, OUTPUT);
pinMode(OE, OUTPUT);
//7-Segment Display Init
digitalWrite(OE,LOW); //Enables SR Operation
initializeSRData(); //Prepares SR and clears data on serial line
//Test
setDigit(0,0,4,true);
setDigit(0,1,5,true);
setDigit(0,2,6,true);
}
void loop()
{
refreshDisplay(); //Cycles through all displays and digits
}
//==========BEGIN SR Functions==========
void initializeSRData()
{
//Display Scanner (Iterates through each display module)
digitalWrite(LATCH,LOW); //Tells all SRs that uController is sending data
for(int dispID = 0; dispID < maxDisplays; dispID++)
{
//Digit Scanner (Iterates through each SR (digit) in a display module)
for(int digit = 0; digit < maxDigits; digit++)
{
//Clears any garbage on the serial line
shiftOut(DOUT,CLK,LSBFIRST,0); //Shift out 0s to all displays
SRData[dispID][digit] = 0; //Stores a 0 for each digit so its completely off
}
}
digitalWrite(LATCH,HIGH); //Tells all SRs that uController is done sending data
}
void printSRData()
{
if(!debug)
return;
Serial.println(“Printing SR Data…”);
//Display Scanner
for(int dispID = 0; dispID < maxDisplays; dispID++)
{
Serial.print(“Display # “);
Serial.println(dispID);
//Digit Scanner
for(int digit = 0; digit < maxDigits; digit++)
{
Serial.print(“Digit “);
Serial.print(digit);
Serial.print(“: “);
Serial.println(SRData[dispID][digit],BIN);
}
Serial.println();
}
}
void setDigit(int dispID, int digit, int value, boolean dp)
{
//Parameter checker
if(dispID < 0 || dispID >= numDevices)
{
Serial.println(“dispID OoB!”); //OoB = Out of bounds
return;
}
if(digit < 0 || digit > maxDigits)
{
Serial.println(“digit OoB!”);
return;
}
if(value < 0 || value > 9)
{
Serial.println(“Invalid value!”);
return;
}
value = numTable[value];
//Toggle dp if needed
if(dp)
value |= B00000001; //Turns on the first binary digit (segment) using an OR bitmask
//Store the digit
SRData[dispID][digit] = value;
if(debug)
printSRData();
}
void setSegments(int dispID, int digit, byte value)
{
//Parameter checker
if(dispID < 0 || dispID >= numDevices)
{
Serial.println(“dispID OoB!”);
return;
}
if(digit < 0 || digit > maxDigits)
{
Serial.println(“digit OoB!”);
return;
}
if(value < 0 || value > 255)
{
Serial.println(“Invalid byte!”);
return;
}
//Store the digit
SRData[dispID][digit] = value;
if(debug)
printSRData();
}
void clearDisplay(int dispID)
{
initializeSRData();
refreshDisplay();
}
void refreshDisplay()
{
//Digit Scanner
for(int digit = 0; digit < maxDigits; digit++)
{
//Display Scanner
digitalWrite(LATCH,LOW);
for(int dispID = numDevices – 1; dispID >= 0; dispID–)
{
//Pre-Digit blanker (shifts out 0s to correct digits before sending segment data to desired digit)
for(int blanks = (maxDigits – 1 – digit); blanks > 0; blanks–)
shiftOut(DOUT,CLK,LSBFIRST,0);
shiftOut(DOUT,CLK,LSBFIRST,SRData[dispID][digit]);
//Post-Digit blanker (shifts out 0s to remaining digits)
for(int blanks = digit; blanks > 0; blanks–)
shiftOut(DOUT,CLK,LSBFIRST,0);
}
digitalWrite(LATCH,HIGH);
//Demonstrates multiplexing operation
delay(delayTime);
delayTime -= 10;
if(delayTime <= 0)
delayTime = 0;
}
}
//==========END SR Functions==========
END CODE
For more detail: Multiplexing 7 Segment displays with Arduino and Shift Registers