APA102 5050 RGB LED with Integrated Driver

Technical

Technical Support
Staff member
This part is similar to the WS2812 (search forum for WS2818 discussion), but has a big advantage in that the clock speed can be anywhere between 800 and 1200 using 'proper' SPI, instead of just the fixed 800 of the WS2818. That should make it a bit easier to use with fast clocked signal as the timing does not need to be quite so specific.
 

sniper887

Member
I got an LED strip working with HSPI from a PicAxe 20x2. For the HSPIsetup I used spimode00 and spifast. Just send 4 bytes with 0 in each one to start, then a 4 byte packet for each led, per the data sheet. The end packet doesn't seem to be necessary, at least with the bit of playing around I did.
 

premelec

Senior Member
Thanks... the reason I asked is I couldn't get any results trying to drive WS2812s 800KHz protocol and I wonder what's the slowest SPI rate to drive these strips?... Adafruit doesn't seem to have this data other than stating it's faster than with the 800KHz protocol... With the PICAXE I want a fairly slow SPI and high PWM rate limiting flicker that the WS2812s have... I would be interestedin what 'AXE you used at what frequency...
 

sniper887

Member
Thanks... the reason I asked is I couldn't get any results trying to drive WS2812s 800KHz protocol and I wonder what's the slowest SPI rate to drive these strips?... Adafruit doesn't seem to have this data other than stating it's faster than with the 800KHz protocol... With the PICAXE I want a fairly slow SPI and high PWM rate limiting flicker that the WS2812s have... I would be interestedin what 'AXE you used at what frequency...
I used a 20X2 at the default frequency (8 MHz I think?). For the HSPI setup I used spimode00 and spifast. I looked at the WS2812 LEDs and it seemed like the protocol was way too fast for the Picaxe to be able to signal them.
 

premelec

Senior Member
Thanks. I couldn't quite make out in the chinglish data sheet just what the speed could be... I've got an LPD8806 strip to try also - my WS2812 attempt was total failure... :-0
 

grim_reaper

Senior Member
Yes, my WS2812 attempt was also a total failure!
I ended up buying a £10 Arduino and using it as a driver/interface for the PICAXE. Some would say that's a waste of an Arduino, and some would not... lol
 

randymgn

New Member
The APA102 LED only have a maximum of 400Khz while the WS2812B LED has a maximum (and minimum) of 800Khz.I think for bigger things the WS2812 is better since it's not that hard for a powerful MCU to control them + the higher refresh speed + when you do something with allot of LEDs you alredy are using a powerful MCU/processor.
 

hippy

Senior Member
We have got our hands on a light strip based around the APA102 and have some example programs which we will be releasing in due course. In the meantime we can clarify some of the issues relating to them.

The 'clock speed' stated in the datasheet relates to the internal oscillator which drives the module's PWM controller rather than the data rate or refresh rate. The modules can be controlled via bit-banging or HSPI. The SPI data rate can be from DC up to tens of MHz so can easily be used with any PICAXE with little care as to what the timing is or what delays there are between bits and or bytes.

Of course, the fastest SPI data rates allow for highest refresh rates and will have less delay across a LED strip when turning all LED's on or off. With slower SPI rates the first LED will noticeably change before the last. This likely isn't a problem for ambient or mood lighting but disco-style changes are best achieved using HSPI.

Because the data update is initiated by a specific 'start frame' rather than time period between data bytes, the APA102 and APA102C modules are much easier to control with a PICAXE and most other microcontrollers than WS2812 and similar LED's which require more accurate or specific data timing.

Because of the way the LED modules pass data on to the next; the end frame is required to ensure the last LED is updated in the same frame as the others are. If the end frame is not sent then the last LED may only get updated when a subsequent frame is sent. Something the datasheet does not indicate is that there needs to be an end frame sent for every 64 sets of LED's.

The LED modules are self contained so, once they have been set, they do not necessarily need to be have data sent again though we would recommend refreshing them fairly regularly in case any external interference causes any LED modules to unexpectedly light.
 

premelec

