leddrvr2 - Copyright (c) 2002 Tundraware Inc., All Rights Reserved
$Id: readme.txt,v 1.3 2002/05/02 18:58:17 tundra Exp tundra $
PLEASE NOTE: Before using anything in this archive, you must read, and
agree to abide by, the licensing terms found in:
Makefile Unix-style makefile which runs under DJGPP and
should also work under MKS and Cygwin.
README.txt This file.
leddrvr2-sch.brd leddrvr2 PCB board layout in Eagle format.
leddrvr2-license.txt Licensing terms for using this software.
leddrvr2-sch.gif leddrvr2 schematic in GIF format.
leddrvr2-sch.png leddrvr2 schematic in png format.
leddrvr2-sch.ps leddrvr2 schematic in PostScript format.
leddrvr2-sch.sch leddrvr2 schematic in Eagle format.
leddrvr2.asm leddrvr2 software written in PIC assembler.
leddrvr2.hex The assembled leddrvr2 software.
leddrvr2.lst leddrvr2 listing file generated by assembler.
leddrvr2.xrf leddrvr2 cross-reference file generated by assmbler.
WHAT IS 'leddrvr2'?
'leddrvr2' is a simple 7-segment LED display driver system using PIC
technology. The idea is to use minimum parts count to drive up to 8,
7-segment LEDs using only 3 I/O pins on the PIC chip.
This is also a useful introduction to some of the PIC microprocessor
programming techniques insofar as 'leddrvr2' exercises many of the
basic features of these chips including:
- Programming & use of the TMR0 timer & prescaler
- Timer-based interrupt handling
- Asynchronous application/interrupt interaction.
- Display multiplexing
- Serial interfacing to external 'glue' logic.
I wrote this code so *I* could learn the PICs and in the process
actually do something semi-useful. I've tried to document the
hardware and software sufficiently to make 'leddrvr2' useful as a
learning tool for others.
In order to fully understand 'leddrvr2', it is useful to first
examine its predecessor design, 'leddrvr' which can be found at:
The following sections assume such familiarity and only comment upon
the newly implemented features found in 'leddrvr2'.
HOW DOES THE HARDWARE WORK?
The original 'leddrvr' design works quite nicely but has one huge
drawback: it uses all but 1 of the PIC's I/O pins. This really limits
its usefulness for all but the simplest of applications. The idea
behind 'leddrvr2' be able to drive an array of 7-segment LEDs using a
minimum number of I/O pins - in this case the design ultimately only
ends up needing *3* pins to drive up to 8 LEDs. (In fact, the design
can be extended for many more LEDs simply by adding more shift
registers and adjusting the code accordingly, all without the need for
any more I/O pins!)
'leddrvr2' is able to do all this with only 3 I/O pins because of two
outboard serial shift registers which are chained together - the
output of one drives the second. Instead of loading the segment and
LED selection bits in parallel as is done in 'leddrvr', this
implementation writes the bits out *serially* (via RA0-RA2) to the
shift registers. The shift registers take this serial stream and
convert it into the equivalent parallel control word to properly drive
the display hardware.
One consequence of this design is that 7404 drivers had to be added.
The PIC can drive enough current to light an LED segment, but the
CD4094 shift registers cannot. The 7404s take the logical output
level of the shift register and provide the necessary current drive to
light the LED segments.
This release of 'leddrvr2' includes a Printed Circuit Board layout
In Eagle format. This board has actually been built and works fine.
Most of the parts used in this project are pretty standard and you
should be able to find them at any good electronic supply store.
However, there are a couple of specific parts used here you should
know about. First, the 7-segment LED displays used are common cathode
devices. The board is laid out for the Jameco #24782 (LSD3211-11)
There is also provision for an On-Off switch. If you use an
off-board switch, any standard SPST or SPDT switch should work. If
you want to actually mount the switch to board as I have, use the
Jameco #71597 (MTS-102-A4) part.
You can see both the blank and the finished PCB at:
The original design included pulldown resistors (RN2) for the FET
inputs so that I could connect and disconnect the gates from the rest
of the circuit as I was debugging the hardware. In this final
circuit, the gates are hardwired to the shift register outputs and
this resistive termination is not needed. I left provision for RN2 in
the schematic and finished PCB, but I don't actually use it. This is
why you see an empty SIP socket in the upper right side of the
finished PCB. In any case, it does no harm to put a 10K termination
pack there if you like. One nice think about leaving the resistors
out is that you can use that socket to inspect the signal at each of
the FET bases with an oscilloscope.
WHERE TO GET THE PRINTED CIRCUIT BOARD
This is a very nice PCB prototype house. They are located in Bulgaria
so shipping can take a couple of weeks to the US or Canada unless you
are willing to pay a premium for expedited delivery. However, their
prices for boards are the lowest I've found and their product quality
and turn around time are excellent judging from my experience. The
board layout provided here is designed to fit twice on their standard
prototype panel, so you actually get *two* boards for their standard
price. As of 5/2002, I was able to get 2 boards delivered to the US
for just over $40! This is *very* inexpensive.
You send them the Eagle .brd file via email and they will respond with
a price estimate. You then sign the order, provide Credit Card
information and FAX it to them. Several weeks later, you should have
your boards. I especially like the fact that I do not have to fiddle
with post-production tasks like producing Gerber files, drill files,
silk screen layer information, and so on. They do it all for you from
the Eagle .brd file.
Try these guys out - I think you will really be happy. I was.
HOW DOES THE SOFTWARE WORK?
The 'leddrvr2' software is structurally quite similar to the original
'leddrvr' code, though it should be noted that some of the variable
names have changed. A few other core differences are worth noting:
- Be forewarned: 'dsply_val' is the register set which holds the
current value to display. It is allocated to be (num_led/2)
bytes in size. This assembly-time allocation works so long as
num_led is *even* but will fail if it is odd. Subsequent use
of this array in 'display_led' also expects there to be the
correct number of bytes of storage here, so make sure you have
it right if you're using an odd number of LEds.
- 'led_lookup' has been expanded to accomodate 8 LEDs.
- The overall code works pretty much the same way it did in
'leddrvr' with one big exception: 'display_led' no longer
actually writes to the LED control hardware. Instead,
'display_led' now computes the desired segment selection bit
pattern and stores it in 'led_seg'. Similarly, it computes
the LED selection bit pattern and stores it in 'led_sel'.
'display_led' then calls a new routine, 'led_wrt' which reads
these values and shifts them out in correct order to the
outboard shift registers, thereby illuminating the LEDs as
- In 'leddrvr', 'display_led' did some explict offset arithmetic
to figure out what value to display on each LED. This has
been changed in 'leddrvr2'. Now, 'display_led' does pointer
arithmetic to compute an offset from the base of the
'dsply_val' array. This is a more general approach which
should work without modification as more LEDs are added.
- Most of 'led_wrt' should be fairly self-explanatory, but a few
clarifying comments might be helpful:
a) The 'STR' (strobe) input of the shift registers
allows data to be moved into the register
without actually having it appear on the output.
When the registers are finished being loaded,
the parts are strobed and the LED display is
I experimented with pulling the 'STR' pins
high permanently (and thereby freeing up another
I/O pin). This effectively causes all changes to
the shift register to be immediately reflected
on the LEDs. This does still work, but there is
an annoying flicker in the unused LED segments
which are being lit as data shifts across the
registers. So, I went back to using the STR line.
b) Clocking occurs when the 'CLK' pin of the
CD4094 goes from low to high with one
exception: The output of the high bit (Q8) does
not propagate to the shift register ripple out
(QS*) until the clock transitions from high to
low. This delay is necessary so that bit
ripple is properly implemented without race
condition timing concerns. In any case, this
is transparent to the software, which sees the
two shift registers as a single 16-bit logical
c) Data is shifted into the shift register by
placing the bit value on the 'D' input (RA1)
and then clocking it into the register by first
raising and then lowering the 'CLK' pin
(RA0). After all 16 bits have thus been shifted
into the shift registers, the devices are
strobed via 'STR' (RA2) as described above.
This causes the new bit values to actually
propagate to the LED hardware.
- One word of caution: 'display_led' is called as a subroutine
from the TMR0 Interrupt Service Routine. It, then, in turn,
calls 'led_wrt' as a subroutine. This is kind of dangerous
because the PIC has very limited stack space. So long as
you are certain that you will not see another interrupt
while servicing the TMR0 interrupt, this should work.
However, if you end up building a system that needs to be
able to interrupt during ISR handling, you should
restructure this code to be inline in the TMR0 ISR and *not*
use subroutine calls.
The basic TMR0 interrupt rate of 1ms found in the original 'leddrvr'
design is preserved here. 'leddrvr' only drove 4 LEDs so each one got
updated every for 4ms - an effective multiplex/refresh rate of 250 Hz.
The 'leddrvr2' design is extended to handle 8 LEDs. This means the
multiplex/refresh rate is 125 Hz. This is probably as slow as you
want to go or you'll begin to see some display flicker.
If the design is extended for more than 8 LEDs (by adding more shift
registers), the multiplex/refresh rate will degrade in direct
proportion to the number of LEDs supported. This will certainly
introduce unacceptable display flicker almost immediately. This
problem can be solved by changing the TMR0 interrupt rate to something
faster than 1ms.
If you do this, note that the 'wait' subroutine assumes a basic timing
interval of 1ms. This routine will have to be modified to accommodate
any TMR0 timing change you make.
As a matter of practicality, this design is way better than the
original 'leddrvr' approach. However it raises the parts count and
therefore the real estate and cost to build a real system. Even so,
this higher parts count is still somewhat cheaper than using a
specialty chip like a MAX7219.
This approach also caused the code size to grow somewhat (from 117 to
150 bytes) but this is probably a small price to pay for the vastly
reduced pin count.
As a matter of simplicity and minimizing parts count/board size, using
a dedicated part like the MAX7219 is probably preferred. But where cost
is concerned, this design is probably somewhat better.
Like all engineering, these are *tradeoffs*, not absolutes...
I'D LIKE TO THANK THE ACADEMY...
Lots of nice people on Usenet answered my stupid questions as I've
been learning the PIC. Their help is very much appreciated.
You should feel free to poke around the code, change things, and
generally experiment with what is there. It's the best way I know to
learn something new. If you have any other thoughts / fixes /
questions / improvements for 'leddrvr2', please let
Happy Hacking! ...