Cogless, Smart Pin APA102c
JonnyMac
Posts: 9,512
The APA102c is a two-wire pixel that is preferred in the film/TV industry because it's easier to use (SPI without chip select) and it has a higher internal PWM frequency. While at DEF CON, there were lots of badges and boards (including my custom P2 accessory) that used just a few smart LEDs. If you have a project that doesn't need a full pixel driver, it's easy to use the APA102c. My normal driver uses manual bit-banging because the output pins can be changed while it's running for the next cycle.
For my little fixed-IO accessory board, I decided to go with Smart Pins for the APA102c. It's easy. In the method that sets up your IO, configure the smart pins like this:
m := P_SYNC_TX | P_OE ' spi tx mode m |= ((RGB_C-RGB_D) & %111) << 24 ' add clk offset (B pin) x := %1_00000 | (32-1) ' start/stop mode, 32 bits pinstart(RGB_D, m, x, 0) ' activate smart pin pinf(RGB_D) ' reset/disable until used m := P_PULSE | P_OE ' spi clock mode (CPOL = 0) x.word[0] := 2 #> (clkfreq / 10_000_000) <# $FFFF ' ticks in period (10 MHz) x.word[1] := x.word[0] >> 1 ' ticks in low cycle (50%) pinstart(RGB_C, m, x, 0) ' initialize smart pin
Here's the method that will write to your LED change. Note that this is good for up to 64 LEDs. This has to do with the end-frame needing to send half as many bits as there are pixels in the chain. For badges and similar boards this method will work fine. For big chains and changeable IO pins my full driver is probably the best way to go.
pub update_leds(p_leds, n)
'' Update n APA102c pixels
'' -- color format is $RR_GG_BB_00
'' -- p_leds is pointer to color array
'' -- end-frame good for up to 64 pixels
org
mov pr0, #0 ' start frame
call #.shiftout
.loop rdlong pr0, p_leds ' get color
add p_leds, #4 ' point to next
call #.fix_rgb ' fix & display
djnz n, #.loop
mov pr0, #0 ' end frame
call #.shiftout
jmp #.done
.fix_rgb movbyts pr0, #%%0123 ' $RR_GG_BB_00 --> $00_BB_GG_RR
setbyte pr0, #$FF, #3 ' $FF_BB_GG_RR (global = 100%)
rev pr0 ' reverse for smart pin SPI (LSBFIRST)
.shiftout wypin pr0, #RGB_D ' write value to SPI out
drvl #RGB_D ' enable SPI smart pin
wypin #32, #RGB_C ' set clock bits
nop ' let it start
testp #RGB_C wc ' wait for it to finish
if_nc jmp #$-1
fltl #RGB_D ' reset spi tx pin
ret
.done
end
Yes, you can do this is Spin2, but I find the inline PASM clean and easy, and especially helpful with the movbyts instruction used to fix the byte order in a single instruction. The setbyte instruction works just like the .byte[n] modifier in Spin2.
