logo
background
 Home and Links
 Your PC and Security
 Server NAS
 Wargames
 Astronomy
 PhotoStory
 DVD making
 Raspberry Pi
 PIC projects
 Other projects
 Next >>

PIC LED display

PIC text LED display

PIC based rolling text (LED) display


The following exercise in driving LED's uses techniques from Microchip application note 91029a.pdf

This project is to build a 'marching display' using a single (cheap) PIC chip. LED displays typically come in 8x8=64 LED 'blocks' (16 pin, Common Anode) for less than 40p each. Many incorporate a driver chip, typically the MAX7219 (driving a Common Cathode display) at a cost of approx £1 per block. The advantage of using a driver is that they only require 2 i/o pins (data, clock) to control a whole 8x8 matrix

The MAX7219 is designed to drive a '7 segment display', which contains 7 segments plus a 'decimal point' i.e. 8 'elements'. However the chip supports a 'no decode' mode which allows a single line of 8 LEDS to be driven at any time. The chip supports a total of 8 "7 segment displays" thus allowing it to drive the whole 8x8 matrix (see how to drive a 8x8 (64 dot) matrix)
 
The MAX7219 is driven by a 2 wire 'SPI' serial link (clk & data) 'control'. So that it can be 'daisy chained', the MAX7219 has a 'data out' which can be liked to the 'data in' of the next chip in the chain. This allows a 'marching display' to be built using multiple 8x8 'blocks', which are designed so they can be laid end -to-end without leaving a gap and driven by 2 PIC i/o pins.
 
Note that it is possible to purchase a pre-assembled 5 or 8 block 'matrix', however this is frequently more expensive than purchasing multiple individual blocks !

Driving LED's

LED's are usually 'rated' to be driven by some specific 'continuous' current, typically 5-20mA. This mA rating is for continuous drive - if you 'pulse drive' the LED, it's the 'average' mA that counts = so a 20mA LED can be pulse driven at 40mA for half the time and, so long as it's not driven at all for the other half, this results in an average of 20mA. The device will typically have a 'Max peak current' rating, typically about 5 to 6 times the average (various ones I have are rated at 90-110mA), so 2A for 1/100th the time isn't going to work - or at least not for very long :-) )

