Table of Contents
PIC18: 8x8 LED Matrix display and shift registers
In this experiment we will use PIC 18F4550 little bro, the 18F2550; it has (mostly) the same features as its oldest sibling but with a reduced number of I/O's, making it suitable when there is no need of many I/O ports.
For this reason we are not going to adopt the [Freedom II board](http://www.laurtec.it/progetti-elettronici/sistemi-automatici/76-scheda-di-sviluppo-per-pic-freedom-ii) that previous articles featured, but will use a common breadboard instead. It will be an occasion to refresh our memory as to which are the absolute minimum connections/components that a PIC18 needs in oder to correctly operate.
8x8 LED matrix display
A common cathode 8×8 LED matrix display got LED's anodes 'exposed' as _rows_ and cathodes shared in every column:
In the image current enters rows by being sourced through the eight pins on the left and flows out by being sinked through the common cathodes at the top (it's only a visual representation: real pins are on top and at the bottom of the display, with rows and columns mixed); so if for instance we want to light up 4 particular LEDs on the 3rd column we should connect the correspondent rows to +5V, protecting the connections with suitable resistors, and cathode pin 4 to ground:
But suppose we want to light up LEDs that reside on different rows and on more than one column at the same time; that wouldn't be possible with a static configuration as that one above, because the different columns we choose to tie to the ground will eventually light up the same LEDS on the rows.
Enter shift registers; those chips accept a serial input, one bit at a time, and give a parallel output at the same time. That's how.
Basically they need three pins, which are:
- DATA_DS_pin: used to set Data bit 1 or 0
- CLOCK_SH_CP_pin: by doing a Low-to-High transition, it saves Data bit serially, one bit at a time, into the 74HC595 buffer
- LATCH_ST_CP_pin: it outputs Data bits stored in the 74HC595 buffer parallelely
The shift register is rather quick and there are no particular timings nor delays to consider by doing the above transitions, even with a fast 20 MHz clock like the one used in this experiment.
As we can see in the animated gif below:
- DATA wait to enter serially the shift register on the DS pin
- as soon as there is a Low-to-High transition on the SH_CP (CLOCK) pin the datum is pushed down to the 74HC595 buffer
- whenever the ST_CP (LATCH) pin is set the byte is output parallelely on the Q0-Q7 pins:
Ok, now that we understood that shift registers are useful, because they allow us to spare output pins on our PIC, how can they serve our purpose, which is to display LEDs on different rows and columns? Simple: by strobing or multiplexing, as we saw in [this article](https://www.t3ch.it/?q=pic18-7-segment-displays).
Every column will be on only for a fraction of time needed to light the LEDs on the desired rows
Then the next row will be activated with the LEDs lit on the desired rows. And so on.
In practice for every column we need two pieces of information: the rows to light up and the correspondent column. That is we need two bytes.
The cool thing about 74HC595 chips is that there is another pin Q7S which can be used to daisy chain two or more shift registers; it connects to the DS (Data) pin of the next chip. So, if we consider two shift registers as in our project, the first byte transits temporarily into the first chip and then, through the Q7S-DS connection, it ends into the second register. In the meantime, the subsequent byte is loaded into the first register.
Now, that's something we can use in our scenario; let's suppose that the first byte, the one that goes to the second daisy-chained shift register, signals the columns that have to be light up while the second byte, the one that ends up into the first shift register, marks the rows that have to be lighten up in that column: could that work?
Nope, because LEDs of a column share a common cathode, so columns current must be sunk; and the 74HC595 chips cannot sink more than 20mA on each Qn, that means that only a single LED could be safely lit up on a single column. We need another device that can manage the needed current for all the desired LEDs and what a more suitable device than a transistor or better a transistor array?
The transistor array we are going to use is the ULN2803A; as stated on its datasheet it “consists of 8 npn Darlington pairs, with 500mA of rated collector current for single output, more than enough for our use. The outputs of the shift register are connected to the base of each transistor:
Now we got everything in place to operate the 8×8 LED Matrix display; things can then be arranged in this guise:
with the two 74HC595 daisy chained marking the row-column combinations and the ULN2803A sinking current.
The code for this program, available for download at the bottom of this page, is quite simple; as we mentioned above for every column we need a couple of bytes, one which marks the active column and the other for the rows:
movlw b'10000000' ; Load SerialData reg with value call SendSerialData ; Data goes to the daisy-chained (2nd) 74HC595 movlw b'00000000' ; Load SerialData reg with value call SendSerialData ; Data goes to the 1st 74HC595
The SendSerialData routine takes one bit at a time of the two bytes and saves them (or pushes them down as for the above animation) the shift register storage, by doing a Low-to-High transition on the Clock pin:
bcf CLOCK_SH_CP_Pin ; CLOCK; LOW-to-HIGH bsf CLOCK_SH_CP_Pin ; transition
Once the entire byte is saved inside the shift register storage it is paralleled out, by setting Latch pin:
bsf LATCH_ST_CP_Pin ; Set Latch pin: parallel output
Latch pin is then cleared, ready for the next time:
bcf LATCH_ST_CP_Pin ; Clear Latch pin: parallel output
Now the code can proceed with the next columns, with the same concept.