Cogless, Smart Pin APA102c
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.