Chasing/Scrolling LEDs

Peter Fender

New Member
I used a PICAXE 40X2 to drive 208 chasing LEDs for a sign.
The LEDs are mounted on a clear acrylic sheet in the pattern of an EKG heatbeat.
The sheet is mounted on a plywood frame behind a sheet of red acrylic.
A co-worker cut the patterns in the plywood and acrylic with a CNC router.

Notes on the code:
There is extra code to increase the speed in parts of the sign. This may not be needed for other applications.
Sign runs on 3 D-cells, and checks the battery voltage before each sweep. If lower than 3 warning thresholds, it blinks the first LED 1-3 times. Circuit has been running for 2 months now. They usually turn it off at night but sometimes forget.

Only one LED is lit at a time - more will overload current on pins.
Speed is adjustable with the pot.
The push button toggles a slow speed mode used to check the wiring.
If the push button is held down on power-up, it leaves the 1st LED on - used for max current measurement for battery life.

I have a video of the working sign. I'll try to upload separately (too big).



Well done on your project and thank you for posting the code, schematic diagrams, images, etc.

There are places where the code can be optimised.
1. A few cases where an alternative command may slightly improve the speed of a calculation
2. removing calculations from within the main loops which are only in reality changed if there is an action such as pressing a button.

Untested, but try the following code and see if you gain any significant speed improvement. But then you might need to increase the time delays if the overall speed is crucial.
If you gain enough speed through code changes in the program you may be able to reduce the PAUSE delay intervals and adjust the PICAXE speed back to 8 MHz which will save some power (current drain) for the PICAXE chip.

' Test Chasing LEDs 
'  208 LEDs in 16 column x 13 row matrix
' 21 Jan 12  
' 25 Jan 12  add Batt_Check
' 04 Feb 12  add pushbutton for slow speed
' 05 Feb 12  16MHz, all pauses 2x
' 16 Feb 12  v5 add row_12 on A.5
' 22 Feb 12  v6 update pulsetimes
' 27 Feb 12  v7 addl debug msg
' 19 Apr 12  v8 adjust battery warn levels

#picaxe 40X2
#terminal 19200
'#define verbose	; for debug messages
setfreq m16			; 8MHz default, 16MHz max w/o res

' I/O definitions
symbol PushB     	= pinA.6		; Push Button to +V on pin 9
symbol Knob			= 7			; 20k pot on ADC7 - A.7,pin 10

symbol Col_MSB		= outpinsC	; Column bits [15:8]
symbol Col_LSB		= outpinsD	; Column bits [ 7:0]
symbol Row_MSB		= outpinsA	; Row    bits [12:8]
symbol Row_LSB		= outpinsB	; Column bits [ 7:0]

Col_MSB				= %00000000	; Init
Col_LSB				= %00000000
dirsC					= %11111111
dirsD					= %11111111

Row_MSB				= %00101111	; skip A.4 (SerOut)
Row_LSB				= %11111111
dirsA					= %00101111
dirsB					= %11111111

adcsetup 			= 0x0080		; enable ADC7 (pin 10)

' Program definitions
symbol row					= b10
symbol col					= b11
symbol LED_Num				= b12

symbol time_multiplier	= b13
symbol on_time			= w10 ; b20,b21
symbol pulse_time 		= w11 ; b22,b23
symbol pulse_time2 		= w12 ; b24,b25
symbol on_time_M			= w13 ; b26,b27
symbol pulse_time_M 		= w14 ; b28,b29
symbol pulse_time_M2		= w15 ; b30,b31


pause 100
sertxd("208 Chasing LEDs (40X2) v8", cr,lf)
time_multiplier = 1

if PushB is ON then
	Col_MSB = $00		; light (0,0) LED for current meas. 
	Col_LSB = $01
	Row_MSB = $2F
	Row_LSB = $FE
	do loop until PushB is OFF
	pause 100

