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 IO's, making it suitable when there is no need of many ports.

For this reason we are not going to adopt the Freedom II board 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 8x8 LED matrix display got LED's anodes 'exposed' as rows and cathodes shared in every column:

8x8 matrix led display pinout

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 columms we choose to tie to the ground will eventually light up the same LEDS on the rows.

Shift registers

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:

74hc595 animated

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; 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?

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:

transistor array

Now we got everything in place to operate the 8x8 LED Matrix display; things can then be arranged in this guise:

8x8 matrix led display 74HC595 shift registers ULN2803A transistor array

with the two 74HC595 daisy chained marking the row-column combinations and the ULN2803A sinking current.


See also gEDA downloadable file below.


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.

Add new comment

Filtered HTML

  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <p> <h1> <h2> <h3> <h4> <h5> <div> <pre> <object>
  • You may insert videos with [video:URL]
  • Web page addresses and e-mail addresses turn into links automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.