APA102 5050 RGB LED with Integrated Driver

premelec

Senior Member
@Greyling - I haven't used the 28X2 but suggest you look at the hspi commands and setup sniper used and look at Manual 1 P34 for HSPI clock and data out connections on the 28X2... Have you tried these? The APA102 is pretty straightforward to drive... clock and data and a variety of speeds - I'm working on some code for old 08M PICAXEs [just to use them up!] - I have with some success.

I remember that I've added 220 ohm resistors in series with clock and data lines from PICAXEs as I had a chip die mysteriously which _may_ have come from some sort of back feed from an APA102 strip while I was changing power around - the resistors don't don't seem to have an effect on the data transmission being ok.
 
Last edited:

1968neil

Senior Member
@ Technical :

I note from your excellent write up that there is no way of controlling an individual LED ?
I need to control a couple of led's every 50mm or so is there and addressable strip that can be controlled on an individual basis ? I just need them to light a single colour.
Any help appreciated.
Regards
Neil
 

hippy

Senior Member
All LED's in a strip are individually controllable, however you need to send data for all of the strip, or at least up to the last LED needing to be controlled. This is the case for all LED strips that I am aware of, and an intrinsic feature of their linear, 'infinite length', design.
 

techElder

Well-known member
I suppose I didn't study the thread enough, but I could never figure out how to send ("infinite length" - 1) packets of data.

Do you have to know how many LEDs are in the strip and build that into your program?
 

premelec

Senior Member
@1968neil - you can make up a strip from individual APA102s which then only has the units you want to change - even 1 APA102 will work... still if they are all in series you have to go along the whole strip to get to the last one... there are some APA102s mounted on small circuit boards or you can cut them out of a long strip... I haven't tried it but it might be possible to use a common clock pin and then various data leads to a multitude of strips giving 'one wire' control... not sure what happens if you only give clock pulses to a powered strip... something to try... :)
 

hippy

Senior Member
Do you have to know how many LEDs are in the strip and build that into your program?
It might be possible to take the output from the strip back into the PICAXE and use that to determine when the last has been written to but otherwise the number of LED's needs to be known by the program.
 

techElder

Well-known member
Now I'm wondering if you could strap a PICAXE on the end of the string of LEDs and give feedback to the controller so you wouldn't have to know how many LEDs are in the string.
 

BESQUEUT

Senior Member
I suppose I didn't study the thread enough, but I could never figure out how to send ("infinite length" - 1) packets of data.
Do you have to know how many LEDs are in the strip and build that into your program?
You have to known what you want to draw... so by the way, you must know how many LEDs you are adressing...
And you also need that to know how many bytes to send in the end frame.
For the "infinite length" aspect, you can read understanding-the-apa102-superled/
To count the LEDs, you can wait for the special "end frame".
 
Last edited:

Bill.b

Senior Member
Hi Neil

This code will write data to individual LEDs in a string of 60 APA 102 LED.

I have run this test program using a 1m string for 60 apa 102 LEDs.
This can only be used with a 28X2 or 40X2 because 180 bytes of scratchpad memory is required.

I use a spreadsheet to map the LEDs to determine which LED to control.

Code:
#picaxe 28x2
#no_table
#no_data
setfreq em32
; drop LED Strip light control
;  
;  |       28X2                 5V -.-   Light strip
;  |  .-----__-----.                |
;  |  | RST    B.7 |                `--> RED +V
;  '--= A0         =  .--> SCK --------> BLU CK
;     | C.2    C.5 |--|--> SDO --------> GRN DI
;  .--| C.3    C.4 |  |             .--> YEL 0V
;  |  |            |  |         0V _|_
;  |  `------------'  |
;  `------------------'

; Set how many LED modules in the strip
Symbol HOW_MANY_LEDS = 60
`
Symbol HOW_MANY_LEDS_MINUS_1 = HOW_MANY_LEDS - 1
Symbol HOW_MANY_LEDS_TIMES_3 = HOW_MANY_LEDS * 3

; Set the maximum brightness of the LED while testing
; Use small values to keep current consumption low
Symbol MAX_BRIGHTNESS = 31 ; 0 to 255 (full)

; Initialise the HSPI interface
#macro init()
  hspisetup spimode00, spifast
#endmacro