readadc Knob,b0						; read speed adj (0-255)
sertxd("Knob:  ",#b0, cr,lf)
on_time     = b0 / 2	 			; 0-127 ( 0-63 msec)
pulse_time  = on_time * 3 / 4		; 75% delay
pulse_time2 = on_time     / 2		; 50% delay
GOSUB Setdelays
#ifdef verbose
sertxd("OnTime:  ",#on_time,    "  Mult: ",  #time_multiplier,cr,lf)
sertxd("Pulse:   ",#pulse_time, "  Pulse2: ",#pulse_time2,cr,lf)

Row_MSB = $2F
Row_LSB = $FF

for row = 0 to 12
	Col_MSB = $00
	Col_LSB = $00
	w0   = 1 << row
	if row=12 then : w0 = w0 <<1 : endif  ; skip A.4, use A.5
	Row_LSB = NOT b0
	Row_MSB = b1 XOR $2F
	#ifdef verbose
	sertxd("Row info: ",#row, "  ",#Row_MSB,"  ",#Row_LSB, cr,lf)
	for col = 0 to 15
		w0   = 1 << col
		Col_LSB = b0
		Col_MSB = b1
		#ifdef verbose
		sertxd("Col info: ",#col, "  ",#Col_MSB,"  ",#Col_LSB, cr,lf)

		LED_Num = row * 16 + col		; 0 to 207

		select LED_Num
		case  28 to  67
			pause pulse_time_M
		case  68 to  119
			pause pulse_time_M2
		case 120 to 131
			pause pulse_time_M
			pause on_time_M
		if PushB is ON then gosub PushButton
		next col
	next row

Col_MSB = $00
Col_LSB = $00

gosub Batt_Check

for b0 = 1 to 1
	pause 100
	if PushB is ON then gosub PushButton
	next b0

goto again



' If pushbutton was pressed, then toggle multiplier
	sertxd("Button pressed.",cr,lf)
	time_multiplier = time_multiplier XOR $04

	do loop until PushB is OFF
	pause 100 ; debounce
		on_time_M     = on_time     * time_multiplier + 1
		pulse_time_M  = pulse_time  * time_multiplier
		pulse_time_M2 = pulse_time2 * time_multiplier


' Measure internal reference to check +V power
	symbol ADCval   = w26
	symbol V_plus   = w27			; *100 volts

	calibadc10 ADCval
	; V_plus / 1023 = 1.024 / ADCval
	; 100 x V_plus = 100 x 1047.552 / ADCval
	; 100 x V_plus = 52377.6 / ADCval * 2
	V_plus = 52378 / ADCval 	
	V_plus = V_plus * 2
	sertxd ("+V * 100 = ", #V_plus,cr,lf)

	'bintoascii V_plus, b4,b3,b2,b1,b0
	'sertxd (#ADCval," counts,   ",b2,".",b1,b0,  " volts",cr,lf)

	' new is ~ 4.6V
	if V_plus<380 then gosub Blink_LED00	; < 3.8 V
	if V_plus<360 then gosub Blink_LED00	; < 3.6 V
	if V_plus<330 then gosub Blink_LED00	; < 3.3 V

	; alert, blink the first LED
	Col_MSB = $00
	Col_LSB = $01
	Row_MSB = $2F
	Row_LSB = $FE
	pause 500
	Col_MSB = 0		; all off
	Col_LSB = 0
	Row_MSB = $2F
	Row_LSB = $FF
	pause 500


Battery Life:
3x D cells = 3 x 12,000 mAh
circuit current = 24.5 mA
life = 61 days
Last edited:

Peter Fender

New Member
Movie attached. Had to make a new one at lower resolution.

Westaus55 - thanks for your thoughts.
Your attached code snippet looks like what I have already. Not sure what you are suggesting.
This only gets run when the button is pushed, so speed here is not a big deal.

Calculations were kept inside the loop so that the speed changes instantly when the button is toggled.
Otherwise it would not change until the beginning of the next sweep.
Could be a long time if the speed is slow.
I did keep the ADC knob reading outside the loop due to time. I thought about also reading it on the button press, but it was not really helpful.
Most of the current draw is in the LED (18-20 mA), and not in the PICAXE (~4mA), so CPU speed is not a big driver.

A few more notes on the code.
LEDs are in a matrix, and the outputs pins are initialized with all the column pins low, and all row pins high.
To light one LED, a column pin is set high, and a row pin low.
If more that one LED is attempted, others in a square may light, and will exceed the max current on an output pin.
Be careful if modifying or debugging the code.



Senior Member
Looks good. Good job.

Had a look at the schematic. On Page1, you have a diode and a resistor on the power connector. What are they for?
If it is for reverse polarity protection, then you don't need the 10R, but do require a fuse to blow.
If it is an LED, then 1: the wrong symbol has been used; and 2: the wrong polarity is shown.


Westaus55 - thanks for your thoughts.
Your attached code snippet looks like what I have already. Not sure what you are suggesting.
sorry there some weird things happening at times when I try to post of recent.
Even now when I click on "Reply with Quote" all I get is an empty "Quick Reply" window. :(
Same is somtimes occring when I hit "Submit" and see an error. When I try to edit, on an iPhone using Safari all I get is an empty window (no tect to edit/fix/add to). :(

I had not saved the revised code that was to be included at post 2 so have tried to reconstruct what I was thinking and have replaced the few lines that were visible with what I believe I had in mind. Give the new code posted at Post 2 a try - it should make more sense now.

Peter Fender

New Member
Thanks again for looking at the code.
Good improvement there. Needed re-calculations are only done when the button is pressed.
I'll include that if I do an upgrade to the sign.

The diode is a simple try at reverse-polarity protection.
I did not want to use a series diode, because of the voltage drop across it.
The diode clamps the voltage some, and the resistor limits the current thru it, so it does not burn up.
It will only keep the reverse voltage down to a volt or 2, but was simple to include, and better than nothing.
If I had a small fuse at hand, I would have used that.


Senior Member
Too bad you can't light more than 1 LED at a time. It would look really cool if you could emulate a CRT display where there's a sort of fading persistence effect on the pixels behind the bright trace.

Actually, if you re-think how you do the loop timing, and hit every LED several times a second, then you could do a sort of PWM and accomplish the persistence/fade effect.