Welcome to the Matrix. The LED Matrix!
A quick search of the Internet shows various LED Chasers and circuit designs that mimic Knight Rider's Kitt car or Battlestar Galactica's Cyclon Scanner/Eye. This circuit uses a simple 12bit PIC to run 16 LEDs in a 4x4 matrix configuration (this includes the fading-tail-light effect as the LEDs scan from side to side).
Two of these scanners were mounted on two pictures of "suspected cyclons" at a science-fiction End Of Season Party as part of the props and decorations to add to the theme of the party.
![]() |
| This is it. The end. The last episode of Battlestar Galactica. We will find all the clues and get all the answers. It will be two solid hours of our favourite show. Natasha Eloi from Space will be our special guest.
It's bound to be one frakkin' good finish. |
Needless to say, but still worth saying it.
The Party was definitely a Frakkin success.
You can download the Larson Eye file, or you can copy and paste the code shown below into your ASM code editor.
title 'Eye, Copyright by Jose Da Silva, 2009mar20'
list P=16C54
; list P=16F84
list f=INHX8M,C=120,T=on,ST=off
;******************************************************************************
;DIP 16C54 or 16F84 ____ ____
; <-|a2 \/ a1|->
; <-|a3 a0|->
; gnd-Rx120k ->|TOCKI osc1|<-RC=(12k+100pf)=650khz
;3k9+104!MCLR/Vpp->|!MCLR osc2|-> /4
; gnd ->|Vss Vdd|<- +5v
; LEDrow0+ <-|b0 b7|--W--> LEDcol0- <->ICSPDAT
; LEDrow1+ <-|b1 b6|--W--> LEDcol1- <->ICSPCLK
; LEDrow2+ <-|b2 b5|--W--> LEDcol2-
; LEDrow3+ <-|b3 b4|--W--> LEDcol3-
; ----------
;
radix dec
nolist
#ifdef __16C54
#include "P16C5X.INC"
StartR equ 8 ;Start of available RAM
#endif
#ifdef __16F84
#include "P16F84.INC"
errorlevel -224 ;Suppress tris/option warnings
StartR equ 0x0c ;Start of available RAM
#endif
list
__fuses _CP_OFF&_WDT_OFF&_RC_OSC
__idlocs h'0904' ;date code - finished 2009, mar20th
;as seen on 13th Colony Battlestar Galactica group on www.meetup.com
#define LEDPORT PORTB
#define BUTTON PORTA,4
#define freq 655000
#define LEDs 17 ;16 LEDs (+1 for looping back)
;******************************************************************************
;RMB substitution from V5 to V8 code migration (TY RES solution, jun2011)
rmb macro bytes
org $
org $+bytes ;...next loc
endm
;******************************************************************************
org StartR
nxtLEDt rmb 1 ;next primary LED to light from 3 LED table
LEDprtV rmb 4 ;port values for 3+1 LEDs
temp rmb 2 ;temporary storage used by subroutines
countB rmb 2 ;large loop of about 1000x4x17x2
dataOut rmb 2 ;data to transmit out
Atris rmb 1 ;Current state of output PORTs
Aport rmb 1
org 0
#ifdef __16F84
goto Reset
goto Reset
goto Reset
goto Reset
goto Reset
#endif
;******************************************************************************
;4 LED Table, this table lists 4 LEDs in sequence from the 16 LED 4x4 matrix.
;enter: w=LED.table.position
;exit: w=matrix.tris.value required to light this LED
;time: 4cycles
LED4tbl addwf PCL,f ;get Table Value based on entry with W register
Delay4 retlw 0 ;delay 4 cycles {call=2, retlw=2}, note: Wreg=0
dt b'11100111',b'11010111',b'10110111',b'01110111'
dt b'11101011',b'11011011',b'10111011',b'01111011'
dt b'11101101',b'11011101',b'10111101',b'01111101'
dt b'11101110',b'11011110',b'10111110'
dt b'01111110',0xff,b'01111110'
dt b'10111110',b'11011110',b'11101110'
dt b'01111101',b'10111101',b'11011101',b'11101101'
dt b'01111011',b'10111011',b'11011011',b'11101011'
dt b'01110111',b'10110111',b'11010111',b'11100111',0xff
LED4end equ $
dt 0xff,0xff
;******************************************************************************
Delay22 goto $+1 ;2,total delay is 22 cycles (include call/retn)
goto $+1 ;2
goto $+1 ;2
goto $+1 ;2
goto $+1 ;2
Delay10 goto $+1 ;2
goto $+1 ;2
Delay6 goto Delay4 ;delay 6 cycles {call,goto,retlw}, note: Wreg=0
;******************************************************************************
;{10,4,2}cycles x (loops for 2 seconds)
;{
;LED routine to use less program space while repeatedly showing LEDs total 75x
;enter: LEDprtV[0..2]=port values for LED output,countr
;exit: w=0
;calls: Delay4,Delay6,Delay10
;time: 4096cycles+2154cycles=6250cycles
;values located in LEDprtV[0,1,2]
;do this for 2/3. Loop for 1/34*2=2seconds={10,4,2}=freq/4/16/256*2/17/2 * 2/3
LEDrtn movlw freq/24576/LEDs+1 ;1,1/34 and 2/3rd of 2 seconds
movwf temp ;1,time=2seconds
movlw (freq/96/LEDs & 0xff)+1 ;1,1/34 of 2 seconds
movwf temp+1 ;1,
LEDrtn1 btfss BUTTON ;1,"fancy" or "simple" LED display
goto LEDrtn2 ;2,go do "fancy" LED version
goto $+1 ;2,delay, "simple" 1 LED version
goto LEDrtn3 ;2,done
LEDrtn2 movfw LEDprtV+2 ;1,output LEDPORT=LEDprtV[2]
tris LEDPORT ;1,
movfw LEDprtV+1 ;1,output LEDPORT=LEDprtV[1]
tris LEDPORT ;1,
LEDrtn3 movfw LEDprtV+0 ;1,output LEDPORT=LEDprtV[0] (brightest)
goto $+1 ;2,delay
tris LEDPORT ;1,
goto $+1 ;2,delay
decfsz temp+1,f ;1,looped 256 times?
goto LEDrtn1 ;2,not yet, do more
decfsz temp,f ;1,looked long enough?
goto LEDrtn1 ;2,not yet, do more
;----------------------------------------------------------------------
;move next LED pointer to next LED to display
movfw LEDprtV+2 ;1,output LEDPORT=LEDprtV[2]
tris LEDPORT ;1,
movfw LEDprtV+1 ;1,output LEDPORT=LEDprtV[1]
tris LEDPORT ;1,
movfw LEDprtV+0 ;1,output LEDPORT=LEDprtV[0]
goto $+1 ;2,
tris LEDPORT ;1,
decfsz nxtLEDt,w ;1,point to next LED to glow
goto LEDrtn4 ;2,do the opposite of decsz
movlw LEDs*2 ;1,if reached end, loop to beginning
LEDrtn4 movwf nxtLEDt ;1,update pointer to next LED in table
movfw LEDprtV+2 ;1,move LEDs down array
movwf LEDprtV+3 ;1
movfw LEDprtV+1 ;1
movwf LEDprtV+2 ;1
movfw LEDprtV+0 ;1
movwf LEDprtV+1 ;1
;----------------------------------------------------------------------
;get next LEDprtV[0] value to display
movfw LEDprtV+3 ;1,output LEDPORT=LEDprtV[2]
tris LEDPORT ;1,
movfw LEDprtV+2 ;1,output LEDPORT=LEDprtV[1]
tris LEDPORT ;1,
movlw freq/49152/LEDs+1 ;1,1/34 and 1/3rd of 2 seconds
movwf temp ;1,time=2seconds
movfw LEDprtV+1 ;1,output LEDPORT=LEDprtV[0]
tris LEDPORT ;1,
movlw (freq/192/LEDs & 0xff)+1 ;1,1/34 of 2 seconds
movwf temp+1 ;1,
movfw nxtLEDt ;1,prepare to collect next new location
call LED4tbl ;6,go get next table location
movwf LEDprtV+0 ;1,hold new value in LEDprtV[0]
;----------------------------------------------------------------------
;display 4 LEDs since we are moving to next LED {5,6,3,2}
LEDrtn5 movfw LEDprtV+3 ;1,output LEDPORT=LEDprtV[2old]
tris LEDPORT ;1,
movfw LEDprtV+2 ;1,output LEDPORT=LEDprtV[1old]
tris LEDPORT ;1,
movfw LEDprtV+1 ;1,output LEDPORT=LEDprtV[0old]
nop ;1,
tris LEDPORT ;1,
goto $+1 ;2,
goto $+1 ;2,
movfw LEDprtV+0 ;1,output LEDPORT=LEDprtV[0new]
tris LEDPORT ;1,
decfsz temp+1,f ;1,looped enough?
goto LEDrtn5 ;2,not yet, do more
decfsz temp,f ;1,
goto LEDrtn5 ;2,not yet, do more
;----------------------------------------------------------------------
;done, exit routine while displaying LEDs
movfw LEDprtV+2 ;1,output LEDPORT=LEDprtV[2]
tris LEDPORT ;1,
movfw LEDprtV+1 ;1,output LEDPORT=LEDprtV[1]
tris LEDPORT ;1,
movfw LEDprtV+0 ;1,output LEDPORT=LEDprtV[0]
goto $+1 ;2,
tris LEDPORT ;1,
goto Delay10 ;10,done
;******************************************************************************
;look in a given direction for 2 seconds {2,13,2}cycles = freq/4/17/256*2
LEDlk movlw freq/512/LEDs+1 ;1,look in directionX for 2 seconds
movwf temp ;1,time=2seconds
clrf temp+1 ;1,
LEDlk1 btfss BUTTON ;1,"fancy" or "simple" LED display
goto LEDlk3 ;2,go do "fancy" LED version
goto $+1 ;2,do "simple" 1 LED version
goto LEDlk4 ;2,go reload 1 LED
LEDlk3 movfw LEDprtV+0 ;1,output LEDPORT=LEDprtV[0]
tris LEDPORT ;1,
movfw LEDprtV+2 ;1,output LEDPORT=LEDprtV[2]
tris LEDPORT ;1,
LEDlk4 movfw LEDprtV+1 ;1,output LEDPORT=LEDprtV[1] (brightest)
tris LEDPORT ;1,
call Delay4 ;4,delay
nop ;1,delay
decfsz temp+1,f ;1,looped 256 times?
goto LEDlk1 ;2,not yet, do more
decfsz temp,f ;1,looked long enough?
goto LEDlk1 ;2,not yet, do more
retlw 0 ;done, continue
;******************************************************************************
Reset movlw b'00001111' ;set options register
option
movlw 0 ;Start with all ports as outputs low
movwf PORTA
;reset needed for 18 pin 16c54 type chip
movwf Aport
movlw b'00010000' ;{a3..a0}=N/A, a4=BUTTON=input_N/A
tris PORTA
movwf Atris
movlw b'11111111' ;{b7..b0}=set all LEDs start as off
tris PORTB
movlw b'00001111' ;direction 4pins=+0V ---> 4pins=+5V
movwf PORTB
;----------------------------------------------------------------------
movlw LEDs/2 ;init to point at middle LED to begin glowing
movwf nxtLEDt
call LED4tbl ;init LEDprtV[0,1,2] before calling routine
movwf LEDprtV+0
movlw LEDs/2+1
call LED4tbl
movwf LEDprtV+1
movlw LEDs/2+2
call LED4tbl
movwf LEDprtV+2
;----------------------------------------------------------------------
movlw (50*LEDs*2/256)+1 ;loop 100 cycles for the 1st time
movwf countB ;approx 2x100 seconds or 1.4 minutes
movlw ((50*LEDs*2) & 0xff)+1
movwf countB+1
goto main1
;------------------------------------------------------------------------------
;main loop does everything here as a 'state machine' to keep correct timing
main movlw (1000*LEDs*2/256)+1 ;loop 1000 cycles
movwf countB
movlw ((1000*LEDs*2) & 0xff)+1
movwf countB+1
;----------------------------------------------------------------------
main1 call LEDrtn ;24,glow 3 LEDs
;----------------------------------------------------------------------
;need to loop while also maintaining synchronized glow pattern
decfsz countB+1,f ;done yet?
goto main1 ;not yet
decfsz countB,f
goto main1 ;not yet
call LEDlk ;look in directionX for 2 seconds
goto main ;reset and do it again
;******************************************************************************
dt "Copyright Jose Da Silva 2009mar20 Vancouver BC"
;******************************************************************************
#ifdef __16C54
org 1FFh
goto Reset ;program starts here on reset
#endif
end |
This circuit is designed to run on a basic pic16c54 type chip, but should be easy to transfer to another PIC chip if you have that instead (this example shows the 14bit PIC 16f84 as an example substitute).
The LED matrix runs from one 8 bit port in a 4x4 configuration, PORTB - 4pins[0..3] are set positive, 4pins[4..7] are set negative.
The original code used RES to reserve RAM locations which works fine with older MPLAB compilers, but new MPLAB compilers don't like RES. You will notice the RMB macro substitution (I haven't found the original source of the RMB macro, but one URL credits Tony Yates - therefore... thanks for the RES replacement - it works as a good substitute).
The LED4tbl table is a list of TRIS outputs to enable one of the 16 LEDs at a time in one direction, then in the other. Delay4 is oddly located in the table, to make it so that we can start the table from [1] instead of [0].
LEDrtn does the work of lighting 3 LEDs and moving to the next LED. The LEDs are pulsed in the ratio of 10/4/2 to give an appearance of a fading-tail as the LEDs scan side to side. The majority of time is spent in this routine. Although this circuit runs at a leisurely 650kHz using an RC oscillator (12k and 100pF), it is still fast enough to rely on persistence of vision so that you do not see the LEDs flicker.
LEDlk does the work of staring in a given direction for 2 seconds while pulsing the LEDs in the ratio of 2/13/2 to give a tapered effect to the sides of the centre LED.
The main program loops repeatedly forever, but every 1000's cycle, does a stop-and-stare for about 2 seconds.
| Here's a bit of trivia for anyone who has watched the new re-imaged BSG: Assuming it takes one second to scan left and one second to scan right, and do this 1000 times, the time taken is 2000 seconds, which is approximately 33 minutes. ...then again, maybe it's just a coincidence for one of the episodes to be called 33. |
CIRCUIT COMPONENTS AND HARDWARE
The circuit was built on a perf board. Resistors for the LEDs are 220 ohm. Runs at 4.5v based on 3 batteries.
![]() ![]() |
If you find this code (or sections of this code) useful for yourself, or a project, I will appreciate a little credit for providing it here for your use/project (for example, in your source code comments, or even a web link back to this web page is great if you have a project that uses it that you want to show/demonstrate). Thanks.
| Other PIC Related Pages On This Web Site | ||||
|---|---|---|---|---|
| Home Page | A/B Long Term Timer | ASCII To MorseCode | Clear RAM Macro | Delay Macro |
| Random Number Generator (RNG) | 7.96Hz Sinewave Generator | Software RS232 | Table Array FSR Macro | PicDis Disassembler |