; Send a four byte packet out via HSPI
#macro sendPacket( n1, n2, n3, n4 )
  hspiout( n1, n2, n3, n4 )
#endmacro

; Send the start of data header
#macro head()
  sendPacket( $00, $00, $00, $00 )
#endmacro

; Send a LED controlling command
#macro send( red, green, blue)
  sendPacket( $FF, blue, green, red )
#endmacro

; Send the end of data tail
#macro tail()
  sendPacket( $FF, $FF, $FF, $FF )
#endmacro

PowerOnReset:

  ; Initialise the HSPI interface
  init

  ; Turn all LED modules off
  head
  for w0 = 1 To HOW_MANY_LEDS
    send( $00, $00, $00 ) ; 1 to last = Off
  next
  tail
 Main:
	DO
	gosub ClearAllLEDs
	gosub setleds
	pause 12000
	gosub ClearAllLEDs
	gosub setleds1
	pause 12000
	gosub ClearAllLEDs
	gosub setleds2
	
	pause 12000
	Loop
  ClearALLLEDS:
   Ptr = 0
   For w0 = 1 to HOW_MANY_LEDS_TIMES_3		'set all array elements to 0
	   @ptrinc = 0
   next w0
   ptr = 0							'Set pointer to first location of data
    	head							'Send start data to LEDs - ($00, $00, $00, $00)
    	for w0 = 1 to HOW_MANY_LEDS
    	  	send( @ptrinc, @ptrinc, @ptrinc )	'Send 3 bytes of data to each leds (Blue, Green, Red) 
    	next   
	tail
   return 
		'PTR 0,1,2  = LED 60 the last LED in the string.
		
Setleds:			'Set Brightness of LED		
    	put 33,32								'Set LED 49 to Blue
	put 124,32								'Set LED 19 to Green
    	; Output the LED data
    	ptr = 0								'Set pointer to first location of data
    	head									'Send start data to LEDs - ($00, $00, $00, $00)
    	for w0 = 1 to HOW_MANY_LEDS
    	  	send( @ptrinc, @ptrinc, @ptrinc )			'Send 3 bytes of data to each leds (Blue, Green, Red) 
    	next   
	tail									'Send End data to LEDs - ($FF, $FF, $FF, $FF)		

return
Setleds1:			'Set Brightness of LED
    	put 34,32								'Set LED 49 to green
	put 125,32								'Set LED 19 to red
    	; Output the LED data
    	ptr = 0								'Set pointer to first location of data
    	head									'Send start data to LEDs - ($00, $00, $00, $00)
    	for w0 = 1 to HOW_MANY_LEDS
    	  	send( @ptrinc, @ptrinc, @ptrinc )			'Send 3 bytes of data to each leds (Blue, Green, Red) 
    	next   
	tail									'Send End data to LEDs - ($FF, $FF, $FF, $FF)		

return
Setleds2:			'Set Brightness of LED
    	put 35,32								'Set LED 49 to red
	put 123,32								'Set LED 19 to blue
    	; Output the LED data
    	ptr = 0								'Set pointer to first location of data
    	head									'Send start data to LEDs - ($00, $00, $00, $00)
    	for w0 = 1 to HOW_MANY_LEDS
    	  	send( @ptrinc, @ptrinc, @ptrinc )			'Send 3 bytes of data to each leds (Blue, Green, Red) 
    	next   
	tail									'Send End data to LEDs - ($FF, $FF, $FF, $FF)		

return
View attachment apa102DataMap1.pdf

Have Fun


Bill
 
Last edited:

1968neil

Senior Member
Thanks so much for the help/advice.

Thanks Bill you are a STAR !!
That makes sense now, really looking forward to getting my strips now will post my results when im nearer a finished project.
Many Thanks
Best Regards
Neil
 

jims

Senior Member
Here's some code that gives good control of the "Dot Star" LED strip with an IR Remote "clicker" (set the brightness, background color of the strip, and set individual LEDS). It can be tailored to use either Scratchpad or EEPROM memory. The code contains ideas that I have copied from many others shown on the FORUM. It's fun to play around with. JimS

EDIT: Only uses 60 bytes of memory.

