Or how to drive nCR2 LEDs with only n GPIO. Where nCR2 is the combinatorics with repetition of n elements selecting 2 elements at the time.
For this article I will try to avoid the basics of LED functionality and focus on the application. I will limit myself to remind you that LEDs work like diodes, allowing the pass of current only on one direction and that they need a minimum voltage between their pins to start emitting light. From this what you should keep is that the bare minimum to have a working LED is one pin set to digital high (typical values go from 1.8V to 5V depending on the MCU and the LED we are trying to feed) and a second pin set to digital low (typically GND).
Let’s start by defining our constraints:
- We need a minimum of two MCU pins to drive our LEDs. This could be as simple as connecting all our LEDs to VCC and GND and call it a day, but…
- Let’s say we want to build a LED display where several lights should be on at different instants. We want to be able to control each of the LEDs individually. Again, we could just change the VCC for a GPIO, but this will imply that for N LEDs we will need N pins available in our MCU. This can become unmanageable pretty quickly, so..
- To keep our design simple, we want to reduce the amount of pins used. Also, we want to reduce costs, so we want to use as few pins as possible and buy a smaller MCU.
So far the minimum we can do is connect our LEDs to a GPIO and GND.
This will give us a couple of LEDs that can be driven independently. But right now we are using 3 pins of our MCU, how can we reduce this even more? What about this?
As long as we respect the polarity of the LED by setting one GPIO to HIGH and the other to LOW we should be just find. But I can hear you say: “Hey, but what’s up with the other LED”. And I will tell you, what about this configuration?:
We can easily see that if we set PIN1 to HIGH and PIN2 to LOW, LED1 will turn on but not LED2, while inverting them will set on LED2 but no LED1. This way, we only need 2 pins to drive our 2 LEDs. Furthermore, we can safely say that for every combination, order independent, of two GPIO pins in a MCU a LED can be driven using this configuration. This can be translated to the combinatorial:
L = nCR2 = n!/2!(n-1)!
So for n=3, L=6:
Important: Since a each pin will be connected to several LEDs, we need to set only one pin to HIGH and one pin to LOW, while any other pin on the circuit should be set to High Impedance to avoid turning on undesired lights.
One interesting thing about this circuit is that we only need one resistor per pin to limit the circuit, and since there will always be two resistor in series with the LED we just need to divide the value chosen for a single LED by 2.
But there is one problem left to solve: using this configuration, only one LED in the array can be on at the time and constraint (2) says that we should be able to control each pin individually; this includes having several pins turning on at the time. Well, actual displays have the answer for this. You see, when you see a 7 segments display with two or more digits, they way this device is operated is by turning on each digit for some time t measured in milliseconds and turning it off to cease control to the other digit. This works by taking advantage of a very useful property of our eyes called persistence of vision (link needed).
For our application, due to the persistence of vision, only one LED needs to be active at the time as long as each light source hits the eye for about 30 times a seconds (the exact time depends on the intensity of the light*) and, for any human looking at our display, all the LEDs will seem to be on at the same time. This will allow us to control each led in the matrix independently.
I hope this short article come of use for all of you next time you are designing with hard constraints on the amount of pins available in your MCU. Good luck!
*The actual time the LEDs stay on will depend on the amount of LEDs we have, the speed of the microcontroller and the intensity of the LEDs we are working with.