Senior Member
Another factor often not obvious is the strobing rate from the internal PWMs - the WS2812 has a pretty low rate [annoyingly so in my experience] and supposedly the LPD8806 and the unit being discussed in this thread have a much higher internal strobing rate - I haven't seen what the APA102 rate is but look forward to trying one...

I looked up the PWM rates - WS2812 = 400Hz; LPD8806 = 4,000Hz, APA102 = 19,000[?? found in a blog]

The chip also has a 32 step global dimming possibility however this evidently produces flicker at about 400 Hz like the WS2812.
 
Last edited:

premelec

Senior Member
Thanks Tech... I'm using M2 'AXEs so will need to change the output... my approach so far has been to calculate values to be outputted and store them in SRAM and then then select with @bptr and bit bang... apparently the APA102 will take a lot of sloppy timing...
 

hippy

Senior Member
I am not sure who the LED strip supplier was but the PSU used was a current limited bench supply.
 

jims

Senior 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
@Technical...Thank you for this excellent article/tutorial. There seems to be a lot of interest on the FORUM about driving LED strips. Is there any chance that you'll give the same detailed coverage to LED strips with WS2812 chips? JimS
 

hippy

Senior Member
Is there any chance that you'll give the same detailed coverage to LED strips with WS2812 chips?
Probably not, because the WS2812 has specific timing requirements which seem to make it unusable using a direct connection to a PICAXE.

I recall there has been some discussion and theorising that, if the WS2812 doesn't require the exact timing as the datasheets indicate, it may be possible, but I don't believe anyone has actually put that to the test or managed to do it yet.
 

premelec

Senior Member
@jims - as Hippy says.... and note that the APA104 appears to be similar timing to WS2812 - Wesaust55 has some extensive WSA2801 discussion and code.
 

jims

Senior Member
@jims - as Hippy says.... and note that the APA104 appears to be similar timing to WS2812 - Wesaust55 has some extensive WSA2801 discussion and code.
Thank you...premelec. Can you give me a reference to the post for Westy's discussion? JimS
 

rjandsam

Member
Hi Technical,
thank you for the great examples, I have only just gotten around to playing with the strip today and have to say I am impressed.

I have taken your example code and modified it to do a single colour fill followed by a complete empty, I was just wondering if the way I have done it is the fastest method. I understand obviously that the empty routine takes longer as you have to fill each time to the next pixel you want to empty as you state in your readme file that you cannot address an individual pixel.

Thanks

Rich

Code:
[color=Navy]#picaxe [/color][color=Black]40x2[/color]
[color=Navy]#no_table
#no_data[/color]
[color=Blue]setfreq em64[/color]
[color=Green]; =======================================================
; Example 4
; Bounce one LED from end to end of the light strip
; =======================================================