Code:
'********************************************************
'* 20x2 controls the Dotstar APA102 LED strip with HSPI.
'* Uses Macros. Use IR "clicker" to set the LED brightness 
'* level, background color of LED string, and to set color
'* of individual LEDs.
'* NOTE: Now setup to use scratchpad memory. This can be 
'* changed to use EEPROM by replacing the commands 
'* ( PUT to WRITE and GET to READ ).
'********************************************************

;          20X2                +5V -.-   Light strip
;     .-----__-----.                |
;     |            |                `--> RED +V
'	|        B.7 |11---> SCK --------> YEL CK
;     |        C.1 |9----> SDO --------> GRN DI
;     |            |                .--> BLK 0V
;     |            |           0V _|_   Colors for my LED strip.
'     |            |
'     |        C.2 |8<----- CLICKER
'	|        B.1 |17----> OLED
'	|        B.0 |18----> SPEAKER
'     .------------.

Symbol CLICKER =  C.2	'Pin 8; Clicker will select routine or interrupt.
Symbol OLED = B.1		'Pin 17.
symbol speaker = B.0 	'Pin 1.
symbol irdata = b0
symbol addr = b1
symbol red = b2
symbol green = b3
symbol blue = b4
symbol color = b7
symbol counter = b8
symbol value = b9
symbol brightness = b10
Symbol HOW_MANY_LEDS = 60  '* Number of LED modules in the strip.


;** Initialise the HSPI interface
#macro Init()
  hspisetup spimode00, spifast
#endmacro

;** Send the start of data header
#macro head()
  sendPacket( $00, $00, $00, $00 )
#endmacro

;** Send the end of data tail
#macro tail()
  sendPacket( $FF, $FF, $FF, $FF )
#endmacro

;** Send a four byte packet out via HSPI
#macro sendPacket( n1, n2, n3, n4 )
  hspiout( n1, n2, n3, n4 )
#endmacro

;** Send a LED controlling command
#macro send(  red, green, blue )
  sendPacket( $FF, green,blue, red )
#endmacro

;** Turns all LED modules off.
#macro AllOff
	head	; Turn all LED modules off
      for b0 = 1 To HOW_MANY_LEDS
     	 send(0,0,0)
      next
      tail	; Send the end of data tail.
#endmacro

#picaxe 20x2
pause 1000
	serout oled,n2400,(254,1):pause 30  'Clear OLED.
	Init		'Macro initializes the HSPI interface.
	AllOff	'Macro turns all LEDS OFF.
	 	
Main:
	serout oled,n2400,(254,1):pause 30
	serout oled,n2400,(254,128,"Use Clicker  ")
	do
	 call SetBrightness
	 call FillMemory  'Fills memory with background color
	 call SendData	'Sends background color to LED strip.
	 call GetData	'Get data for individual LEDs.
	 call SendData	'Sends data to all LEDs.
	loop

'* Fill Scratchpad/EEPROM memory with background color.
FillMemory: 
	serout oled,n2400,(254,128," Set Background   ")
	serout oled,n2400,(254,192,"    Color: _       ")
	serout oled,n2400,(254,203)	 'Places cursor.
	call One_Digit
	let addr =0
	for addr = 0 to HOW_MANY_LEDS 
	put addr,IrData
	next
 	return

SendData:
	serout oled,n2400,(254,1):pause 30
	serout oled,n2400,(254,128,"Sending LED Data ")
	let addr = 0
	head  ;* Send the start of data header.
	 for addr = 0 to HOW_MANY_LEDS 
	 get addr,b0 'value
	 red  = bit0 *Brightness
	 green = bit1 *Brightness
	 blue = bit2 *Brightness
	 send(red,green,blue)
	 'sertxd (red,green,blue,cr,lf) 'Use for testing.
	next addr
	tail	;* Send the end of data tail.
	pause 500
	return

'* Use "clicker' to set individual LED colors (address & color).
'* Loop here until clicker "power" key code (21) is pushed.	
GetData:
		
	do
	serout oled,n2400,(254,128," LED Addr: __     ") 'Positions the underscores for data entry.
	serout oled,n2400,(254,192," Color: _         ") 'Positions the underscores for data entry.
	serout oled,n2400,(254,139)	 'Places cursor.
	let color = value
	let value=0 	'Reset "values" variable to use in this routine.
	call two_digits
	if irdata = 21 then return:endif '"POWER" key.
	let addr = value
	serout oled,n2400,(254,200)	 'Places cursor.
	call one_digit
	if irdata = 21 then return:endif '"POWER" key.
	let color = value
	put addr,IrData
	call SendData
	inc addr
	loop
	return

'* Gets the 10's digit.
two_digits:
	call GetIR		'Use "clicker" to enter data.
	if irdata = 21 then return:endif  '"POWER" key.
	value=irdata*10+value		'10's digit is "x10".
	serout oled,n2400,(#irdata):pause 10	'Places cursor.
	
'* Gets the units digit.
one_digit:
	call GetIR		'Use "clicker" to enter data.
	if irdata = 21 then return:endif  '"POWER" key.
	value=irdata*1+value		'1's digit is "x1".
	serout oled,n2400,(#irdata):pause 10	'Places cursor.
	pause 500
	return

'* Sets LED brightness level from 0 to 9.
SetBrightness:
	serout oled,n2400,(254,1):pause 30
	serout oled,n2400,(254,128,"Set Brightness:_") 'Positions the underscore for data entry.
	serout oled,n2400,(254,143)	 'Places cursor.
	call One_Digit
	let Brightness = IRData
	pause 500
	return

'*Subroutine reads data from IR Remote "clicker" & Corrects numeric key offset.
GetIR:	
	irin clicker,irdata	'Read data from IR clicker.
	do:let b5=0:count C.2,80,b5:loop until b5=0 'Loop until key is released.
	if irdata<10 then let irdata=irdata+1//10:endif 'Corrects numeric key offset.
	return
 
Last edited:

jims

Senior Member
Here's an improved version of some code to control a 60 LED DOT STAR strip. Will let user set the LED brightness level, the background color for the whole strip of 60 LEDs and the address and color of individual LEDs. Prompt messages on an OLED display guide a user through the process. Has been tested and used successfully on with a 60 LED DOT STAR strip using a Picaxe 20x2 chip. JimS

Code:
'**************************************************************
'* 20x2 controls one 60 LED Dotstar APA102 LED strip with HSPI.
'* Uses Macros; and an IR "clicker" to set the LED brightness 
'* level,the background color of the 60 LED strip, and the 
'* address and color of individual LEDs.
'* SEE (clicker key functions) at the end of the code.
'* NOTE: Program is now setup to use scratchpad memory. This 
'* can be changed to use EEPROM by replacing the commands 
'* ( PUT to WRITE and GET to READ ).
'***************************************************************

;          20X2                +5V -.-   Light strip
;     .-----__-----.                |
;     |            |                `--> RED +V
'	|        B.7 |11---> SCK --------> YEL CK
;     |        C.1 |9----> SDO --------> GRN DI
;     |            |                .--> BLK 0V
;     |            |           0V _|_   (Colors are for my LED strip)
'     |            |
'     |        C.2 |8<----- CLICKER
'	|        B.1 |17----> OLED
'	|        B.0 |18----> SPEAKER
'     .------------.