One reason for pulse driving LEDs is to allow a single circuit to drive multiple LEDS, by switching quickly from one to another (so for 2 LED's, each is driven at 'double current' for half the time).

However a more typical reason is to 'make them look brighter' = the human eye tends to 'see' the peak brightness rather than the 'average' = so double the current for half the time results in a much higher 'perceived' brightness than a continuous drive at normal current.

In the example of driving 32 LED's (above), if the normal current is switched between them (so each gets only half the normal current), the human eye will see both as 'almost full brightness' (rather than half brightness). Using 'pulse' driving thus reduces the total current and leads to considerable power saving (a key requirement in any battery powered application).

Whilst low current LED's are often also 'high efficiency', you do need to check the light output ('milli-candles' or 'mCD') to make sure you have the best 'light output per mA'.

Whilst high efficiency Matrix LED's do exist with 'nominal' currents in the 5mA region, these are rather more specialist (read, expensive) items. The typical more generally available (i.e. cheap = 99p each) RED 8x8 Matrix displays is rated as follows :-

Max Continuous Forward Current (IFM): 20mA.
Max Peak Forward Current (IFP): 100mA.
Max Power Dissipation (PM): 40mW.

IF you are just driving a few LED's by sourcing current to the LED (which is connected to Gnd), then you only have to 'count' the source current loading on the PIC. HOWEVER if you are driving a 'matrix' = where you enable the current 'source' to the LED 'row' and then 'select' a 'column' to drive by 'turning on' the 'sink' for that 'column' = you have to 'count' both the source and sink current as part of the PIC loading. Pulse driving is one way to cut down the 'effective' (or 'average') loading on the PIC

Pulse duty cycle

Pulse driving takes advantage of the human eye's sensitivity to the 'maximum' brightness of the LED. This means driving a display at 'normal' mA for even a small percentage of the time results in the human eye 'seeing' the display as (almost) 'normal' brightness, despite the fact that 'most of the time' it's actually 'off' ! This lets us reduce the loading on the PIC i/o pins and reduce the total current consumption for battery driven applications.

A more common reason for pulse driving is to get significantly more light out of the LED, for example when using LED's as a photographic 'flash gun' (where significantly higher currents - up to 5x - are applied for are very short time - in the order of 1/1,000th of a second (1mS) - although it is to be noted that focal-plane camera shutter speeds must be above the flash duration).

As a 'rule of thumb', whilst you can drive at 2x for a duty cycle of 50% (or less), to drive at 3x rated current you should keep the duty cycle below 10%. Reports can be found of users driving 3mm red LED's (continuous rated at 20 mA) at 100 mA (i.e at the peak rated) with an on-pulse duration of 100 microseconds (1/10,000th s) at 0.1% duty cycle !

Note that, as the current is increased, the efficiency of the LED drops (i.e. Vfwd creeps up) and thus the heat generated increases faster than you might expect (at 3x the current, efficiency typically drops by about 30% i.e. 3x current is about 2x brightness, not 3x = the 'missing' brightness is simply going into heating up the LED).

As the temperature of the LED increases, efficiency drops even further = hence the 'run-away heat failure' if you keep driving them too fast to allow the heat to dissipate. Note that red LED's are more effected than other types = at 80 degrees C a red LED is operating at 50% efficiency, and by 120 degrees at only 30% (at which temp. they typically fail)

Pulse frequency

What's the 'maximum' number of LED's you 'get away with' before the human eye notices the 'off time' (as 'flicker') ? Well, one 'rule of thumb' (based on UK 50Hz mains power = fluorescent tube) and the PAL TV standard is that you need to cycle through all the LED's at least 50 times a second. However, when LED computer displays started to become common, it was realised that modern LED's 'switch off' much faster than the old 'phosphor' TV's and fluorescent tubes , so these days we aim to run at 100 Hz or better.

LED 'counts' are high for multi-character displays. The typical '4 block' (8x32) display contains 256 LED's, and getting through all 256 at 100 Hz gives us a display LED frequency of 25.6 kHz !

This is actually 'easy enough' to achieve with a PIC OSC of 20MHz (the CPU instruction CLK is 20/4 = 5MHz, meaning we have 5000/25.6 = 195 CPU instructions per LED. The problem is that each of the 256 LED's will only be 'on' for 1/256 th of the time i.e. the duty cycle will be less than .5% - which means they are going to look quite 'dim', even if we manage to drive them at 3-5x nominal current (no matter how much the human eye 'sees' the 'maximum').

Fortunately, the 8x8 'block' wiring allows us to select (and drive) 1 column (8 LED's) 'at once', giving us a more 'reasonable' duty cycle of 3% (and frequency of 3.2kHz), however even driving the LED's at 'nominal' current then runs us into the PIC i/o current limits

PIC i/o pins are typically rated at 20mA (source) / 25mA (sink) 'each', however the i/o PORT (a group of 8 pins) also has a current limit (typically 100mA) which means you can't drive all 8 pins in one PORT at the maximum !

There will also be a package limit (typically 200mA) which means you can't drive all PORTS at the maximum anyway (and if we are both 'sourcing' and 'sinking' current, we have to count BOTH as part of the loading !)

If the PIC will be driving 8 LED's 'on average' (i.e. constantly cycle through up to 32 sets of 8), the PIC package will limits us to a maximum of 25mA per LED (by driving 4 LED's from one port and 4 from a second we stay within the 100mA port limit). The problem is the common 'sink' = this will need to handle 200mA !

In theory, multiple PIC pins could be 'commoned up' to handle more current, however to avoid a single 'faster' or 'more efficient' pin burning out we then have to use current sharing resistors (plus, of course, we quickly run out of PIC pins ...). An alternative is to fit 'drain' transistors (one per column will be required i.e. 8 per 8x8 matrix block), however the cost will likely exceed that of using a dedicated LED matrix driver chip

NB. If you 'short circuit' a PIC i/o pin it will naturally limit to some current (less than infinity :-) ). At the sort of duty cycles mentioned above (3% at 3.2kHz) we can directly drive the LED (i.e. without a current limiting resistor) so long as the PIC pin current limits to a value lower than the maximum 'instantaneous' current the LED can withstand (typ. 100mA) and we 'turn off' the i/o pin again before it (or the LED) burns out !

To limit the maximum 'instantaneous' current we can place a single current limiting resistor between the PIC package V+ power pin the and the power supply rail (note - this has to be set so the PIC supply does not drop below the minimum for the OSC frequency being used :-) )

Current limiting resistor

If you apply a voltage greater than Vf (forward voltage drop) to a LED, it's resistance becomes essentially zero. So you need to limit the current with a series resister

