Just wanted to record this little "practice project", in case I need to refer back to it, or its useful to someone else.
Normally, 12 outputs from an Arduino would be needed to drive a 4 digit 7-segment display. 4 outputs for the common connection of each digit, plus 8 outputs for the segments.
This circuit uses the technique known as Charlieplexing, so needs only 9 outputs. In fact, up to 8 7-seg digits could be driven with these same 9 outputs, which would have needed 16 lines using conventional methods, so quite a saving. Its a commonly held belief that when using Charlieplexing, only one led can be lit at any instant in time. This is a myth, and in this circuit, an entire digit (8 leds) can be lit at any instant. This means the multiplexing ratio is only 1:4 (meaning each digit is lit for one quarter of the time) keeping the display reasonably bright.
The downside of Charlieplexing is that the wiring is slightly trickier. As a result, you can't use multi-digit 7-seg displays, you have to use individual ones. In multi-digit displays, the segments share common pins for both anodes and cathodes, making the wiring needed for Charlieplexing impossible.
Charlieplexing makes use of the fact that each Arduino output can be individually set to one of 3 states - HIGH, LOW and "High-Z" (achieved by setting the pinMode to INPUT). This makes the sketch a little trickier also, but not too bad.
9 series resistors are needed to limit the current and prevent damage to the led segments and the Arduino outputs. I used 330R because that's what I had, but a much lower value could be used, e.g. 100R, making the display brighter and visible even in sunlight.
A single Arduino output can drive a single LED segment easily, but it can't drive a whole digit - that much current would damage the Arduino, unless much higher series resistors were used, but that would make the display very dim.
To overcome the above, 4 everyday NPN transistors are also needed to drive the common anodes of the displays I used. (If using common cathode displays, PNP transistors would be needed, along with changes to the circuit and the sketch). I used BC337 transistors because I had some, but almost any of the common NPN types will probably do, as long as they can handle the current for all 8 segments (the 8th segment being the decimal point, of course). Some common NPNs can only handle a max of 100mA. that would be OK with 330R series resistors, but if using 100R, that might well be too much current. The BC337 can handle up to 800mA, so no problems.
The NPN transistors are used in a configuration known as "emitter follower". This means the transistor does not need to amplify the voltage, only the current provided by the Arduino output. It also means that only a small current will naturally flow from the Arduino output through the base of the transistor, so no extra resistors are needed to limit that current. For example, if 160mA were flowing through the collector and emitter of the transistor, and its gain was around 100, only about 1.6mA would flow through the base from the Arduino.
So, how does Charlieplexing work?
- When an Arduino output is LOW, current can flow through one of the segments, lighting it, and through the Arduino to ground.
- When an Arduino output is HIGH, a (small) current can flow out of the Arduino into the base of a transistor, switching the transistor on, so it can provide current to the common anodes of a digit.
- When an Arduino output is "High-Z" (i.e. actually an INPUT), no current flows into or out of the Arduino, so nothing happens.
So the same output can control both anodes and cathodes, and this is why Charlieplexing uses fewer outputs than conventional methods. But hang on a minute, you say, if an output is HIGH, switching on a digit, it can't also be LOW, so one of the segments in the digit can't be lit? This is where the 9th output comes in. The wiring is done so that the 9th output takes the place of the whichever output driving the common anodes for that digit, enabling all segments to be lit (if needed, depending on what digit is being displayed).
// Charlieplex 4 x 7-seg displays
// PaulRB
// Sept 2013
const byte digitalLine[9] = {4, 5, 6, 7, 9, 10, 11, 12, 8};
byte digitValue[4];
const byte segmentPattern[10] = {
B01111110,
B01001000,
B00111101,
B01101101,
B01001011,
B01100111,
B01110111,
B01001100,
B01111111,
B01101111 };
void setup() {
}
void loop() {
// Display elapsed time in tenths of a second.
int x = millis() / 100;
// Turn the number to be displayed into 4 digits
digitValue[3] = x % 10;
digitValue[2] = x / 10 % 10;
digitValue[1] = x / 100 % 10;
digitValue[0] = x / 1000 % 10;
// Briefly light each digit
for (byte digit = 0; digit < 4; digit++) {
// Set up the segments for this digit
for (byte segment = 0; segment < 8; segment++) {
byte useSegment = segment;
// If this segment's line also happens to be the line controlling
// the whole digit, the 9th line will be wired up instead
if (segment == digit) useSegment = 8;
// look up the value for this segment for this digit
if (bitRead(segmentPattern[digitValue[digit]], segment) == 1) {
// the segment needs to be on
pinMode(digitalLine[useSegment], OUTPUT);
digitalWrite(digitalLine[useSegment], LOW);
}
else {
// the segment needs to be off
pinMode(digitalLine[useSegment], INPUT);
}
}
// all segments now ready, so switch on the digit
pinMode(digitalLine[digit], OUTPUT);
digitalWrite(digitalLine[digit], HIGH);
// Wait a moment
delay(1);
// Switch the digit off again
pinMode(digitalLine[digit], INPUT);
}
}
Pictures and Schematic to follow...
Paul