Symbol CLICKER =  C.2	'Pin 8; Clicker will select routine or interrupt.
Symbol OLED = B.1		'Pin 17.
symbol speaker = B.0 	'Pin 1.
symbol irdata = b0
symbol MemAddr = b1
symbol red = b2
symbol green = b3
symbol blue = b4
symbol color = b7
symbol counter = b8
symbol value = b9
symbol brightness = b10
symbol LEDAddr = b11
Symbol HOW_MANY_LEDS = 60  '* Number of LED modules in the strip.


;** Initialise the HSPI interface
#macro Init()
  hspisetup spimode00, spifast
#endmacro

;** Send the start of data header
#macro head()
  sendPacket( $00, $00, $00, $00 )
#endmacro

;** Send the end of data tail
#macro tail()
  sendPacket( $FF, $FF, $FF, $FF )
#endmacro

;** Send a four byte packet out via HSPI
#macro sendPacket( n1, n2, n3, n4 )
  hspiout( n1, n2, n3, n4 )
#endmacro

;** Send a LED controlling command
#macro send(  red, green, blue )
  sendPacket( $FF, green,blue, red )
#endmacro

;** Turns all LED modules off.
#macro AllOff
	head	; Turn all LED modules off
      for b0 = 1 To HOW_MANY_LEDS
     	 send(0,0,0)
      next
      tail	; Send the end of data tail.
