The idea behind multiplexing is, basically, taking advantage of the limitations of the eyes and the brain.
People are limited to process changing images up to a certain frequency (typically around 25 Herz).
Television and movies use this to present a `moving' image while actually just presenting individual pictures at a speed over 25 per second.
In some countries the frequency of the net is 50 Hz, in others 60 Hz. An ordinary light bulb does not flicker, although it runs on alternating current. Same principle.
Multiplexing will just change the columns (or rows, but in practice columns are easier to implement and, very important, extend) at such a rate that the eyes/brains `think' different patterns are shown.
The example in `matrix.png' shows two led displays, each 5 columns and 7 rows. Only one led is shown in each display (lower left), just use your imagination for the other 34/68.
The uC is not shown because the exact connections are not important for the explanation.
Current limiting resistors are only needed for the 7 rows. The reason for this is that, in each row, there is only one led active at any time.
The UDN2981 (upperleft) takes care of the rows. The corresponding rows of the displays are connected. This is possible because only one column active at any time.
Implementation
In so-called pseudo code
numColumns = 10
aMatrix[1..numColumns]: byte
aColumn: byte
//Fill aMatrix with 10 bitpatterns
aMatrix[1..numColumns]: byte
aColumn: byte
//Fill aMatrix with 10 bitpatterns
Repeat
The bitpatterns look like this, they are swapped left-right and rotated left. Each row, stored as a byte, beFor aColumn 1 to 10
Clear the port connected to the UDN2981 (Shadows, later in this document)
Activate aColumn (Activation, later in this document)
Put aMatrix[aColumn] on the port connected to the UDN2981
Next aColumn
Forevercomes a column on the display.
pattern for a '0': -
0111110
1000001
1000001
1000001
0111110
pattern for a '1': -
0000000
0100001
1111111
0000001
0000000
etc, etc, etc...
The bitpatterns are stored in a table, in Proton this is extremely easy to do because you don't have to deal with the inner workings of the PIC, CData and CRead will do the trick for you.
To really understand multiplexing you have to form the image in your mind.
What happens?
The ten bytes in aMatrix all contain 1 byte that wil form 1 column.
Here is a picture of the matrix containing the filled columns: -
01110 00100
10001 01100
10001 00100
10001 00100
01110 01110
On a display:-
[IMG]file:///C:/temp/msohtml1/03/clip_image001.gif[/IMG]
But since the port connected to the UDN2981 (the column) can hold only 1 byte at the time, we have to rotate through the columns very fast.
For column 1 the following pattern will light up: -
all other columns do not show anything because they are disabled.
For column 2 the following pattern will light up :-
[IMG]file:///C:/temp/msohtml1/03/clip_image003.gif[/IMG]
again, all other columns do not show anything because they are disabled.
For column 3 the same following pattern will light up: -
[IMG]file:///C:/temp/msohtml1/03/clip_image004.gif[/IMG]
again, all other columns do not show anything because they are disabled.
And so on, until column 10 after which the column counter will restart at 1.
When using (only) ten columns, each column will light up for 10% of the time.
Implement the pseudo-code given above and insert a Delayms 200 just before `Next aColumn'. (0.5 Hz)
You will see the columns lighting up one by one.
Decreasing the delay will result in two things: -
- the columns light up faster, but for a shorter time
- the brightness decreases
Decrease the delay to 20 ms.
Decrease the delay to 2 ms.
Decrease the delay t0 20 us
Delete the delay instruction.
The code in real life
Because Wastrix asked me to publish this stuff on the WIKI, I decided to build a prototype.
All I had lying around was an experimenter board containing a PIC18F452 running at 40MHz and an 8-by-8 common cathode led matrix.
So I took two more breadboards and started plugging in breadboard cables.
You can find the schematic in `matrix2.png'.
The ULN2803 should be familiar by now.
The UDN2981 from the first schematic is not used here because I use only 1 display and relatively large current limiting resistors (the setup draws 25 mA, any single pin can supply this much).
Please remember, and I know I forget every now and then, that you need some kind of low-side driver to get this working properly.
PORTB drives the rows, PORTC the columns.
Activation of the columns is simply done by bit-shifting %10000000 through PORTC.
The code, which is basically a straightforward PicBasic version of the pseudo code in `Implementation', can be found in the zipfile, it is called `multiplex_demo.bas'.
Activation of the columns
Activation of the columns is a hardware/software issue. There are many solutions to this but, to be fast, decoder chips are preferred.
In the first circuit, a uC having 17 I/O pins available will be sufficient. The second circuit only needs 16 I/O lines.
But if you need 6 displays, e.g. for a clock, or 64 for a running message board, a different solution is called for because you do not have enough I/O lines.
I try to avoid shift registers, like the 74595, because controlling a 74595 takes a lot of processor cycles and, equally important, a 74595 is not capable of delivering the current needed. The latter is the reason why the first circuit employs UDN2981 and ULN2803 IC’s. Both are capable of delivering currents most 74xx(x) and 40xx IC’s can not deliver.
The UDN2981 is a high-side driver, the ULN2803 a low-side driver. Both can drive 500mA typically. See the datasheets.
For a number of circuits I used 4028/74138/74237 IC’s to control the columns. The 4028 and 74138 are decoders driving 1 line HI at the time, the 74138 drives 1 line LO at the time. All three IC's have some sort of enable mechanism allowing you to address multiple displays.
I prefer the 4028 because it will control 10 outputs, where the 74’s will only control 8 outputs.
The number of control lines needed for each solution is 4, effectively doubling the number of I/O lines. See the datasheets.
Of course other solutions are possible. Have it your way, experiment. But always take care of speed, maximum current per output and maximum dissipation per device.
Shadows
So you have a certain value on column 3.
Next you increase the column to 4, fetch the new value for the next column and put it there.
What happens is that the hardware already activates the new column, but with the OLD value. After that the new value is displayed.
So, for a very short moment, the ‘old’ value is present on both column 3 and 4.
This results in shadows and they are not wanted in this type of display.
To avoid this use the next sequence: -
- clear the column
- increase the column counter and activate the new column
- fetch the new value and put it in place
Now column 3 will go blank for a very short moment. But the amazing thing with eyes and brains is that a short blank is ‘skipped’, while a short shadow is noticed.
Brightness of the display
It is important to understand that, since each column will be active for a part of the time (effectively 1/number of columns), the brightness of the led’s will be less than when they would be activated continuously. Experimenting with the value of the current limiting resistors is necessary for the very simple fact that each led/display is different.
Advise: - Start at the value that works for continuously activation. From that point start decreasing the value of the resistors until both brightness and current used are acceptable.
Expanding possibilities
The pseudo code presented will work (as you can try out for yourself by building a circuit like `matrix2.png' and using the code in `multiplex_demo.bas'), but will allow for only very limited possibilities.
The first thing to do is to put the part between `Repeat' and `Forever' in an Interrupt Service Routine (ISR). You can use any timer you like. For displays I always use Timer0 because every PIC has this one.
Using an interrupt routine you are able to do anything, like changing the bitpatterns, and be sure the display will be smooth.
ISR_routine
If aColumn > maxColumn then
Select aColumn
aColumn = aColumn + 1
Return from interruptDeactivate all columns
Do all kinds of initialization
aColumn = 1
EndifDo all kinds of initialization
aColumn = 1
Select aColumn
Clear the output
Case 1
EndselectActivate column 1
Get bitpattern for column 1
Put bitpattern on specified port
Case 2Get bitpattern for column 1
Put bitpattern on specified port
Activate column 2
Get bitpattern for column 2
Put bitpattern on specified port
Case EtceteraGet bitpattern for column 2
Put bitpattern on specified port
aColumn = aColumn + 1
Main
aColumn = maxColumn + 1
Repeat
; do your stuff
Forever
Okay people, I’ll be frank with you.
Up to now I NEVER have used interrupts in Proton. If I want to do that, I use Assembly.
But I insisted that you should use them, so I have to put my money where my mouth is.
Today I spent approximately 3 hours to get it working.
(To be honest, it took me 1 hour to find that stupid error that had nothing to do with interrupts whatsoever.)
The code for this is in the zip file, it is called `multiplex_int_demo.bas'.
You’ll encounter some assembly code, but it is documented.
The interrupt routine works as follows: -
Every time it is entered (and that’s quite often in the example) it will first clear the output to avoid shadows, activate the new column, fetch the value for aColumn and put it on the output, the column activator ixCol is shifted right.
After that aColumn is increased and when it is bigger than numCols it is reset to 1 and the column activator ixCol is reset to %10000000.
You may wonder why I disable and re-enable the Timer0 interrupt. Theoretically it is not necessary, but I just do it for any routine that may cause the Timer0 counter to overflow during the interrupt handling. MicroChip states that, during an interrupt, all interrupts are disabled, so I don’t think there’s any harm.
What’s the fun of it all?
At the end of the code there are 5 bit patterns, the last all zeroes.
The MainProg loop will change the active pattern every 100 ms. This is done by the routine FillPattern. It uses an offset to determine the starting position in the table.
As you may see, I also disable Timer0 during the change of pattern.
Practical implementations
The number of led’s to be driven is only limited by the number of I/O lines available and the hardware to control them.
A 4-by-4 display (16 led’s) can be built using only 8 I/O lines, an 8-by-8 or 4-by-4-by-4(64 led’s) using 16 I/O lines.
If you want to build a massive 8-by-8-by-8 led cube (512 led’s) you will need 24 I/O lines.
A 40 pin PIC like the PIC18F442 has 33 I/O lines available (A:6, B/C/D:8, E:3). If you use one with an internal oscillator this will free up one extra line.
Some of my own implementations
Friends of mine recently bought a house and they asked for "a clock".
But `a clock' is just `a clock', it should have some extras or else you could just by any clock in any store.
So I built a clock using 6 5x7 bicolour displays (420 led’s) and a PIC18F442.
The bicolour displays (red/green) allow for three colours (red, orange/yellow, green).
Two UDN2981’s are used, one controlling the red rows and one controlling the green rows.
Thirty (5*6) bytes are used to store the active bit patterns for the digits and 30 bytes to control which colour is wanted (I could have used less storage here, but this was the easiest programming solution and the PIC18F442 has plenty of storage).
Controlling the 30 columns is done using 3 4028’s followed by 4 ULN2083's driving the actual columns.
Timing is done using a 32768 Hz crystal driving a 4060 followed by half a 4013 to obtain a nice 1 Hz pulse (actually calibrating the clock took most of the time since it takes days to detect differences comparing it to the output of a GPS).
The fun thing, of course, was to come up with various patterns.
Basic, of course, all Red, all Green or all Yellow.
But the 30 `colour' bytes also allow for more intricate patterns: -
- Hours, minutes, seconds each their own colour (nice, much better then all Red/Green/Yellow)
- Columns varying, e.g. Red, Green, Yellow, Green, Red for each display
- Colours shifting from left to right
- One background colour and one column in a different colour shifting left, shifting right or bouncing from left to right (eat your heart out Knightrider!).
A touch button allows selecting one of the available patterns.
The circuit draws about 180 mA.
The schematic and code are, of course, available for anyone wanting to use it. But beware Proton-lovers, it's 100% ASSEMBLY.
I have an 8-by-8 matrix of RGB led’s. That's 192 led's.
Same principle, only now each of the 192 led’s is addressed separately.
One of my favourites, though by no means very complex: -
4 SMD RGB led’s mounted at the side of opaque ‘plexiglas’ (sorry if that does not ring a bell, that's the name in the Netherlands). Slowly increasing and decreasing colours, shifting colours, random colours, whatever.... People love it, I had to build a number.
Epilogue
Start simple with just 2-8 columns of led's to grasp the fundamentals.
When you think you're in control, take it to the next level.
And always: -
- make a backup of your code (Proton will not do this for you)!
- enjoy experimenting!


Menu
Recent Articles


Using PDS with SPI GLCD based on ST7565R Controller
Graphic LCDs based on the ST7565 are cheaper then GLCDs with other controllers. SPI requires only four pins. If the circuit