LED drivers usually have some sort of current limit circuits 'built in', however if you are driving direct from a PIC i/o pin you need to run the numbers. At 5v, a PIC i/o pin 'Hi' is 5.0-0.7 = 4.3v, Lo is (typically) 0.6v (a LED is 'driven Lo' by wiring the other end to 5v). Max source/sink current would be 20mA. See below for worked examples :-
 
LED Vf depends on the color of the LED, theoretical values are :-
650nm red, 1.89v, 120/125 ohms
550nm green, 2.25v, 102/107 ohms
470nm blue, 2.62v, 84/89 ohms
(resistor pairs are '20mA source from Hi / 20mA sink to Lo'. For 5mA LEDs, multiply the resistor values by 4)
 
However actual LED light output colour (frequency) can differ from that shown above (and some LED's output a broad range of colors eg 'white' LED's), so it's always a 'good idea' to check the spec. sheets before burning out the LED (or, worse, the PIC i/o pin) :-)

Theoretical i/o pin counts

This is really only of interest to those using high efficiency matrix blocks (with 3-5mA LEDs) - a PIC has insufficient 'sink' capability to support more than half a dozen individual 20mA LED's directly

Each display 'block' of 64 LED's is arranged in a square grid 'matrix' of 8 rows of 8 columns. To select (drive) a single LED, one column and one row line are 'activated'. You can drive multiple LED's in a single column by activating one column and multiple rows. 8 rows and 8 columns means 16 i/o pins are used.

Adding an extra 8x8 block only requires 8 extra i/o pins since you can 'common up' on the rows (or columns). So driving 3 blocks requires 8 row + 24 column = 32 i/o pins - and this is beyond the i/o pin counts of low cost DIL package PIC's (the PIC 16F57 only has 20 i/o's, the PIC 16F882 24, and whilst the 16F59 does have 32 i/o's that would leave none for control or comms)

Minimising the i/o pin count means 'double using' the i/o pins. First, we note that each pin can act as either a source or sink of current - and LED's can't be driven 'backwards'. So, if one LED is wired up 'forwards' and the other 'backwards', across two i/o pins then, depending on which pin is 'sourcing' and which is 'sinking', only one LED will be 'activated'. This means with 16 i/o pins you can actually drive two 8x8 blocks (by wiring one block 'forwards' and the other 'backwards').