;          40X2                 5V -.-   Light strip
;     .-----__-----.                |
;     | RST    B.7 |                `--> RED +V
;     =            =  .--> SCK --------> BLU CK
;     | C.2    C.5 |--|--> SDO --------> GRN DI
;  .--| C.3    C.4 |  |             .--> YEL 0V
;  |  | D.0    D.3 |  |             |
;  |  | D.1    D.2 |  |         0V _|_
;  |  `------------'  |
;  `------------------'

; Set how many LED modules in the strip[/color]
[color=Blue]Symbol HOW_MANY_LEDS [/color][color=DarkCyan]= [/color][color=Navy]60[/color]

[color=Blue]Symbol HOW_MANY_LEDS_MINUS_1 [/color][color=DarkCyan]= [/color][color=Blue]HOW_MANY_LEDS [/color][color=DarkCyan]- [/color][color=Navy]1[/color]

[color=Green]; Set the brightness of the LED while testing
; Use small values to keep current consumption low[/color]
[color=Blue]Symbol BRIGHTNESS    [/color][color=DarkCyan]= [/color][color=Navy]150 [/color][color=Green]; 0 to 255 (full)

; Initialise the HSPI interface[/color]
[color=Navy]#macro [/color][color=Black]init[/color][color=Blue]()
  hspisetup spimode00[/color][color=Black], [/color][color=Blue]spifast[/color]
[color=Navy]#endmacro[/color]

[color=Green]; Send a four byte packet out via HSPI[/color]
[color=Navy]#macro [/color][color=Black]sendPacket[/color][color=Blue]( [/color][color=Black]n1, n2, n3, n4 [/color][color=Blue])
  hspiout( [/color][color=Black]n1, n2, n3, n4 [/color][color=Blue])[/color]
[color=Navy]#endmacro[/color]

[color=Green]; Send the start of data header[/color]
[color=Navy]#macro [/color][color=Black]head[/color][color=Blue]()
  [/color][color=Black]sendPacket[/color][color=Blue]( [/color][color=Navy]$00[/color][color=Black], [/color][color=Navy]$00[/color][color=Black], [/color][color=Navy]$00[/color][color=Black], [/color][color=Navy]$00 [/color][color=Blue])[/color]
[color=Navy]#endmacro[/color]

[color=Green]; Send a LED controlling command[/color]
[color=Navy]#macro [/color][color=Black]send[/color][color=Blue]( [/color][color=Black]red, green, blue[/color][color=Blue])
  [/color][color=Black]sendPacket[/color][color=Blue]( [/color][color=Navy]$FF[/color][color=Black], blue, green, red [/color][color=Blue])[/color]
[color=Navy]#endmacro[/color]

[color=Green]; Send the end of data tail[/color]
[color=Navy]#macro [/color][color=Black]tail[/color][color=Blue]()
  [/color][color=Black]sendPacket[/color][color=Blue]( [/color][color=Navy]$FF[/color][color=Black], [/color][color=Navy]$FF[/color][color=Black], [/color][color=Navy]$FF[/color][color=Black], [/color][color=Navy]$FF [/color][color=Blue])[/color]
[color=Navy]#endmacro[/color]

[color=Black]PowerOnReset:

  [/color][color=Green]; Initialise the HSPI interface
  [/color][color=Black]init

  [/color][color=Green]; Turn all LED modules off
  [/color][color=Black]head
  [/color][color=Blue]for [/color][color=Purple]w0 [/color][color=DarkCyan]= [/color][color=Navy]1 [/color][color=Blue]To HOW_MANY_LEDS
    [/color][color=Black]send[/color][color=Blue]( [/color][color=Navy]$00[/color][color=Black], [/color][color=Navy]$00[/color][color=Black], [/color][color=Navy]$00 [/color][color=Blue]) [/color][color=Green]; 1 to last = Off
  [/color][color=Blue]next
  [/color][color=Black]tail

BounceLed:
  [/color][color=Green]; Set one LED at a time
  ; 'w0' indicates which LED to set
  [/color][color=Blue]do
  [/color][color=Black]head
        
    [/color][color=Blue]for [/color][color=Purple]w0 [/color][color=DarkCyan]= [/color][color=Navy]1 [/color][color=Blue]to HOW_MANY_LEDS
    [/color][color=Black]send[/color][color=Blue]( BRIGHTNESS[/color][color=Black], [/color][color=Navy]$00[/color][color=Black], [/color][color=Navy]$00 [/color][color=Blue]) [/color][color=Green]; red
     [/color][color=Blue]pause [/color][color=Navy]20
    [/color][color=Blue]next[/color]
[color=Black]tail
    [/color][color=Blue]for [/color][color=Purple]w0 [/color][color=DarkCyan]= [/color][color=Blue]HOW_MANY_LEDS_MINUS_1 to [/color][color=Navy]1 [/color][color=Blue]step [/color][color=DarkCyan]-[/color][color=Navy]1
      [/color][color=Blue]Gosub [/color][color=Black]SetLed
    [/color][color=Blue]next
  loop[/color]

[color=Black]SetLed:
  [/color][color=Green]; Set a specific LED and keep the others off
  [/color][color=Black]head
  [/color][color=Blue]for [/color][color=Purple]w1 [/color][color=DarkCyan]= [/color][color=Navy]1 [/color][color=Blue]to HOW_MANY_LEDS
            if [/color][color=Purple]w1[/color][color=DarkCyan]<[/color][color=Purple]w0 [/color][color=Blue]then [/color][color=Black]send[/color][color=Blue]( BRIGHTNESS[/color][color=Black], [/color][color=Navy]$00[/color][color=Black], [/color][color=Navy]$00 [/color][color=Blue]) [/color][color=Green]; red
 [/color][color=Blue]elseif [/color][color=Purple]w0[/color][color=DarkCyan]<[/color][color=Blue]HOW_MANY_LEDS then [/color][color=Black]send[/color][color=Blue]( [/color][color=Navy]$00[/color][color=Black], [/color][color=Navy]$00[/color][color=Black], [/color][color=Navy]$00 [/color][color=Blue]) [/color][color=Green]; red
 [/color][color=Blue]endif
  next
  [/color][color=Black]tail
  [/color][color=Blue]return[/color]
 

hippy

Senior Member
It's not clear why you need the ELSEIF, and if not this will be faster ...

Code:
head
for w1 = 1 to HOW_MANY_LEDS
  if w1<w0 then 
    send( BRIGHTNESS, $00, $00 ) ; red
  else
    send( $00, $00, $00 )        ; off
  endif
next
tail
Untested, but this may be even faster, perhaps not ...

Code:
head
for w1 = 1 to HOW_MANY_LEDS
  w2 = w0 Min w1 - w1 Max 1 * BRIGHTNESS
  send( w2, $00, $00 )
next
tail
 

rjandsam

Member
Hi Hippy,
you are correct, the elseif was pointless oops .
:confused:
the strip runs very smooth during empty now, your second example is slightly slower unfortunately.

Thanks for the help ;-)
 

sniper887

Member
Rainbow color fade with APA102 1 meter strip, using Picaxe 28X2 on AXE091 development board.

https://youtu.be/ZXw9U--Sba0

Code:
setfreq em16
hspisetup spimode00, spifast
hspiout (0,0,0,0)
test:
pause 4000
hspiout (255,255,255,255,255,255,255,255,255,255,255,255)


symbol red = b0
symbol green = b1
symbol blue = b2
symbol modulus = b3
symbol pauseval = w5
let pauseval = 40
symbol counter1 = w6
symbol counter2 = w7
symbol counter3 = w8

init: 
pause 4000
gosub initlight
counter3 = 0
ptr = 0
for red = 240 to 1 step -3
	green = 240 - red
	@ptrinc = green
	@ptrinc = blue
	@ptrinc = red
	counter3 = counter3 + 3
	
next red

for green = 240 to 1 step -3
	blue = 240 - green
	@ptrinc = green
	@ptrinc = blue
	@ptrinc = red
	
	counter3 = counter3 + 3
	
next green


for blue = 240 to 1 step -3
	red = 240 - blue

	@ptrinc = green
	@ptrinc = blue
	@ptrinc = red
	counter3 = counter3 + 3
	
next blue

debug



counter1 = 0
counter3 = 0

start:










for counter3 = 0 to 717 step 3
	pause 100
gosub initlight
ptr = counter3
for counter2 = 0 to 60
	if ptr > 717 then
		ptr = 0
	endif
	
	if counter3 > 717 then
		counter3 = 0
	endif
	hspiout (255, @ptrinc, @ptrinc, @ptrinc)
	
next counter2

next counter3



goto start






initlight:
hspiout (%00000000,%00000000,%00000000,%00000000)
return
 
Last edited:

premelec

Senior Member
@sniper887 thanks for posting your code - I've been playing with a 14M2 PICAXE and 30 Pixel APA102 and it seems quite doable with using Bit0-Bit7 and PULSOUT as Wstaust55 showed for WS2801- I haven't fully adapted your code but I'm close! This APA102 unit is very easy to drive. Haven't fully understood what the 32 ones end signal is used for as it seems to start and take over simply with 32 zeros start... Data sheet is a bit obscure. I'm having fun with it....
 

premelec

Senior Member
Thanks technical - I also found a youtube demonstration which showed the effect... I'm using Editor #5 and older computer.

PS I just checked example six looks like b20 slipped in where b27 should be...

"110 PIN_SDO = b20 / $80 : pulsout SCK, 1
111 PIN_SDO = b27 / $40 : pulsout SCK, 1"
 
Last edited:

hippy

Senior Member
PS I just checked example six looks like b20 slipped in where b27 should be...

"110 PIN_SDO = b20 / $80 : pulsout SCK, 1
111 PIN_SDO = b27 / $40 : pulsout SCK, 1"
You are correct that is typo which was not caught in testing; the 'b20' should be 'b27' as per the other bit extractions. That should only affect Example 6 as it's the only one which uses software SPI.
 

premelec

Senior Member
@hippy - I've been putting the byte into b0 with @bptrinc and using the Bit7-Bit0 to PULSOUT - is there an advantage to using the method shown in Ex#6 as noted above? [I'm using the 14M2 at 32MHz PULSOUT 1 - seems to work ok]. Thanks...

On further reflection on the code shown in Ex#6 - shouldn't this be a bitwise AND? I'm not seeing how the division extracts bits... I'm looking for the fastest bit banged SPI...
 
Last edited:

hippy

Senior Member
@hippy - I've been putting the byte into b0 with @bptrinc and using the Bit7-Bit0 to PULSOUT - is there an advantage to using the method shown in Ex#6 as noted above?
The advantage of the code used in Example 6 is it will work with any variable. The disadvantage is it is not as fast as using 'b0' through 'b3' and associated individual bit variables.

One advantage of not using 'b0' through 'b3' and associated individual bit variables is that this technique doesn't have to be explained in the code !

On further reflection on the code shown in Ex#6 - shouldn't this be a bitwise AND? I'm not seeing how the division extracts bits...
After substituting SYMBOL definitions it is a "Let pinX=..." assignment so the least significant bit of the expression is output ( there's an implicit AND 1 ). The three options are -

Let pinX = var / $80

Let pinX = var >> 8

Let pinX = var And $80 Max 1

Shift operations do not exist on the M2 parts. Doing a simple AND without the MAX doesn't set the lsb.

I'm looking for the fastest bit banged SPI...
Using 'b0' through 'b3' and associated individual bit variables will be fastest.
 

premelec

Senior Member
Thanks hippy... I've tried Bit0-Bit7 with 4 gosubs and Bit0-Bit31 with 4 vars and one gosub and don't see much difference - anyhow it's working so much better than my failed attempts with WS28XX that I'm happy so far! :)
 

hippy

Senior Member
I would have expected quite a good improvement, though admit I have not tested it myself. Perhaps post your code so we can spot if there's anything amiss.
 

premelec

Senior Member
@hippy - I've been changing setfreq & other variables so I haven't got any hard data... mainly what's seen is subtle progressing modulation as updates occur but I haven't actually timed how long sequences take... strobe rate is great compared to WS28xx units... L8R G8R... :-0

PS I'm using old Lithium 3.8 volt battery - seems to work fine

I measured time for 4 Byte Bit0-Bit31 SPI vs 1 Byte Bit0-Bit7 same program [similar to Sniper's code] 22 seconds vs 27 seconds for 1 byte - 486 bytes code vs 413 bytes code. So not a lot faster using 4 byte variables...

Caution note: I was trying a new program that wasn't working and the PICAXE failed - I was powering the APA102 strip separately from the PICAXE and at times noticed strange feed through from the clock and data lines into the APA102 strip when its APA102 V+ was disconnected. It seems possible that excessive currents via what are normally just signal connections can happen... I'll put series resistors [100ohm or so] in series with the data and clock lines and see if that seems to limit oddities appropriately and still gets the signals through ok - In a decade of PICAXE experimenting this is the first time I've had one fail... [PS new 14M2 works fine confirming original chip failure] 220 ohms in series with CLK & DTA works fine...
 
Last edited:

Greyling

New Member
Hello sniper887, Can you tell us what the wiring diagram to the 28X2 would look like? (pinouts)
I would like to try this code, but am not sure how it is hooked up to the picaxe.
Thank you!
 
Top