Hubbry Logo
CharlieplexingCharlieplexingMain
Open search
Charlieplexing
Community hub
Charlieplexing
logo
8 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Charlieplexing
Charlieplexing
from Wikipedia
Illustrates how a diagonal array enables nearly twice as many unique intersections as an x/y array.

A Charlieplexed digital clock which controls 90 LEDs with 10 pins of a PIC 16C54 microcontroller.

Charlieplexing (also known as tristate multiplexing, reduced pin-count LED multiplexing, complementary LED drive and crossplexing) is a technique for accessing a large number of LEDs, switches, micro-capacitors or other I/O entities, using relatively few tri-state logic wires from a microcontroller. These I/O entities can be wired as discrete components,[1][2] x/y arrays,[3][4] or woven in a diagonally intersecting pattern to form diagonal arrays.[5]

Etymology

[edit]

Although the technique was introduced in 2001 by Maxim Integrated, the name "Charlieplexing", however, first occurred in a 2003 application note.[6] It was named after Charles "Charlie" M. Allen, an applications engineer of MAX232 fame,[7][8][9] who had proposed this method internally.[when?]

How and why to use Charlieplexing

[edit]

The simplest way to address a single pixel (or input button) is to run a wire out to it and another wire back to ground, but this requires a lot of wiring. A slight improvement is to have everything return on a common ground, but this still requires one wire (and one pin on the microcontroller) for each pixel or button. For an X by Y array, X*Y pins are required.

With tri-state logic pins (high, low, disconnected), matrix wiring needs only X+Y pins and wires. Each X and each Y take turns being on vs being disconnected; the disadvantage is that each light is only powered at most 1/(X*Y) of the time. If there is enough fan-out, the Y pins can be left always on, and all checked in parallel. The refresh can then happen every 1/X of the time, but the X wires each need to pass enough current to light up Y lights at once.

Charlieplexing is a further improvement on matrix wiring, which enables the I/O entities (LEDs, switches etc.) to be connected between any two microcontroller I/O pins. Instead of X horizontal wires meeting Y vertical wires, every wire meets every other wire. Assuming diodes are used for the connections (to distinguish between wire A going to wire B vs wire B going to wire A), Charlieplexing needs only about half as many pins as a conventional matrix arrangement, at the cost of more complicated mapping. Alternatively, the same number of pins will support a display nearly four times (doubling in both directions) as large.

Wiring options with 4 pins.

Current flows from the Vcc (high) voltage pin to the GND (low) voltage pin. All remaining pins are Hi-Z (high impedance) so no current flows through them.

Matrix wiring uses pins 1 and 2 only for X values, and uses pins 3 and 4 only for Y values.
Switch CharliePlexing means a connection can be made or broken between any two pins.
LED CharliePlexing also notices which direction the current flows.
Vcc GND Hi-Z Matrix Switch LED
1 2 3,4 No Yes Yes
2 1 3,4 No duplicate Yes
1 3 2,4 Yes Yes Yes
3 1 2,4 duplicate duplicate Yes
1 4 2,3 Yes Yes Yes
4 1 2,3 duplicate duplicate Yes
2 3 1,4 Yes Yes Yes
3 2 1,4 duplicate duplicate Yes
2 4 1,3 Yes Yes Yes
4 2 1,3 duplicate duplicate Yes
3 4 1,2 No Yes Yes
4 3 1,2 No duplicate Yes

For example, with 4 I/O pins, standard x/y matrix multiplexing distinguishes only 4 positions. Pins representing the same direction (such as 1 and 2) can never pair with each other.

With Charlieplexing, each pin can pair with any other pin, so those same 4 I/O pins have 6 unique pairings: all those available to matrix multiplexing, but also (1 to 2) and (3 to 4).

Because the microcontroller can send current in either direction, and diodes can filter out one direction, each connection can address two diodes separately. In this example, 4 pins with six connections can identify 12 independent diodes. Doubling connections with diodes isn't unique to Charlieplexing, but is rarely done unless I/O pins are scarce enough to at least consider Charlieplexing.

Although Charlieplexing is more efficient in its use of I/O lines, the mapping from I/O lines to a physical location is usually more complicated.

Other issues that affect standard multiplexing but are exacerbated by Charlieplexing include:

  • consideration of current requirements and the forward voltages of the LEDs.
  • a requirement to cycle through the in-use LEDs rapidly so that the persistence of the human eye perceives the display to be lit as a whole. Multiplexing can generally be seen by a strobing effect and skewing if the eye's focal point is moved past the display rapidly.

Origin

[edit]

The Charlieplexing technique was introduced[6] by Maxim Integrated in 2001[10] as a reduced pin-count LED multiplexing scheme in their MAX6951 LED display driver.[10][6]

Also in 2001, when the name "Charlieplexing" became common, Don Lancaster illustrated the method as part of his musings about the "N-connectedness" problem,[11] referring to Microchip Technology,[11] who had already discussed it as "complementary LED drive technique" in a 1998 application note[12] and would later include it in a tips & tricks booklet.[13]

While Microchip did not mention the origin of the idea, they might have picked it up in the PICLIST, a mailing list on Microchip PIC microcontrollers, where, also in 1998, Graham Daniel[14][15] proposed it to the community as a method to drive rows and columns of bidirectional LEDs. Daniel at the time had created simple circuits with PIC 12C508 chips driving 12 LEDs off 5 pins with a mini command set to set various lighting displays in motion.[14][15]

The method, however, was known and utilized by various parties much earlier in the 1980s, and has been described in detail as early as in 1979 in a patent by Christopher W. Malinowski, Heinz Rinderle and Martin Siegle of the Department of Research and Development, AEG-Telefunken, Heilbronn, Germany for what they called a "three-state signaling system".[16]

Reportedly, similar techniques were already in use as early as 1972 for track signaling applications in model railroading.[17][citation needed]

Display multiplexing is very different from multiplexing used in data transmission, although it has the same basic principles. In display multiplexing, the data lines of the displays are connected in parallel to a common databus on the microcontroller. Then, the displays are turned on and addressed individually. This allows the use of fewer I/O pins than it would normally take to drive the same number of displays directly. Here, each "display" could, for instance, be one calculator digit, not the complete array of digits.

With traditional multiplexing I/O pins can drive a maximum of LEDs, or listen to that many input switches. Charlieplexing can drive LEDs, or listen to buttons even if directionality is not enforced by a diode.

Tri-state multiplexing (Charlieplexing)

[edit]
A symmetric layout of Charlieplexed LEDs. On left, 3 pins drive 6 LEDs arranged in a triangle. On right, 4 pins drive 12 LEDs arranged in a tetrahedron.

The Charlieplexing configuration may be viewed as a directed graph, where the drive pins are vertices and the LEDs are directed edges; there is an outward-pointing edge connected from each vertex to each other vertex, hence with n drive pins there are (n)(n-1) total edges (a pronic number). This equates to n pins being able to drive n2 − n segments or LEDs.

Pins LEDs
2 2
3 6
4 12
5 20
6 30
7 42
8 56
9 72
10 90
11 110
12 132
13 156
14 182
15 210
16 240
20 380
24 552
32 992
40 1,560
48 2,256
56 3,080
64 4,032
n n2 − n

If the number of LEDs (L) is known, then the number of pins (n) can be found from the equation: , the result being rounded to the nearest whole number.[nb 1]

Example: If L = 57, then √L = 7.549, and 1 + √L = 8.549; the nearest whole number to this is 9, so 9 pins are needed to drive 57 LEDs (9 pins could drive up to 72 LEDs, but 8 pins could drive only 56 LEDs at most).

If L = 56, then √L = 7.483, and 1 + √L = 8.483; the nearest whole number to this is 8, so 8 pins are needed to drive 56 LEDs.

Origin of the (n2 − n) equation in Charlieplexing
Six conductors allow nine LEDs to be individually controlled in a standard x/y multiplexed array, but 30 LEDs in a "Charlieplexed" array.

Unlike in a traditional x/y multiplexed array, where a sub-set of conductive elements crosses a different sub-set of conductive elements, in a "fully Charlieplexed" multiplexed array, each conductive element crosses every other conductive element.

Six (n) conductive elements in a standard x/y multiplexed array forms a maximum of nine ((n / 2)2) unique intersections (see figure on far left).

The other diagrams also show six (n) conductive elements, but here all six elements cross over themselves, forming a multiplexed array of 36 (n2) intersections. LEDs are shown placed at every intersection. However, each conductor also crosses itself at the diagonal. Horizontal conductor 1 crosses vertical conductor 1, horizontal conductor 2 crosses vertical conductor 2, etc. This means that six of these LEDs are short-circuited (e.g. D1 and D5 are short-circuited). The six (n) diagonal LEDs will, therefore, never light up, because no voltage can ever develop across them, so (n) has to be subtracted from the total. There is no point in installing these LEDs (they are simply included here for illustrative purposes).

This leaves 30 LEDs (n2 − n) that can be uniquely addressed and lit up independently.

Conductor "a" crossing conductor "b" is distinguishable from conductor "b" crossing conductor "a" because LED polarity is reversed. For example, when conductor 3 is positive and conductor 2 is negative, current flows through, and lights up LED D8, but when conductor 3 is negative and conductor 2 is positive, current flows through, and lights up LED D9.

