Shop OBEX P1 Docs P2 Docs Learn Events
Cogless, Smart Pin APA102c — Parallax Forums

Cogless, Smart Pin APA102c

JonnyMacJonnyMac Posts: 9,159
edited 2022-08-22 14:39 in PASM2/Spin2 (P2)

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.

Sign In or Register to comment.