#endmacro

#picaxe 20x2
pause 1000
	serout oled,n2400,(254,1):pause 30  'Clear OLED.
	Init		'Macro initializes the HSPI interface.
	AllOff	'Macro turns all LEDS OFF.
	 	
Main:
	serout oled,n2400,(254,1):pause 30
	serout oled,n2400,(254,128,"Use Clicker  ")
	pause 500
	do
	 call SetBrightness
	 call FillMemory  'Fills memory with background color
	 call SendData	'Sends background color to LED strip.
	 call GetLEDAddr	'Get data for individual LEDs.
	 call SendData	'Sends data to all LEDs.
	 serout oled,n2400,(254,1):pause 30
	loop

'* Sets LED brightness level from 0 to 9.
SetBrightness:
	serout oled,n2400,(254,1):pause 30
	serout oled,n2400,(254,128,"Set Brightness:_") 'Positions the underscore for data entry.
	serout oled,n2400,(254,143)	 'Places cursor.
	call One_Digit
	let Brightness = IRData
	pause 50
	return

'* Fill Scratchpad/EEPROM memory with background color.
FillMemory: 
	serout oled,n2400,(254,128," Set Background   ")
	serout oled,n2400,(254,192,"    Color: _       ")
	serout oled,n2400,(254,203)	 'Places cursor.
	call One_Digit
	let MemAddr =0
	for MemAddr = 0 to HOW_MANY_LEDS 
	put MemAddr,IrData
	next
 	return

'* Send data to LED strip.
SendData:
	let MemAddr = 0
	head  ;* Send the start of data header.
	 for MemAddr = 0 to HOW_MANY_LEDS 
	 get MemAddr,b0 'value
	 red  = bit0 *Brightness
	 green = bit1 *Brightness
	 blue = bit2 *Brightness
	 send(red,green,blue)
	 'sertxd (red,green,blue,cr,lf) 'Use for testing.
	next MemAddr
	tail	;* Send the end of data tail.
	return

'* Use "clicker' to set beginning memory address for colors.
GetLEDAddr:
	serout oled,n2400,(254,1):pause 30	
	serout oled,n2400,(254,128," LED Addr: __     ") 'Positions the underscore.
	serout oled,n2400,(254,139)	 'Places cursor.
	let color = value
	call two_digits
	let LEDAddr = value