The next step is to note that to 'turn on' a LED you have to overcome the 'forward voltage drop', typically 2-2.5v. So, when two LED's are wired in series, so long as the applied voltage is less than 4v, neither will turn on (this can be 'guaranteed' by running the PIC from a 3.3v supply). This means we can use 3 i/o pins to activate one of 6 LED's (the 3 pins are used in 3 sets of 2 pins, each set of two being wired to a pair of LED's, one forward, one reverse)

This means we can drive 6 blocks of 8x8 by using 3 only sets of 8 i/o pins i.e. 24 i/o pins. Unfortunately, driving 4 blocks still requires all 3 sets of 8, however the 16F59 with it's 32 i/o pins can be used to drive 4 blocks just fine

The problem (mentioned above) is the max. continuous sink current of a PIC i/o pin is only 25mA. So we can 'sink' 8 row LED's at 3mA (24mA) just fine, and maybe 'get away with' 5mA (40mA) LED's 'on average' (i.e. so long as they are not 'all on' at the same time), but 20mA LED's (160mA) are plainly a 'non-starter' (unless transistors are used as current sinks)

8x8 matrix with integrated driver

An alternative approach (which avoids the current 'sink' problems) is to stick to displays with integrated drivers (such as the MAX7219). To control the MAS7219 you need only 2 i/o pins (data and clock) - and because the MAX7219 can be 'daisy chained', these same 2 pins can drive any number of blocks !

'Daisy chain' works because the MAX7219 has a 'serial out' pin which can be connected to the next MAX7219 'serial in'. To 'drive' the MAX7219, you send a 'command' string to the 'serial in'. The 'length' of the command is a fixed bit count, and consists of 'control' bits and 'data' bits. If you send multiple commands in a single string, the command 'buffer' of the first MAX7219 overflows (via the serial out) to the next. So, to control a daisy chain of 4 matrix blocks, all you have to do is send command strings 4x longer then 'normal' !

Whilst daisy chaining of the MAX devices keeps the i/o count down, for ease of programming you may wish to control each MAX 'on it's own' (so a 4 block display needs 8 i/o pins, 8 blocks 16 pins).

A typical 8x32 display (4 blocks), using one MAX7219 per block, can be found for £4.45 on eBay (Hong Kong), which compares favorably with the 'single 8x8 (no driver chip)' cost of 99p each (so 4 for £3.96).

Character shapes

The minimum practical number of 'blocks' for a 'marching display' is 4 (i.e. 8 dots high x 32 dots wide). The basic LED character set is defined as '5x7' (5 columns by 7 high, or 5x8 with lower-case descenders). A typical 'variable character width' set has an 'average' width of 3.8 columns, so, adding 1 column for inter-character spacing, this means a 32 wide display will show just over 6 & a half characters.

Since each block is 8 LED's 'high', we use a basic 5x7 'font' modified for 5x8 = the bottom row will allow 'descenders'. There are 95 common ASCII characters (including 'space'), codes 0x20 to 0x7E

Whilst upper case characters will need the full '5 columns wide' space, lower-case and most numerals will be only 4 columns wide and most punctuation even less. If we 'pack' these together we can saves on the 'dot map' storage space, however it does make programming the 'marching display' harder.

A quick 'worse case' assumption is that all 54 letters (26 upper + 26 lower case) and 10 numbers will be 5 columns wide (so 64 x 5 = 310 bytes) and the remaining 31 punctuation and 'specials' will take 4 bytes each (so another 124), making 434 bytes to store the character 'shapes'. This is at least 2 data-tables (each PIC data table is limited to 255 locations), so the 16F54 is out and we have to use the PIC 16F57 or 59 (both have 2k of program space, so that leaves 1.5k for actual code which should be more than enough).

Driving a marching display

In theory, the shapes of the 6 or 7 characters 'currently being shown' could just be 'looked up' every time the display 'cycles' (at 100Hz), however, to make the program code easier to understand and debug, the shapes being shown are best held in a set of registers (this has the advantage of allowing arbitrary shapes to be shown by adding a 'PC interface' to the design, however that does mean the inter-character 'gap' has to be held in a register (and not generated 'on the fly')).

A 32 column display thus needs 32 registers for the 'live' display, plus an additional 4 for the 'overflow' (= the rest of a 5x8 character whose first column has just started to appear on the display) = the 16F57 has 72 registers and the 16F59, 134 registers, so there should be 'no problem' (other than coping with the needlessly complex register 'bank' addressing scheme used in the 16F5x series)

Variable character widths

Making 'all' the characters 'variable width' allows us to maintain a visually pleasing display whilst avoiding 'special cases' (such as '.' and ',' etc).

All PIC's have storage space limitations - and the cheaper the PIC the less space it has. A font of variable width characters will save space as we only store the actual bit-map patterns and so long as the overhead of finding the shapes ('end flag', pointer/address or width table) is minimized.

The 'classical' approach to holding 'variable length' data strings is to use some sort of end code or 'flag byte', typically '00' or NULL.
 
However this would add 1 byte to every character width - and choosing a 'flag byte' value is not obvious - 00 for example, will appear between the two parts of a double quote (") and whilst you might think 0xFF would be 'safe' this will turn up in the vertical 'pipe' character '|'.
 
A further disadvantage to using 'flag codes' is the time taken to find the 'start' of a any specific character within the font store. Using 'end codes', you have to step through all the characters from the start of the table finding each 'end' until you reach the character you want.
 
To speed up character location, fonts typically have a 'pointer table' that gives the start position of each character (a shape is then found as all bytes between it's start pointer and the start of the next in the font). However 'start tables' - especially a 'jump address' table - also cost 'extra bytes'. A typical 96 character font would require at least 9bit address pointers, which would typically be stored in a 'word' table 'costing' a total of 192 bytes.
 
The final approach is to store 'width counts'. To find a character you then just have to add up the widths of all the preceding characters (which is faster than actually stepping through the table looking for end codes but much slower than a jump table). A font based on an 8x8 matrix would have character shapes between 1 and 8 bytes wide - so their widths can be encoded in 3 bits and packed 2 per byte (or 5 per word). A typical 96 character font width table would 'cost' 48 bytes (at 2 widths per byte) or 40 bytes (3 per word).
 
A 5x7 font would have characters are between 1 and 5 bytes wide - the challenge then being to come up with an approach that stores the width in only 2 bits. If 1 byte stores 4 widths, 96 widths needs only 24 bytes.

For more on Character Fonts, see my pages on PIC VTI/OSD projects

Next page :- PIC as a Logic analyser - (16F59)

[top]