These reverse polarity LED pairs are called complementary pairs. This diagram has 15 complementary pairs, allowing 30 LEDs to be lit independently.

n I/O forms a n (n - 1) Charlieplexed "off-set" x/y LED array.

The 6 unusable diagonal LEDs can be conveniently replaced by actual bidirectional shortcuts (so that there's no longer need to set up the interconnection lines grouped on the left and bottom of the diagrams, to drive the bottom input of vertical connectors from the matching left input of horizontal connectors).

By adjusting diagonally the form of horizontal and vertical connectors along the short-circuited main diagonal of the original matrix, this can be easily transformed into an array of 5 × 6 or 6 × 5 LEDs arranged on a regular grid.

A similar pattern could be used for a 10 × 11 matrix that could be used to drive up to 110 keys (including a few indicator LEDs) on a modern PC keyboard, where each key switch includes a small serial diode or LED, so that only 11 pins would be needed to individually control all of them (these individual diodes or LEDs inside each key switch would also avoid all common and undesirable "ghosting" effects, that are hard to eliminate completely when an arbitrary number of keys at any position are pressed at the same time).

Charlieplexing can also be used to significantly reduce the number of controlling pins for much larger matrixes, such as modern digital displays with high resolution. E.g. for a 4K RGB display at 3840 × 2160, this requires more than 8 millions individually addressable pixels, each featuring at least 3 colored LEDs or LCD cells, for a total of nearly 25 millions LEDs or LCD cells. Using a conventional x/y multiplexing would require at least (3840 + 2160 × 3) = 10320 controlling pins and many selection chips for controlling rows and columns all around the panel of LEDs or LCD cells. But with Charlieplexing, this can be reduced to only 63 controlling pins for the selection gate of display columns, plus 46 × 3 controlling pins for the selection and power-activation of RGB display rows, by a single transistor for each row or column (possibly with an extra common shielding ground to limit their mutual coupling); these controlling pins can easily fit around the output pins of one or two controller chips, even if we add the few additional pins needed on the controller for power, ground, clocks and I/O buses, surface-mounted with a high density and low cost on a single-layer PCB, and no need of complex routing and interconnection holes between layers; a dual layer is needed only for the basic Charlieplexing matrix mounted on borders of the panel itself.

Positions in the Charlieplexed matrix are not reduced to be just LEDs or diodes, they can be filled as well by two pins of a transistor (including its gate pin) so that its third pin is used as output to further control other devices, such as the horizontal and vertical selection lines of a large flat display panel (in that case, the two Charlieplexed matrices of transistors controlling and activating the rows or columns of the panel will be smartly arranged all along a border of that panel).

Complementary drive

[edit]

Charlieplexing in its simplest form works by using a diode matrix of complementary pairs of LEDs. The simplest possible Charlieplexed matrix would look like this:

Minimal 2-pin configuration for identical LEDs.
2-pin configuration for different LEDs.

By applying a positive voltage to pin X1 and grounding pin X2, LED 1 will light. Since current cannot flow through LEDs in reverse direction at this low voltage, LED2 will remain unlit. If the voltages on pin X1 and pin X2 are reversed, LED 2 will light and LED1 will be unlit.

The Charlieplexing technique does not actually make a larger matrix possible when only using two pins, because two LEDs can be driven by two pins without any matrix connections, and without even using tri-state mode. In this two-LED example, Charlieplexing would save one ground wire, which would be needed in a common 2-pin driver situation.

However, the 2-pin circuit serves as a simple example to show the basic concepts before moving on to larger circuits where Charlieplexing actually shows an advantage.

Expanding: tri-state logic

[edit]

If the circuit above were to be expanded to accommodate three pins and six LEDs, it would look like this:

3-pin configuration for identical LEDs.
3-pin configuration for different LEDs.

This presents a problem, however: In order for this circuit to act like the previous one, one of the pins must be disconnected before applying charge to the remaining two. If, for example, LED 5 was intended to be lit, X1 must be charged and X3 must be grounded. However, if X2 is also charged, LED 3 would illuminate as well. If X2 was instead grounded, LED1 would illuminate, meaning that LED 5 cannot be lit by itself. This can be solved by utilizing the tri-state logic properties of microcontroller pins.

Microcontroller pins generally have several possible states:

  • "output": the pin "drives". It can drive "high" (often 5 V) or "low" (usually 0 V).
  • "input": the pin does not intentionally drive at all, but instead senses what some other device (or pin) is driving. It may draw current while doing so.
  • "high-Z": the pin is in a high-impedance state. Essentially, it is disconnected from the circuit; it will neither drive nor draw any current.

This allows the circuit to see any number of pins connected at any time, simply by changing the state of the pins. In order to drive the six-LED matrix above, the two pins corresponding to the LED to be lit are connected to 5 V (I/O pin "high" = binary number 1) and 0 V (I/O pin "low" = binary 0), while the third pin is set in its high-impedance state.

In doing so, current leakage out of the third pin is prevented, ensuring that the LED which should be lit is the only one lit.

Because the a diode (such as a LED) reduces the voltage available in series, current will not flow across alternate paths (an alternate 2-LED path exists for every pair of pins in the 3-pin diagram, for example), so long as the voltage drop in the desired LED path is less than the total voltage drop across each string of alternative LEDs. However, in the variant with individual resistors this voltage-regulating effect does not affect the alternative paths so all LEDs used will not have to be lit with half the supply voltage applied because this variant does not benefit from the voltage-regulating effect of the desired path LED.

By using tri-state logic, the matrix can theoretically be expanded to any size, as long as pins are available. For n pins, n(n − 1) LEDs can be in the matrix. Any LED can be lit by applying 5 V and 0 V to its corresponding pins and setting all of the other pins connected to the matrix to high-impedance mode. Under the same constraints as discussed above up to n − 1 LEDs sharing a common positive or negative path can be lit in parallel.

Expanding

[edit]

The 3-wire circuit can be rearranged to this near-equivalent matrix (resistors have been relocated).

3-pin configuration arranged in a 3 × 2 display pattern for identical LEDs; any number of LEDs on a single row may be powered at a time.
3-pin configuration arranged in a 3 × 2 display pattern for different LEDs; any number of LEDs on a single row may be powered at a time.

This emphasizes the similarities between ordinary grid multiplex and Charlieplex, and demonstrates the pattern that leads to "the n-squared minus n" rule.

In typical usage on a circuit board the resistors would be physically located at the top of the columns and connected to the input pin. The rows would then be connected directly to the input pin bypassing the resistor.

The first setup in the image on the left is suitable only when identical LEDs are used since a single resistor is used for current-limiting through more than one LED (though not at the same time—rather, one resistor limits current through only one LED in a given column at one time). This is contrasted to the second configuration with individual resistors for each LED, as shown in the image on the right. In this second configuration, each LED has a unique resistor paired with it. This makes it possible to mix different kinds of LEDs by providing each with its appropriate resistor value.

In both of these configuration, as shown in both the left and the right image, the relocated resistors make it possible to light multiple LEDs at the same time row-by-row, instead of requiring that they be lit individually. The row current capacity could be boosted by an NPN emitter follower BJT transistor instead of driving the current directly with the typically much weaker I/O pin alone.

Problems with Charlieplexing

[edit]

Refresh rate

[edit]

Refresh rate is not a problem if Charlieplexed Active matrix addressing is used with a Charlieplexed LED array.[18]

In common with x/y multiplexing, however, there can be refresh rate issues if passive matrix addressing is used.

Because only a single set of LEDs, all having a common anode or cathode, can be lit simultaneously without turning on unintended LEDs, Charlieplexing requires frequent output changes, through a method known as multiplexing. When multiplexing is done, not all LEDs are lit quite simultaneously, but rather one set of LEDs is lit briefly, then another set, and eventually the cycle repeats. If it is done fast enough, they will appear to all be on, all the time, to the human eye because of persistence of vision. In order for a display to not have any noticeable flicker, the refresh rate for each LED must be greater than the Flicker fusion threshold; 50 Hz is often used as an approximation.

As an example, 8 tri-state pins are used to control 56 LEDs through Charlieplexing, which is enough for 8 7-segment displays (without decimal points). Typically, 7-segment displays are made to have a common cathode, sometimes a common anode, but without loss of generality a common cathode is assumed in the following: All LEDs in all 8 7-segment displays cannot be turned on simultaneously in any desired combination using Charlieplexing. It is impossible to get 56 bits of information directly from 8 trits (the term for a base-3 character, as the pins are 3-state) of information, as 8 trits fundamentally comprises 8 log23, or about 12.7 bits of information, which falls far short of the 56 bits required to turn all 56 LEDs on or off in any arbitrary combination. Instead, the human eye must be fooled by use of multiplexing.

Only one 7-segment display, one set of 7 LEDs can be active at any time. The way this would be done is for the 8 common cathodes of the 8 displays to each get assigned to its own unique pin among the 8 I/O ports. At any time, one and only one of the 8 controlling I/O pins will be actively low, and thus only the 7-segment display with its common cathode connected to that actively low pin can have any of its LEDs on. That is the active 7-segment display. The anodes of the 7 LED segments within the active 7-segment display can then be turned on in any combination by having the other 7 I/O ports either high or in high-impedance mode, in any combination. They are connected to the remaining 7 pins, but through resistors (the common cathode connection is connected to the pin itself, not through a resistor, because otherwise the current through each individual segment would depend on the number of total segments turned on, as they would all have to share a single resistor). But to show a desired number using all 8 digits, only one 7-segment display can be shown at a time, so all 8 must be cycled through separately, and in a 50th of a second for the entire period of 8. Thus the display must be refreshed at 400 Hz for the period-8 cycle through all 8 segments to make the LEDs flash no slower than 50 times per second. This requires constant interruption of whatever additional processing the controller performs, 400 times per second.

Peak current

[edit]

Due to the decreased duty cycle, the current requirement of a Charlieplexed display increases much faster than it would with a traditionally multiplexed display. As the display gets larger, the average current flowing through the LED must be (roughly) constant in order for it to maintain constant brightness, thus requiring the peak current to increase proportionally. This causes a number of issues that limit the practical size of a Charlieplexed display.

  • LEDs often have a maximum peak current rating as well as an average current rating.
  • If the microcontroller code crashes, and a one-LED-at-a-time Charlieplex is being used, the single LED left lit is under much higher stress than it would be in a row-at-a-time Charlieplexed display or in a traditionally multiplexed display, increasing the risk of a failure before the fault is spotted.

Requirement for tristate

[edit]

All the outputs used to drive a Charlieplexed display must be tristate. If the current is low enough to drive the displays directly by the I/O pins of the microcontroller, this is not a problem, but if external tristates must be used, then each tristate will generally require two output lines to control, eliminating most of the advantage of a Charlieplexed display. Since the current from microcontroller pins is typically limited to about 20 mA, this severely restricts the practical size of a Charlieplexed display. However, it can be done by enabling one segment at a time.[19]

Forward voltage

[edit]

When using LEDs with different forward voltages, such as when using different color LEDs, some LEDs can light when not desired.

In the diagram above it can be seen that if LED 6 has a 4 V forward voltage, and LEDs 1 and 3 have forward voltages of 2 V or less, they will light when LED 6 is intended to, as their current path is shorter. This issue can easily be avoided by comparing forward voltages of the LEDs used in the matrix and checking for compatibility issues. Or, more simply, using LEDs that all have the same forward voltage.[11][6]

This is also a problem where the LEDs are using individual resistors instead of shared resistors, if there is a path through two LEDs that has less LED drop than the supply voltage these LEDs may also illuminate at unintended times.

Alternative use cases and variants

[edit]

Input data multiplexing

[edit]

Charlieplexing can also be used to multiplex digital input signals into a microcontroller. The same diode circuits are used, except a switch is placed in series with each diode. To read whether a switch is open or closed, the microcontroller configures one pin as an input with an internal pull-up resistor. The other pin is configured as an output and set to the low logic level. If the input pin reads low, then the switch is closed, and if the input pin reads high, then the switch is open.[20]

One potential application for this is to read a standard (4 × 3) 12-key numeric keypad using only 4 I/O lines. The traditional row-column scan method requires 4 + 3 = 7 I/O lines. Thus Charlieplexing saves 3 I/O lines; however it adds the expense of 12 diodes, (since the diodes are only free when LEDs are used). A variation of the circuit with only 4 diodes is possible,[20] however this reduces the rollover of the keyboard. The microcontroller can always detect when the data is corrupt, but there is no guarantee it can sense the original key presses, unless only one button is pressed at a time. (However, it is probably possible to arrange the circuit so that if at most any two adjacent buttons are pressed, then no data loss will occur.)[vague] The input is only lossless in the 4-diode circuit if only one button is pressed at a time, or if certain problematic multiple key presses are avoided. In the 12-diode circuit, this is not an issue, and there is always a one-to-one correspondence between button presses and input data. However, there are so many diodes that are required to use the method (especially for larger arrays) that there is generally no cost savings over the traditional row-column scan method, unless the cost of a diode is only a fraction of the cost of an I/O pin, where that fraction is one over the number of I/O lines.

Projected capacitance touchscreens and keypads

[edit]
32 input diagonally wired touchscreen

These do not use diodes but rely on the change in capacitance between crossing conductive tracks to detect the proximity of one or more fingers through non-conducting materials such as plastic overlays, wood, glass, etc. - even double glazing.

These tracks can be made from a wide range of materials, such as printed circuit boards, transparent Indium Tin oxide, insulation coated fine wire, etc.

The technology can range in size from very small, as in "fingerprint detectors",[21] to very large, as in "touch interactive video walls". Usually, a limit is imposed on the maximum width of an x/y wired touchscreen, because the horizontal track resistance gets too great for the product to function properly. However, a diagonally wired touchscreen (as described later in this section) does not have this problem.

There are no LEDs or diodes and, at any one time, only one I/O line is set as an output, the remaining I/O lines being set as high-impedance inputs or "grounded". This means that power requirements are very small.

GuGaplexing

[edit]

In 2008, Dhananjay V. Gadre devised Gugaplexing, which is like Charlieplexing with multiple drive voltages.[22][23]

Chipiplexing

[edit]

In 2008, Guillermo Jaquenod's so called Chipiplexing adds emitter followers to boost the strength of the row drive allowing rows wider than a single microcontroller port could drive to be lit simultaneously.[24][25]

Cross-plexing

[edit]

In 2010, the Austrian chip manufacturer austriamicrosystems AG (named ams AG[nb 2] since 2012, and ams-OSRAM AG since 2020) introduced the multiplexing LED driver IC AS1119,[26][27] followed by the AS1130 in 2011.[28][29]

Also, the analog & mixed signal (AMS)[nb 2] division (named Lumissil Microsystems since 2020) of Integrated Silicon Solution Inc. (ISSI) introduced the IS31FL3731 in 2012[30][31] and the IS31FL3732 in 2015.[32][33][34] They all use a technique they call cross-plexing, a variant of Charlieplexing with automatic detection of open or shorted connections and anti-ghosting measures.[35]

Tucoplexing

[edit]

In 2019, Micah Elizabeth Scott developed a method to use 3 pins to run 4 LEDs and 4 switches called Tucoplexing.[36]

Pulse-width modulation

[edit]

Charlieplexing can even be used with pulse-width modulation to control the brightness of 12 LEDs with 4 pins.[37]

Code example

[edit]

In the following Arduino code example, the circuit[38][39] uses ATtiny 8-pin microcontroller which has 5 I/O pins to create a 7-segment display. Since a 7-segment display only requires control of 7 individual LEDs, we use 4 of the ATtiny I/O pins as Charlieplexed outputs (n (n - 1)), i.e. the 4 pins could be used to control up to 12 individual LEDs (here we just use 7 of them). Leaving the fifth I/O pin to be used as digital or analog input or another output.

// ATtiny code.
// Reads analog (or digital) input from pin 4 and every time the input goes below a set threshold.
// It counts one and displays the increase in count either by activating up one of four LEDs (or transistors)
// or one of twelve Charlieplexed LEDs.

// SET THESE VALUES:
int threshold = 500;
int maxCount = 7;
////////////////////
boolean sensorTriggered = false;
int count = 0;
int sensorValue = 0;
long lastDebounceTime = 0;  // The last time the output pin was toggled.
long debounceDelay = 50;    // The debounce time; increase if the output flickers.
////////////////////////////////////////////////////////////////////////////////
void setup() {
  // Use pull-down for disabled output pins rather than pull-up to reduce internal consumption.
  for (int pin = 0; pin < 4; pin++) {
    pinMode(pin, INPUT), digitalWrite(pin, LOW);
  }
  // Internal pull-up for enabled input pin 4.
  pinMode(4, INPUT), digitalWrite(4, HIGH);
}
////////////////////////////////////////////////////////////////////////////////
void loop() {
  testDigits();
}
void testDigits() {
  charlieLoop();
}
////////////////////////////////////////////////////////////////////////////////
void readSensor() {
  sensorValue = analogRead(2);  // pin4!
  delay(100);
  if (sensorValue < threshold && sensorTriggered == false) {
    sensorTriggered = true;
    count++;
    if (count > maxCount) count = 0;
    charlieLoop();
  }
  if (sensorValue > threshold) sensorTriggered = false;
}
////////////////////////////////////////////////////////////////////////////////
void charlieLoop() {
  count++;

  for (int i = 0; i < 1000; i++) {
    for (int c = 0; c < count; c++) {
      charliePlexPin(c);
    }
  }
  delay(1000);
  if (count > maxCount) count = 0;
}
////////////////////////////////////////////////////////////////////////////////
void charliePlexPin(int myLed){

  // Make sure we don't feed random voltages to the LEDs
  // during the brief time we are changing pin voltages and modes.
  // Use pull-down for disabled output pins rather than pull-up to reduce internal consumption.
  for (int pin = 0; pin < 4; pin++) {
    pinMode(pin, INPUT), digitalWrite(pin, LOW);
  }
  // With 4 pins we could lit up to 12 LEDs, we use only 7 here.
  // Make sure to set pin voltages (by internal pull-up or pull-down)
  // before changing pin modes to output.

#if 1 // Reduced code using a static lookup table.

  typedef struct {
    // Two different pin numbers (between 0 and 3; order is significant),
    // otherwise no led will be lit.
    low, high: int: 2;
  } Pins;

  static Pins pinsLookup[] = {
    {2, 0}, {2, 3}, {1, 3}, {0, 1}, {1, 0}, {0, 2}, {1, 2},
    // Other possible combinations for up to 12 LEDs:
    // {0, 3}, {2, 1}, {3, 0}, {3, 1}, {3, 2},
    // Other unusable combinations that don't lit any LED with a significant voltage and current,
    // unless pull-up or pull-down resistances are very unbalanced:
    // {0, 0}, {1, 1}, {2, 2}, {3, 3}
  };

  if (myLed >=0 && myLed <= sizeof(pinsLookup) / sizeof(Pins)) {
      register Pins &pins = pinsLookup[myLed];
      // Note that the first digitWrite to LOW is commented out,
      // as it is already set above for all output pins.
      /* digitalWrite(pins.low, LOW), */ pinMode(pins.low, OUTPUT);
      digitalWrite(pins.high, HIGH), pinMode(pins.high, OUTPUT);
   }

#else // Equivalent code using a long switch.

  switch(myLed) {
  case 0:
    /* digitalWrite(2, LOW), */ pinMode(2, OUTPUT);
    digitalWrite(0, HIGH), pinMode(0, OUTPUT);
    break;
  case 1:
    /* digitalWrite(2, LOW), */ pinMode(2, OUTPUT);
    digitalWrite(3, HIGH), pinMode(3, OUTPUT);
    break;
  case 2:
    /* digitalWrite(1, LOW), */ pinMode(1, OUTPUT);
    digitalWrite(3, HIGH), pinMode(3, OUTPUT);
    break;
  case 3:
    /* digitalWrite(0, LOW), */ pinMode(0, OUTPUT);
    digitalWrite(1, HIGH), pinMode(1, OUTPUT);
    break;
  case 4:
    /* digitalWrite(1, LOW), */ pinMode(1, OUTPUT);
    digitalWrite(0, HIGH), pinMode(0, OUTPUT);
    break;
  case 5:
    /* digitalWrite(0, LOW), */ pinMode(0, OUTPUT);
    digitalWrite(2, HIGH), pinMode(2, OUTPUT);
    break;
  case 6:
    /* digitalWrite(1, LOW), */ pinMode(1, OUTPUT);
    digitalWrite(2, HIGH), pinMode(2, OUTPUT);
    break;
  }

#endif
}
////////////////////////////////////////////////////////////////////////////////
void spwm(int freq, int pin, int sp) {
  // Call Charlieplexing to set correct pin outs:
  // on:
  digitalWrite(pin, HIGH);
  delayMicroseconds(sp * freq);
  // off:
  digitalWrite(pin, LOW);
  delayMicroseconds(sp * (255 - freq));
}

See also

[edit]

Notes

[edit]

References

[edit]

Further reading

[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
Charlieplexing is a multiplexing technique that enables a to control a large number of light-emitting diodes (LEDs) using a minimal number of input/output (I/O) pins by leveraging the tri-state logic capabilities of those pins—high, low, or high-impedance states—to selectively activate individual LEDs. With n pins, this method can drive up to n(n − 1) LEDs, as each of pins supports one LED oriented to conduct current from the higher-voltage pin to the lower one when appropriately biased. Developed in early 1995 by Charlie Allen at Products, charlieplexing was initially applied to drivers to minimize pin requirements in space-constrained designs, with its first commercial implementation in the MAX6951 LED driver, which controls eight 7- or 8-segment digits using just nine pins. In practice, the technique involves rapidly sequencing through pin states to illuminate LEDs one at a time, creating the illusion of simultaneous lighting through , while ensuring that non-active LEDs remain in a high-impedance or reverse-biased state to prevent unintended conduction. This requires LEDs rated to withstand the supply voltage in reverse bias, typically up to 5V or more, to avoid damage during off cycles. The primary advantages of charlieplexing include significant reductions in pin count compared to traditional row-column —for instance, three pins can control six LEDs instead of the four needed in a basic matrix—leading to lower costs, simpler circuit boards, and more efficient use of resources in pin-limited applications. It is widely used in LED matrices, cubes, indicators, and custom displays for embedded systems, such as projects, wearables, and , though it demands precise timing in software to maintain brightness and can complicate wiring and debugging for large arrays.

Background

Etymology

The term "Charlieplexing" is derived from the first name of its inventor, Charlie Allen, an engineer at Products, combined with "plexing," an jargon shorthand for . This portmanteau was coined in Maxim Integrated's Application Note 1880 ("Charlieplexing—Reduced Pin-Count LED Display Multiplexing"), published on February 10, 2003, to describe Allen's innovative approach to driving multiplexed displays using tri-state logic for efficient pin utilization. The name first gained formal recognition in 's documentation, particularly in the aforementioned application note, which attributes the technique's development to Allen and uses the term to differentiate it from traditional methods.

Origin

Charlieplexing was invented by Charlie Allen in early 1995 while he was an applications engineer at Products, a company specializing in analog and mixed-signal integrated circuits. The technique emerged as a solution to the challenge of driving multiple LEDs in displays using limited I/O pins, particularly in resource-constrained embedded systems prevalent during the mid-1990s, such as pagers and early portable communication devices that required compact, low-pin-count designs to minimize power consumption and board space. Allen proposed the method initially on an internet mailing list, highlighting its potential for efficient LED without dedicated driver hardware. The tristate multiplexing technique on which Charlieplexing is based originated earlier, with a 1979 German patent by Christopher Malinowski (U.S. Patent 4,319,227 granted in 1982) describing the control of multiple LEDs using few lines via tri-state logic, where pins dynamically serve as anodes or cathodes. The first formal documentation using the name "Charlieplexing" appeared in a Maxim application note published in 2003, which detailed its implementation for reducing pin requirements in LED display drivers. This note coincided with the introduction of Maxim's MAX6951 LED driver IC in 2001, the company's inaugural product incorporating the technique to control eight 7- or 8-segment digits using just nine pins. No specific patent was filed by Allen for the core method, as it built upon these earlier tri-state logic concepts. Charlieplexing evolved from conventional schemes, which typically required separate rows and columns of pins leading to linear scalability (n² elements with 2n pins), by innovating the use of tri-state outputs to allow each pin to serve dynamically as either an or , achieving near-exponential control (n² - n elements with n pins). This advancement was driven by the need for higher integration in battery-powered gadgets, enabling designers to allocate scarce pins to other functions like sensors or communication interfaces.

Principles

Tri-state Logic Fundamentals

, also known as , refers to a digital output stage that can assume one of three distinct states: a high state (logical 1, typically connected to Vcc), a low state (logical 0, typically connected to ground or GND), or a high-impedance state (denoted as ), where the output is effectively disconnected from the circuit, presenting very high resistance to current flow. This high-impedance state is crucial for preventing electrical conflicts in shared signal paths, as it isolates the device from the line without influencing the voltage level. The high-impedance state enables bidirectional control on a single pin or bus line by allowing the device to alternate between actively driving the line (high or low) and acting as an input receiver, where it avoids interfering with other drivers. In bidirectional applications, such as data buses, multiple devices can connect to the same wire; only the active device drives the line while others remain in high-impedance, ensuring no short circuits occur if one attempts to drive high while another drives low. This capability is implemented using control signals to enable or disable the output transistors in the buffer circuit, typically involving complementary MOSFETs or BJTs configured to float the output when inactive. A fundamental implementation is the tri-state buffer, which has a data input (IN), an enable input (EN), and an output (OUT). For an active-high, non-inverting tri-state buffer, the behavior is described by the following :
ENINOUT
00Hi-Z
01Hi-Z
100
111
When EN is low (0), the output is in high-impedance regardless of IN; when EN is high (1), OUT mirrors IN. The circuit diagram typically shows two transistors (e.g., PMOS for high and NMOS for low) gated by the enable signal, with the output node between them; both transistors off produces the Hi-Z state. Tri-state logic serves as a prerequisite for advanced multiplexing techniques, as standard binary logic (high/low only) prohibits sharing output lines among multiple drivers due to the risk of contention and short circuits, limiting efficiency—for instance, N pins might control at most N-1 lines in simple configurations without dedicated isolation. In applications like LED matrix driving via Charlieplexing, this third state is essential to configure pins dynamically for current paths without additional hardware.

Complementary Drive Mechanism

In Charlieplexing, the complementary drive mechanism leverages tri-state logic outputs to generate opposing voltage signals across specific LED pairs, enabling selective illumination while isolating other segments. This approach builds upon the high, low, and high-impedance states of pins to ensure current flows only through the intended LED, without unintended paths forming due to voltage gradients. The core process involves configuring one pin to drive high (acting as the ) and another to drive low (acting as the ) for a targeted LED, while all remaining pins are set to high-impedance to prevent any current leakage or back-biasing that could affect adjacent elements. This configuration exploits the unidirectional nature of LEDs, allowing forward only across the selected pair, as the high-impedance state effectively disconnects unused pins from the circuit. By dynamically reassigning pin roles in sequence, the mechanism avoids ghosting—unwanted illumination of non-targeted LEDs—through precise control of voltage differentials. The for driving involves rapid sequential switching: for each time slot, a unique high-low pair is activated briefly, with the high-impedance state on other pins ensuring isolation throughout the cycle. This pulsed operation multiplexes the display, refreshing each LED in turn to maintain perceived continuous illumination without overlap or interference. For instance, in a three-pin setup (N=3), up to six LEDs can be controlled by cycling through the six possible unique high-low combinations across the pins. Consider a simple for N=3 pins (labeled P1, P2, P3) connected to six LEDs oriented between each pair: LED12 ( at P1, at P2), LED21 ( at P2, at P1), LED13 ( at P1, at P3), LED31 ( at P3, at P1), LED23 ( at P2, at P3), and LED32 ( at P3, at P2). To light LED12, set P1 high, P2 low, and P3 high-impedance; current flows only from P1 through LED12 to P2, with P3 isolated. Subsequent states similarly activate each LED by swapping the high-low assignments. The maximum number of controllable elements in this scheme is given by the equation: N×(N1)N \times (N - 1) where NN represents the number of available pins, derived from the unique directed pairs formed by selecting one pin high and one low, excluding self-pairs. For N=3, this yields 6 elements, as each pin can pair as with the other two as cathodes.

Matrix Expansion Techniques

Charlieplexing scales the number of controllable LEDs quadratically with the number of available pins, enabling efficient use of limited I/O resources for larger displays. With N pins, the technique supports up to N × (N − 1) LEDs, as each of pins can independently drive one LED by directing current from one pin (set high) to another (set low), while all other pins remain in high-impedance state. This formula arises from the tri-state logic, where the directed nature of LED polarity allows two LEDs per unordered pin pair without interference. For practical scaling, 10 pins can thus drive 90 LEDs, demonstrating the method's efficiency for displays requiring dozens to hundreds of indicators. A concrete illustration is a 4-pin matrix (pins labeled P1, P2, P3, P4), which controls 12 LEDs connected between every pair of pins in opposing orientations (e.g., one LED to P1 and to P2, another reversed). To light a specific LED, the software sets the pin high, the low, and the unused pins to ; all other LEDs remain off due to the lack of complete current paths. The full set of states requires systematic enumeration to avoid conflicts, often implemented via a mapping each LED to its pin configuration. The table below outlines the configurations for all 12 LEDs:
LED IDAnode (High)Cathode (Low)P1 StateP2 StateP3 StateP4 State
1 (P1→P2)P1P2HighLowZZ
2 (P2→P1)P2P1LowHighZZ
3 (P1→P3)P1P3HighZLowZ
4 (P3→P1)P3P1LowZHighZ
5 (P1→P4)P1P4HighZZLow
6 (P4→P1)P4P1LowZZHigh
7 (P2→P3)P2P3ZHighLowZ
8 (P3→P2)P3P2ZLowHighZ
9 (P2→P4)P2P4ZHighZLow
10 (P4→P2)P4P2ZLowZHigh
11 (P3→P4)P3P4ZZHighLow
12 (P4→P3)P4P3ZZLowHigh
(Z denotes high-impedance state.) This configuration ensures only the targeted LED illuminates, with sequencing cycled rapidly for perceived simultaneous operation. For larger matrices, expansion techniques include systematic wiring patterns that diagonalize LED pairs across rows to simplify layout and maintain the tri-state core, as seen in progressive matrix designs. Hybrid implementations may integrate shift registers to virtually expand pin count, allowing Charlieplexing to drive extended lines while preserving the complementary drive principle. However, scaling introduces challenges in state management, where larger N demands complex lookup tables and precise timing sequences, leading to increased software overhead on the . Dedicated driver ICs, such as the MAX6951, further facilitate expansion by handling multiplexing internally for up to 8 digits with 9 pins.

Implementation

LED Driving Process

The LED driving process in Charlieplexing begins with initializing all pins to a high-impedance (tri-state) state, typically by setting them as inputs, to ensure no unintended current paths exist before multiplexing begins. This setup leverages the tri-state logic where pins can be driven high, low, or left floating, allowing selective activation of LEDs without additional switching components. To multiplex the LEDs, the process cycles rapidly through all possible unique high-low pin pairs, activating one LED at a time by setting one pin to high () and an adjacent pin to low (), while keeping all other pins in high-impedance to isolate the current flow. For instance, in a configuration with LEDs connected between pins in both directions, reversing the high and low states on the same pair lights the oppositely oriented LED. This scanning repeats continuously, with each LED receiving brief illumination in sequence to create the illusion of simultaneous operation. For a display, the timing must ensure the full scan cycle—covering all LEDs—completes faster than the human eye's threshold, typically at a of at least 60 Hz, meaning the total cycle time should be less than 1/60 seconds or approximately 16.7 ms. The duration per LED state is adjusted proportionally based on the number of LEDs, often using short delays (e.g., 1-5 ms per state) to balance brightness and speed. Essential circuit components include current-limiting resistors to prevent LED damage, typically placed in series with each pin (e.g., 150-330 ohms depending on supply voltage and LED forward current, around 10-20 mA) rather than per LED, as only one path is active at a time. LEDs must tolerate reverse up to the supply voltage during off states; if not, additional diodes can be added in anti-parallel to each LED for protection, though this is uncommon in basic implementations to minimize parts. As an example workflow for a 3-pin setup equivalent to driving 6 LEDs (3² - 3 = 6, simulating a sparse 3x3 matrix), the following illustrates one full scan cycle, assuming pins A, B, and C:

# Initialize all pins to high-impedance (input mode) // [pseudocode](/page/Pseudocode) for one full scan # Light LED between A (high) and B (low) set_pin(A, high); set_pin(B, low); set_pin(C, high-Z); delay(2 ms); # Brief dwell time # Light LED between B (high) and A (low) set_pin(A, low); set_pin(B, high); set_pin(C, high-Z); delay(2 ms); # Light LED between A (high) and C (low) set_pin(A, high); set_pin(B, high-Z); set_pin(C, low); delay(2 ms); # Light LED between C (high) and A (low) set_pin(A, low); set_pin(B, high-Z); set_pin(C, high); delay(2 ms); # Light LED between B (high) and C (low) set_pin(A, high-Z); set_pin(B, high); set_pin(C, low); delay(2 ms); # Light LED between C (high) and B (low) set_pin(A, high-Z); set_pin(B, low); set_pin(C, high); delay(2 ms); # Return all to high-Z for next cycle

# Initialize all pins to high-impedance (input mode) // [pseudocode](/page/Pseudocode) for one full scan # Light LED between A (high) and B (low) set_pin(A, high); set_pin(B, low); set_pin(C, high-Z); delay(2 ms); # Brief dwell time # Light LED between B (high) and A (low) set_pin(A, low); set_pin(B, high); set_pin(C, high-Z); delay(2 ms); # Light LED between A (high) and C (low) set_pin(A, high); set_pin(B, high-Z); set_pin(C, low); delay(2 ms); # Light LED between C (high) and A (low) set_pin(A, low); set_pin(B, high-Z); set_pin(C, high); delay(2 ms); # Light LED between B (high) and C (low) set_pin(A, high-Z); set_pin(B, high); set_pin(C, low); delay(2 ms); # Light LED between C (high) and B (low) set_pin(A, high-Z); set_pin(B, low); set_pin(C, high); delay(2 ms); # Return all to high-Z for next cycle

This sequence totals about 12 ms, supporting a refresh rate above 80 Hz when looped.

Hardware Requirements

Charlieplexing implementations require a microcontroller equipped with general-purpose input/output (GPIO) pins that support true tri-state logic functionality, enabling each pin to operate in high (logic 1), low (logic 0), or high-impedance (input) states. This tri-state capability is essential for isolating unused pins while actively driving both high and low on the selected pair; open-drain or open-collector configurations can be used but require external pull-up resistors to achieve the high state, potentially limiting current drive and complicating the circuit. The primary components include light-emitting diodes (LEDs) arranged in a non-traditional matrix, where LEDs are connected between every pair of pins in both polarities to maximize the number of controllable elements (up to n2nn^2 - n LEDs for nn pins). Current-limiting resistors must be placed in series with each pin to regulate current and avoid exceeding the microcontroller's drive limits or damaging the LEDs; their value is determined by the formula R=VhighVfIR = \frac{V_{high} - V_f}{I}, where VhighV_{high} is the logic high voltage, VfV_f is the LED's forward voltage drop, and II is the target forward current (typically 10-20 mA). For power supply stability, especially during rapid pin state switching, bypass capacitors (e.g., 0.1 µF ceramic) are recommended across the microcontroller's supply pins to decouple noise and maintain voltage integrity. Charlieplexing circuits are compatible with standard and TTL logic families prevalent in microcontrollers like AVR or PIC series. Supply voltage levels, such as 5 V for TTL-compatible systems or 3.3 V for low-power devices, directly influence LED selection, as lower voltages necessitate LEDs with reduced forward voltage drops to ensure sufficient headroom for illumination. To validate the high-impedance state during setup, measurements can trace pin voltages, confirming minimal leakage current (ideally <1 µA) and no voltage clamping when the pin is configured as an input.

Advantages and Limitations

Reasons for Using Charlieplexing

Charlieplexing offers significant pin efficiency by enabling a microcontroller with N pins to control up to N(N-1) LEDs, far surpassing traditional binary multiplexing methods that typically require 2√M pins for M LEDs in a row-column configuration. For instance, with 4 pins, Charlieplexing can drive 12 LEDs, compared to only 4 LEDs using a 2x2 row-column matrix on the same 4 pins. This efficiency saves over 50% of I/O pins on resource-constrained microcontrollers, allowing more functionality without expanding the chip package. The reduced pin count translates to substantial cost and space savings, as smaller microcontrollers or driver ICs with fewer I/O lines are less expensive and more compact, making Charlieplexing ideal for applications like wearables and Internet of Things (IoT) devices where board real estate is limited. For example, a 9-pin driver like the MAX6951 can manage 8 LED digits, halving the pin requirements—and thus the complexity and cost—compared to 16-pin alternatives such as the MAX7219. In terms of power efficiency, Charlieplexing activates only one LED at a time, with undriven pins in a high-impedance state to prevent unintended current paths, thereby minimizing average power consumption relative to methods that energize multiple elements simultaneously. This approach is particularly beneficial in battery-powered systems, as it avoids the need for additional logic components that could increase quiescent current draw. The following table compares Charlieplexing to traditional row-column multiplexing for selected LED counts:
Number of LEDsPins Required (Charlieplexing)Pins Required (Row-Column)
434
946
1658
25610
This illustrates how Charlieplexing consistently requires fewer pins, enhancing scalability for larger displays.

Refresh Rate Challenges

In Charlieplexing, sequential scanning of the LED matrix using tri-state outputs results in each LED being illuminated only briefly during its assigned state in the cycle, potentially causing visible flicker if the time between successive activations for any individual LED exceeds the human eye's flicker fusion threshold, typically around 16 ms corresponding to a 60 Hz refresh rate per LED. Persistence of vision, the optical phenomenon where retinal images linger for approximately 1/16 to 1/25 of a second after the stimulus ends, enables the perception of continuous illumination from rapid sequential flashes in multiplexed displays; however, insufficient refresh rates disrupt this, making flicker apparent and potentially causing visual discomfort, as seen in early film projections at 16 frames per second or modern low-refresh LED signage below 50 Hz. To address flicker, the microcontroller's clock speed can be increased to shorten state transition times, or the duty cycle adjusted by minimizing each LED's on duration while ensuring the full matrix scan completes within the persistence threshold; the minimum display refresh rate ff is given by f=1N(N1)×ton,f = \frac{1}{N(N-1) \times t_{\text{on}}}, where NN is the number of control pins and tont_{\text{on}} is the on-time per LED state, guaranteeing each LED achieves the target refresh without perceptible gaps. Larger matrices exacerbate these issues due to the quadratic increase in addressable states, demanding faster processing; for example, a 20-pin setup enables 380 LEDs but requires microcontroller speeds exceeding 10 kHz to sustain viable tont_{\text{on}} values at 60 Hz per LED, often limiting practical implementations to smaller configurations without dedicated driver ICs.

Peak Current and Tristate Issues

In Charlieplexing, each LED is driven individually in sequence, resulting in a high peak current for the active LED to ensure adequate brightness, typically limited to 20 mA for standard low-power LEDs, while the average current duty cycle across the matrix remains low due to time-sharing among multiple LEDs. This peak current is determined by the supply voltage, the LED's forward voltage drop, and the series current-limiting resistor, allowing brief high-intensity pulses without continuous high power draw. However, microcontroller I/O pins have inherent current limits, often 20–40 mA absolute maximum, which must not be exceeded to avoid damage. A critical requirement for effective Charlieplexing is the use of true tri-state logic on the driving pins, where pins not involved in driving a particular LED must enter a high-impedance (Z-state) to isolate them from the circuit and prevent unintended current paths. Without this, residual voltages or currents on "off" pins can cause leakage, leading to ghosting—where unintended LEDs faintly illuminate—or reduced brightness in the intended LED due to voltage division. Attempts to simulate tri-state behavior with pull-up or pull-down resistors introduce additional leakage currents, exacerbating these issues and potentially causing thermal stress or unreliable operation, particularly in larger matrices. The peak current can be precisely calculated using Ohm's law: Ipeak=VsupplyVforwardRI_{\text{peak}} = \frac{V_{\text{supply}} - V_{\text{forward}}}{R}, where VsupplyV_{\text{supply}} is the power supply voltage (e.g., 5 V), VforwardV_{\text{forward}} is the LED's forward voltage (typically 1.8–2.2 V for red LEDs), and RR is the series resistor value chosen to limit current within safe bounds. For instance, with a 5 V supply, 2 V forward drop, and 150 Ω resistor, the peak current is approximately 20 mA, aligning with common LED ratings and MCU capabilities. Exceeding this through insufficient resistance or high supply voltages risks MCU pin failure or LED burnout. To mitigate peak current and tri-state limitations in demanding applications, such as high-brightness displays or larger arrays, external drivers like NPN/PNP transistors or dedicated ICs (e.g., MAX6951) can be integrated to provide higher current sinking/sourcing (up to 40 mA per segment) and built-in high-impedance states, offloading the burden from the microcontroller while preserving the pin-efficiency of . These solutions ensure reliable operation but add minor hardware complexity.

Forward Voltage Considerations

In Charlieplexing configurations that incorporate multi-color LEDs within a shared matrix, variations in forward voltage (V_f) across different LED colors pose significant challenges to uniform illumination. For instance, typical red LEDs exhibit a V_f of approximately 2 V, whereas blue LEDs require around 3 V at standard operating currents. These differences arise from the semiconductor materials used, with longer-wavelength colors like red needing less energy to emit photons compared to shorter-wavelength blue. In the asymmetric drive paths of Charlieplexing, where one pin is set high and another low without individual current-limiting elements, this V_f mismatch directly influences the operating current for each LED. The primary effect is uneven brightness, with higher V_f LEDs appearing dimmer due to reduced current flow under the same drive voltage. This results in current imbalances across the matrix, where lower V_f LEDs (e.g., red) draw disproportionately more current, potentially leading to thermal variations and accelerated degradation in mixed-color arrays. Such disparities are exacerbated in Charlieplexing's tri-state logic, as the shared paths lack dedicated regulation, amplifying the impact compared to conventional multiplexing with per-LED resistors. The underlying relationship can be expressed through the effective voltage drop across the LED, given by Vdrop=VhighVfV_{\text{drop}} = V_{\text{high}} - V_f, where VhighV_{\text{high}} is the logic-high output voltage from the driver pin. The resulting current II is then approximately IVdropRdynamicI \approx \frac{V_{\text{drop}}}{R_{\text{dynamic}}}, with RdynamicR_{\text{dynamic}} representing the LED's small internal dynamic resistance (typically 10–50 Ω near operating point). This equation illustrates how even small V_f differences yield large current variations, as RdynamicR_{\text{dynamic}} is low and does not provide inherent limiting. For example, with a 5 V supply, a red LED (V_f = 2 V) experiences a 3 V drop, while a blue LED (V_f = 3 V) sees only 2 V, potentially halving the current and brightness if uncompensated. To address these issues, designers often employ LED binning to select components with closely matched V_f values within the array, ensuring more consistent performance across colors. Adding series resistors to individual LEDs allows precise current balancing by tailoring resistance to each V_f (e.g., lower resistance for higher V_f LEDs), though this increases part count and may partially offset 's efficiency gains. Alternatively, software compensation via pulse-width modulation (PWM) adjusts the on-time or duty cycle for specific LED types, normalizing perceived brightness without hardware modifications; for instance, increasing the duty cycle for blue LEDs relative to red can equalize luminous output.

Variants and Applications

Input Data Multiplexing

Charlieplexing, originally developed for driving multiple LEDs, can be adapted for multiplexing input devices such as keypads or switches by leveraging the tri-state capabilities of microcontroller pins to scan for closures in a resource-efficient manner. In this configuration, pins are configured in high-impedance (input) mode during scanning to detect switch states, while pull-up resistors—either internal to the microcontroller or external—ensure that inactive inputs read high. When a switch closes, it pulls the input low, signaling activation. To prevent bidirectional current flow inherent in mechanical switches, which could cause ghosting or false detections, a series diode is typically placed with each switch, oriented to allow current only in the direction of detection. This adaptation inverts the output-oriented use of Charlieplexing, focusing instead on input polling while maintaining the same pin efficiency. For an N-pin setup, this method supports up to N(N1)2\frac{N(N-1)}{2} switches, as each unique pair of pins defines a directed path for detection, similar to the LED configuration but accounting for the diode's directionality. For example, with 4 pins, up to 6 keys can be multiplexed, connecting each key between a unique pair of pins with the diode anode toward the pin driven low during scanning. The scanning process involves sequentially configuring one pin as an output driven low (acting as a virtual "row") and setting the remaining pins to high-impedance input mode (as "columns") to read their states via the pull-ups. If a connected input reads low, it indicates a closure on that pin pair, allowing the system to identify the specific key. This cycle repeats rapidly across all pin combinations to poll the entire matrix. Due to mechanical bouncing in switches, debouncing is essential, typically implemented in software by sampling inputs multiple times over a short period (e.g., 10-50 ms) or using hardware capacitors, to filter transient signals and ensure reliable detection. This approach is particularly advantageous in embedded systems with limited I/O pins, such as microcontrollers in wearables or IoT devices, where it enables efficient I/O multiplexing without additional hardware like dedicated matrix scanners, conserving both pins and power while supporting compact designs.

Projected Capacitance Interfaces

Charlieplexing extends to projected capacitance interfaces by applying tri-state logic to scan a matrix of micro-capacitors formed at intersections of conductive traces connected to microcontroller (MCU) pins, enabling efficient touch detection with minimal hardware. In this method, the MCU configures pins in high, low, or high-impedance (tri-state) states to sequentially charge and discharge specific lines, creating a scanning sequence that isolates individual capacitive nodes without requiring additional diodes or multiplexers. A finger touch alters the local capacitance by coupling to ground, which is detected as a change in the charge or discharge time of the node; if the measured time exceeds a predefined threshold, it indicates a touch event. This approach leverages the inherent capacitance between traces in a diagonal or asymmetric matrix configuration, allowing for self-capacitive sensing where each node operates independently during its scan cycle. Implementation involves the MCU driving one pin high to charge a trace while setting others to high-impedance or low states to prevent interference, then measuring the discharge time on a sense pin using an internal timer or ADC to quantify capacitance variations. For instance, during sequential scanning, the MCU cycles through all possible pin combinations (e.g., pin 1 high and pin 2 sense, with others tri-stated), timing how long it takes for the voltage to drop below a reference level; increased capacitance from a touch slows this process, triggering detection logic. This tri-state scanning minimizes pin usage compared to traditional row-column matrices, as inactive pins float without affecting active measurements. Self-capacitive keypads exemplify this, where 4 pins can support up to 10 keys by utilizing both single-pin sensors (4 keys) and intersection nodes (6 keys) in a charlieplexed arrangement, scanned rapidly to maintain responsiveness. However, multi-touch is limited, as simultaneous touches on adjacent nodes can cause crosstalk or ghosting, typically restricting reliable detection to isolated or widely spaced contacts unless advanced noise filtering is applied. In modern low-pin-count IoT devices, such as wearables and smart home interfaces, charlieplexing-based projected capacitance has gained relevance post-2010 through MCU advancements like integrated high-resolution timers and auto-tuning algorithms, enabling precise timing measurements with reduced power consumption. For example, MCUs with enhanced peripherals, such as Atmel's QTouch Charge Transfer method, allow single-pin-per-channel sensing that scales to matrices via tri-state control, supporting wake-on-touch for battery-powered UIs. These developments have facilitated seamless integration into resource-constrained systems, providing robust touch detection over distances up to several centimeters while maintaining low latency. As of 2023, integrations in based MCUs have expanded use in edge AI devices for gesture recognition.

GuGaplexing and Chipiplexing

GuGaplexing is a multiplexing technique that extends by incorporating additional discrete components to double the number of controllable LEDs while using the same number of microcontroller I/O pins. Developed by Dhananjay V. Gadre, it employs N–1 pairs of complementary transistors (such as NPN and PNP types) to generate inverted signal versions, enabling the use of both low-low and high-high pin states alongside the traditional low-high and high-low combinations. This allows control of 2×N×(N–1) LEDs with N pins, for example, 24 LEDs with 4 pins, compared to Charlieplexing's N×(N–1). The method requires the LED forward voltage to exceed half the supply voltage to ensure proper isolation between states, and it maintains the one-LED-at-a-time activation typical of . Chipiplexing, coined by Guillermo Jaquenod, is another variant that modifies to enable simultaneous activation of multiple LEDs, addressing limitations in brightness and peak current by distributing the load across time slots. It uses N microcontroller ports paired with N bipolar transistors (typically PNP with base-emitter resistors) to create a fixed low-voltage reference, allowing up to N+1 LEDs to illuminate concurrently when ports are set high, while high-impedance states turn them off. For instance, with 3 ports, it drives 4 LEDs at once, providing a 30% duty cycle per LED for 5 ports versus 's 5%, which reduces peak currents proportionally. This approach adds hardware overhead but simplifies software by permitting grouped LED control without complex sequencing. In comparison, GuGaplexing prioritizes maximizing LED count per pin at the cost of additional transistor pairs for signal inversion, whereas Chipiplexing emphasizes improved duty cycles and multi-LED operation through transistor-based voltage referencing, trading some pin efficiency for enhanced brightness uniformity. Both variants emerged in the late 2000s from electronics design communities: GuGaplexing was introduced in an EDN Design Idea in October 2008 by Gadre, building on hacker interest in pin-efficient displays, while Chipiplexing appeared in a November 2008 EDN article by Jaquenod. Commercial integrated solutions like the Maxim MAX6968, an 8-port open-drain constant-current LED driver released around 2005, automate tri-state functionality similar to Chipiplexing principles, enabling multiplexed LED arrays with minimal external components and serial interfacing for up to 55 mA per output.

Cross-plexing, Tucoplexing, and PWM Adaptations

Cross-plexing represents a hybrid approach that integrates principles with traditional row-column multiplexing to drive larger LED arrays while selectively employing tri-state logic on specific lines for efficiency. This variant optimizes pin usage by treating portions of the array as a standard matrix for high-density regions and applying tri-state control to expand addressing without proportional increases in I/O requirements. For instance, the AS1130 LED driver IC from ams OSRAM utilizes cross-plexing to control 132 LEDs across 12 lines by exploiting the unidirectional forward bias of LEDs, configuring pins alternately as sources or sinks in a cross-connected topology. Tucoplexing extends Charlieplexing through time-division multiplexing combined with RC-based sensing and UART-like serial protocols, enabling efficient control of both outputs and inputs over extended distances, such as in remote LED matrices or switch arrays. In this method, a base 3-wire Charlieplexed setup drives 4 LEDs while simultaneously scanning 4 buttons via rapid line toggling to detect shorts, followed by capacitor charging to differentiate presses; serial expansion propagates control signals to off-board nodes, reducing local pin demands. Developed for compact remote interfaces, Tucoplexing achieves this with minimal overhead, as demonstrated in firmware implementations for USB switch controllers. PWM adaptations enhance Charlieplexing by modulating the duration each LED is active within its scan cycle, allowing for analog-like dimming and up to 8-bit grayscale resolution without additional hardware. This technique allocates variable on-time slots during the multiplex period, where the duty cycle determines perceived brightness; for example, in an 8-bit system, the formula integrates as \text{Duty} = \left( \frac{\text{on_time}}{\text{scan_period}} \right) \times 255, ensuring smooth intensity control across the array. Commercial drivers like the IS31FL3731 from ISSI Microelectronics incorporate this for 144-LED matrices, supporting per-LED PWM at frequencies up to 1 kHz via I²C configuration, a method widely adopted in displays and indicators since the early 2010s.

Practical Examples

Code Implementation Example

A practical implementation of Charlieplexing in software can be demonstrated using C code for an microcontroller, controlling 12 LEDs connected to 4 pins (yielding N×(N1)=12N \times (N-1) = 12 LEDs for N=4N=4). This example uses tri-state logic on digital pins 8 through 11, configured via a predefined matrix that specifies pin modes (OUTPUT or INPUT for high-impedance) and states (HIGH or LOW) for each LED. The code scans through all LEDs sequentially in a loop, activating one at a time to multiplex the display and prevent flicker when delays are minimized. The following code initializes pins as INPUT in setup for high-impedance, with tri-state control occurring dynamically in the lightOn function, followed by an allOff call to ensure proper deactivation. It includes inline comments explaining the state configurations, where each LED corresponds to a unique pair of pins: one driven HIGH (anode) and the other LOW (cathode), with unused pins in INPUT mode (high-impedance). For robustness, an error check could be added in production code to validate LED indices against the matrix size, though this basic version assumes valid inputs within 0 to 11.

c

// Charlieplexing example for 4 pins controlling 12 LEDs // Pins: A=8 (anode/cathode for LEDs 0,1,4,6), B=9, C=10, D=11 #define A 8 #define B 9 #define C 10 #define D 11 #define PIN_CONFIG 0 // Index for pin modes (OUTPUT=drive, INPUT=high-impedance) #define PIN_STATE 1 // Index for logic levels (HIGH=anode, LOW=cathode) #define LED_NUM 12 // Total LEDs: N*(N-1) for N=4 // 3D array: [LED][config/state][pin A/B/C/D] // Each LED row defines modes and states to light only that LED int matrix[LED_NUM][2][4] = { // LED 0: A high, B low (others input) { {OUTPUT, OUTPUT, INPUT, INPUT}, {HIGH, LOW, LOW, LOW} }, // LED 1: B high, A low { {OUTPUT, OUTPUT, INPUT, INPUT}, {LOW, HIGH, LOW, LOW} }, // LED 2: B high, C low { {INPUT, OUTPUT, OUTPUT, INPUT}, {LOW, HIGH, LOW, LOW} }, // LED 3: C high, B low { {INPUT, OUTPUT, OUTPUT, INPUT}, {LOW, LOW, HIGH, LOW} }, // LED 4: A high, C low { {OUTPUT, INPUT, OUTPUT, INPUT}, {HIGH, LOW, LOW, LOW} }, // LED 5: C high, A low { {OUTPUT, INPUT, OUTPUT, INPUT}, {LOW, LOW, HIGH, LOW} }, // LED 6: A high, D low { {OUTPUT, INPUT, INPUT, OUTPUT}, {HIGH, LOW, LOW, LOW} }, // LED 7: D high, A low { {OUTPUT, INPUT, INPUT, OUTPUT}, {LOW, LOW, LOW, HIGH} }, // LED 8: B high, D low { {INPUT, OUTPUT, INPUT, OUTPUT}, {LOW, HIGH, LOW, LOW} }, // LED 9: D high, B low { {INPUT, OUTPUT, INPUT, OUTPUT}, {LOW, LOW, LOW, HIGH} }, // LED 10: C high, D low { {INPUT, INPUT, OUTPUT, OUTPUT}, {LOW, LOW, HIGH, LOW} }, // LED 11: D high, C low { {INPUT, INPUT, OUTPUT, OUTPUT}, {LOW, LOW, LOW, HIGH} } }; void lightOn(int led) { // Error handling: Ensure valid LED index if (led < 0 || led >= LED_NUM) { return; // Skip invalid requests to avoid pin misconfiguration } // Set pin modes for tri-state: OUTPUT for active pins, INPUT for off pinMode(A, matrix[led][PIN_CONFIG][0]); pinMode(B, matrix[led][PIN_CONFIG][1]); pinMode(C, matrix[led][PIN_CONFIG][2]); pinMode(D, matrix[led][PIN_CONFIG][3]); // Apply logic states: HIGH/LOW only on OUTPUT pins digitalWrite(A, matrix[led][PIN_STATE][0]); digitalWrite(B, matrix[led][PIN_STATE][1]); digitalWrite(C, matrix[led][PIN_STATE][2]); digitalWrite(D, matrix[led][PIN_STATE][3]); } void allOff() { // Set all pins to INPUT for high-impedance off state pinMode(A, INPUT); pinMode(B, INPUT); pinMode(C, INPUT); pinMode(D, INPUT); } void setup() { // Initial pin setup to high-impedance pinMode(A, INPUT); pinMode(B, INPUT); pinMode(C, INPUT); pinMode(D, INPUT); } void loop() { // Scan all LEDs sequentially for [multiplexing](/page/Multiplexing) for (int l = 0; l < LED_NUM; l++) { lightOn(l); // Activate specific LED via matrix config delay(1000 / LED_NUM); // ~83 ms per LED for visible sequential lighting; reduce to ~1 ms for flicker-free multiplex allOff(); // Deactivate all LEDs before next activation } // Full cycle repeats; total time ~1 second with above delay, but <1 ms per state transition in optimized use }

// Charlieplexing example for 4 pins controlling 12 LEDs // Pins: A=8 (anode/cathode for LEDs 0,1,4,6), B=9, C=10, D=11 #define A 8 #define B 9 #define C 10 #define D 11 #define PIN_CONFIG 0 // Index for pin modes (OUTPUT=drive, INPUT=high-impedance) #define PIN_STATE 1 // Index for logic levels (HIGH=anode, LOW=cathode) #define LED_NUM 12 // Total LEDs: N*(N-1) for N=4 // 3D array: [LED][config/state][pin A/B/C/D] // Each LED row defines modes and states to light only that LED int matrix[LED_NUM][2][4] = { // LED 0: A high, B low (others input) { {OUTPUT, OUTPUT, INPUT, INPUT}, {HIGH, LOW, LOW, LOW} }, // LED 1: B high, A low { {OUTPUT, OUTPUT, INPUT, INPUT}, {LOW, HIGH, LOW, LOW} }, // LED 2: B high, C low { {INPUT, OUTPUT, OUTPUT, INPUT}, {LOW, HIGH, LOW, LOW} }, // LED 3: C high, B low { {INPUT, OUTPUT, OUTPUT, INPUT}, {LOW, LOW, HIGH, LOW} }, // LED 4: A high, C low { {OUTPUT, INPUT, OUTPUT, INPUT}, {HIGH, LOW, LOW, LOW} }, // LED 5: C high, A low { {OUTPUT, INPUT, OUTPUT, INPUT}, {LOW, LOW, HIGH, LOW} }, // LED 6: A high, D low { {OUTPUT, INPUT, INPUT, OUTPUT}, {HIGH, LOW, LOW, LOW} }, // LED 7: D high, A low { {OUTPUT, INPUT, INPUT, OUTPUT}, {LOW, LOW, LOW, HIGH} }, // LED 8: B high, D low { {INPUT, OUTPUT, INPUT, OUTPUT}, {LOW, HIGH, LOW, LOW} }, // LED 9: D high, B low { {INPUT, OUTPUT, INPUT, OUTPUT}, {LOW, LOW, LOW, HIGH} }, // LED 10: C high, D low { {INPUT, INPUT, OUTPUT, OUTPUT}, {LOW, LOW, HIGH, LOW} }, // LED 11: D high, C low { {INPUT, INPUT, OUTPUT, OUTPUT}, {LOW, LOW, LOW, HIGH} } }; void lightOn(int led) { // Error handling: Ensure valid LED index if (led < 0 || led >= LED_NUM) { return; // Skip invalid requests to avoid pin misconfiguration } // Set pin modes for tri-state: OUTPUT for active pins, INPUT for off pinMode(A, matrix[led][PIN_CONFIG][0]); pinMode(B, matrix[led][PIN_CONFIG][1]); pinMode(C, matrix[led][PIN_CONFIG][2]); pinMode(D, matrix[led][PIN_CONFIG][3]); // Apply logic states: HIGH/LOW only on OUTPUT pins digitalWrite(A, matrix[led][PIN_STATE][0]); digitalWrite(B, matrix[led][PIN_STATE][1]); digitalWrite(C, matrix[led][PIN_STATE][2]); digitalWrite(D, matrix[led][PIN_STATE][3]); } void allOff() { // Set all pins to INPUT for high-impedance off state pinMode(A, INPUT); pinMode(B, INPUT); pinMode(C, INPUT); pinMode(D, INPUT); } void setup() { // Initial pin setup to high-impedance pinMode(A, INPUT); pinMode(B, INPUT); pinMode(C, INPUT); pinMode(D, INPUT); } void loop() { // Scan all LEDs sequentially for [multiplexing](/page/Multiplexing) for (int l = 0; l < LED_NUM; l++) { lightOn(l); // Activate specific LED via matrix config delay(1000 / LED_NUM); // ~83 ms per LED for visible sequential lighting; reduce to ~1 ms for flicker-free multiplex allOff(); // Deactivate all LEDs before next activation } // Full cycle repeats; total time ~1 second with above delay, but <1 ms per state transition in optimized use }

To test this code, upload it to an board with LEDs wired according to the matrix (e.g., LED 0 to pin A, to pin B via ; repeat for all pairs without shared connections to avoid conflicts). Expected output is sequential illumination of each LED for approximately 83 ms, creating a scanning effect across all 12 LEDs before repeating. For persistent display of multiple LEDs, modify the loop to activate combinations rapidly (e.g., nested loops over desired LEDs with minimal totaling <1 ms per full scan cycle), ensuring uniform brightness via adjustment. This approach is efficient for microcontrollers, as state changes via digitalWrite and pinMode incur negligible overhead (~microseconds per call), allowing refresh rates above 60 Hz for smooth visuals.

Circuit Design Example

A fundamental example of a Charlieplexing circuit utilizes three microcontroller pins to drive six LEDs, maximizing efficiency by exploiting the tri-state capability of the pins (high, low, or high-impedance). In this setup, the LEDs are connected between pairs of pins without a common cathode or anode, allowing each LED to light when one pin is driven high, the opposing pin low, and the third pin in high-impedance state to isolate the path. Current-limiting resistors are placed in series with each LED to prevent damage, typically 150Ω for operation at 5V supply with 20mA LED current, assuming a forward voltage drop (V_f) of approximately 2V for standard red LEDs. The required components include three (GPIO) pins from a such as an , six LEDs with V_f ratings between 1.8V and 2.2V (e.g., red diffused LEDs for visibility), and six 150Ω resistors (1/4W rating). For prototyping, a is suitable, but for permanent implementation, a (PCB) is recommended to minimize wiring errors and improve reliability in the cross-connected topology. Variations to the basic circuit can incorporate decoupling capacitors, such as 0.1μF ceramic capacitors placed across the power supply lines near the , to suppress electrical noise that might interfere with pin states during rapid switching. Circuit simulation tools like can model this setup by representing pins with voltage sources capable of tri-state simulation (using high-value resistors for impedance), allowing verification of current paths and LED illumination before physical assembly. To build the circuit, first connect the anodes and cathodes of the LEDs and resistors in a star-like pattern across the three pins (labeled P1, P2, P3): for instance, LED1 anode to P1 via resistor, cathode to P2; LED2 anode to P2 via resistor, cathode to P1; LED3 anode to P1 via resistor, cathode to P3; and similarly for the remaining three LEDs between P2-P3 and P3-P1 pairs. Secure connections on a breadboard by inserting components into rows and using jumper wires for pin links, ensuring no shorts between adjacent rows. After wiring, verify the high-impedance (Z-state) by setting all pins to input mode and measuring resistance between pins with a multimeter, which should exceed 1MΩ to confirm proper isolation and prevent leakage currents.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.