'* Use "clicker" to enter colors. Loop here until
'* clicker "power" code (21), or other non-numeric
'* key is pushed (see NOTE at end of code).	
GetColor:
	serout oled,n2400,(254,192," Color: _         ") 'Positions the underscore.
	serout oled,n2400,(254,200)	 'Places cursor.
	do
	 let value = 0
	 call one_digit
	 if irdata >9 then goto GetLEDAddr 'Non-Numeric key, see NOTE at end of code.
	 put LEDAddr,IrData
	 call SendData
	 inc LEDAddr
	 serout oled,n2400,(254,128," LED Addr: ",#LEDAddr,"  ")
	 serout oled,n2400,(254,192,"Enter Color: _ ") 'Position the underscore.
	 serout oled,n2400,(254,205)	 'Places cursor.
	loop
	return
	
'* Get the 10's digit.
two_digits:
	let value = 0
	call GetIR		'Use "clicker" to enter data.
	value=irdata*10+value		'10's digit is "x10".
	serout oled,n2400,(#irdata):pause 10	'Places cursor.
	
'* Get the units digit.
one_digit:
	call GetIR		'Use "clicker" to enter data.
	value=irdata*1+value		'1's digit is "x1".
	serout oled,n2400,(#irdata):pause 10	'Places cursor.
	pause 50
	return

'*Subroutine reads data from IR Remote "clicker" & Corrects numeric key offset.
GetIR:	
	irin clicker,irdata	'Read data from IR clicker.
	if irdata = 21 then reset:endif  '"POWER" key resets & starts over.
	do:let b5=0:count C.2,80,b5:loop until b5=0 'Loop until key is released.
	if irdata<10 then let irdata=irdata+1//10:endif 'Corrects numeric key offset.
	return

'	CLICKER KEY FUNCTIONS
' KEY      CODE       	 FUNCTION
'Power	21		Reset chip & start over
' XX		XX		See NOTE.
' 0		000		LED OFF
' 1		001		LED Red
' 2		010		LED Green
' 3		011		LED Yellow
' 4		100		LED Blue
' 5		101		LED Purple
' 6		110		LED Light Blue
' 7		111		LED White
' 8		000		LED OFF
' 9		001		LED Red
' NOTE: Any non-numeric key except 21 goes to
'       enter new LED Address.
 

sniper887

Member
Here are some examples created using a 40X2 and a 60 LED strip. However they could be adapted to pretty much any number of LEDs and any PICAXE type.

www.picaxe.com/downloads/apa102.zip
In the following code, what does the @ptr = @ptr + 1 accomplish? It looks like you take the value in that scratchpad address and add one to it, but I don't understand the bitwise AND you do with that value and 1.
Code:
for w0 = 1 to 5
 random w1
      ptr = w1 / 5 // HOW_MANY_LEDS_TIMES_3
      @ptr = @ptr + 1 & 1
    next
 

premelec

Senior Member
I also don't follow why tech did that - on the other hand maybe it's because of the 4 byte control string and they have leds x 3 - I haven't looked at the code much since it was published a while back. I've been have fun with driving APA102s with an 08M as I have several to put to use. 250 bytes not much memory but then you can still generate a lot of pixel control signals looping. I really like the APA102s. I liked your demo program and you tube show!
 

rjandsam

Member
The following code only works if set to 32mhz or spifast is set to spimedium, the same code with a 40x2 is absolutely fine with em64 and spifast.

Thanks

Rich

#picaxe 20x2
#no_table
#no_data
setfreq m64

Symbol HOW_MANY_LEDS = 22

Symbol HOW_MANY_LEDS_MINUS_1 = HOW_MANY_LEDS - 1

; Set the brightness of the LED while testing
; Use small values to keep current consumption low
Symbol BRIGHTNESS = 255 ; 0 to 255 (full)
Symbol BRIGHTNESS2 = 50 ; 0 to 255 (full)
; Initialise the HSPI interface
#macro init()
hspisetup spimode00, spifast
#endmacro

; Send a four byte packet out via HSPI
#macro sendPacket( n1, n2, n3, n4 )
hspiout( n1, n2, n3, n4 )
#endmacro

; Send the start of data header
#macro head()
sendPacket( $00, $00, $00, $00 )
#endmacro

; Send a LED controlling command
#macro send( red, green, blue)
sendPacket( $FF, blue, green, red )
#endmacro

; Send the end of data tail
#macro tail()
sendPacket( $FF, $FF, $FF, $FF )
#endmacro

PowerOnReset:
pause 100
; Initialise the HSPI interface
init
pause 100
; Turn all LED modules off
head
for w0 = 1 To HOW_MANY_LEDS
send( $00, $00, $00 ) ; 1 to last = Off
next
pause 1000
tail

BounceLed:
; Set one LED at a time
; 'w0' indicates which LED to set
pause 1000
do
for w0 = 1 to HOW_MANY_LEDS
Gosub SetLeds
next
'for w0 = HOW_MANY_LEDS_MINUS_1 to 2 step -1
'Gosub SetLeds
'next
loop

SetLeds:
; Set a specific LED and keep the others off
head
for w1 = 1 to HOW_MANY_LEDS
if w1 = w0 then
send( $00, $00, $ff ) ; red
else
send(00,$00, 10 ) ; off
end if
next
pause 1000
tail
return
 

hippy

Senior Member
Thanks for the clarification. It may be that with SPIFAST at 32MHz the SPI output is just too fast for the APA102 to keep up with, but that is at odds with it working on a 40X2 at 64MHz. We would have to investigate further.
 

newplumber

Senior Member
Thanks for the clarification. It may be that with SPIFAST at 32MHz the SPI output is just too fast for the APA102 to keep up with, but that is at odds with it working on a 40X2 at 64MHz. We would have to investigate further.
Hi
I was just curious if Picaxe found any new information on making the spifast work with apa102 using the 20x2, setfreq m64,? ....
I never tried with a 40x2...but perhaps I may this weekend.
 
Top