Issues with converting RGB LED driver code (LPD8806)

I'm learning P2 Spin using the Propeller Tool. I set out to update some code written by Don Pomplun back in 2014 from the P1 version to P2. This is specifically for the LPD8806 LED Driver from AdaFruit (https://www.adafruit.com/product/306). I can get the code to compile without errors but no response on my 8 LED string. The code is pretty straightforward but I can't figure out what I'm doing wrong. Any suggestions?
Original P1 code:
{
********  Propeller I/O assignments   ***********

P0   RGB data
P1   RGB clock

}

CON
  _clkmode = xtal1  + pll16x    'Standard clock mode * crystal frequency = 80 MHz
  _xinfreq = 5_000_000

  numLEDs  =  8
  LEDdata = 0
  LEDclock = 1

PUB startup

  dira[LEDdata]~~  '  LED strip data line
  outa[LEDdata]~
  dira[LEDclock]~~  '  LED strip clock line
  outa[LEDclock]~
  colorTest

PRI colorTest | i

  streamByte(0)
  repeat i from 1 to 3*numLEDs   ' turn off all LEDs
    streamByte( $80 )
    'waitcnt( clkfreq/60 + cnt )

  repeat ' forever

    streamByte(0) '  primes LED strip to receive new data

    repeat i from 1 to numLEDs   ' magenta
      streamByte( $FF ) ' Blue . . . LED addressing order is Blue Red Green (not RGB)
      streamByte( $FF ) ' Red
      streamByte( $80 ) ' Green

    waitcnt( clkfreq/2 + cnt ) '  1/2 second between color changes
    streamByte(0)

    repeat i from 1 to numLEDs   '  red
      streamByte( $80 )
      streamByte( $FF )
      streamByte( $80 )

    waitcnt( clkfreq/2 + cnt )
    streamByte(0)

    repeat i from 1 to numLEDs   '  yellow
      streamByte( $80 )
      streamByte( $FF )
      streamByte( $FF )

    waitcnt( clkfreq/2 + cnt )
    streamByte(0)

    repeat i from 1 to numLEDs  '  green
      streamByte( $80 )
      streamByte( $80 )
      streamByte( $FF )

    waitcnt( clkfreq/2 + cnt )
    streamByte(0)

    repeat i from 1 to numLEDs  '  cyan
      streamByte( $FF )
      streamByte( $80)
      streamByte( $FF )

    waitcnt( clkfreq/2 + cnt )
    streamByte(0)

    repeat i from 1 to numLEDs  '  blue
      streamByte( $FF )
      streamByte( $80 )
      streamByte( $80 ) ' keep green on

    waitcnt( clkfreq/2 + cnt )

PRI streamByte(d)

  d ><= 8  '  reverse bit order to shift out MSB first

  repeat 8
    if (d & 1) <> 0
      outa[LEDdata]~~

    outa[LEDclock]~~   ' high
    outa[LEDclock]~    ' low

    outa[LEDdata]~

    d >>= 1
Attempt at conversion to P2 spin:
{
LPD8806 LED Driver
********  Propeller I/O assignments   ***********

P54   RGB data
P52   RGB clock

}

CON
  CLK_FREQ = 200_000_000                                        ' system freq as a constant
  _clkfreq = CLK_FREQ                                           ' set system clock

  numLEDs  =  8
  ledData = 54
  ledClock = 52

PUB startup()

dirb[ledData]~~  '  LED strip data line
outb[ledData]~
dirb[ledClock]~~  '  LED strip clock line
outb[ledClock]~
colorTest()

PRI colorTest() | i

  streamByte(0)
  repeat i from 1 to 3*numLEDs   ' turn off all LEDs
    streamByte( $80 )
    waitms(500) '  1/2 second between color changes

  repeat ' forever

    streamByte(0) '  primes LED strip to receive new data

    repeat i from 1 to numLEDs   ' magenta
      streamByte( $FF ) ' Blue . . . LED addressing order is Blue Red Green (not RGB)
      streamByte( $FF ) ' Red
      streamByte( $80 ) ' Green

    waitms(500) '  1/2 second between color changes
    streamByte(0)

    repeat i from 1 to numLEDs   '  red
      streamByte( $80 )
      streamByte( $FF )
      streamByte( $80 )

    waitms(500) '  1/2 second between color changes
    streamByte(0)

    repeat i from 1 to numLEDs   '  yellow
      streamByte( $80 )
      streamByte( $FF )
      streamByte( $FF )

    waitms(500) '  1/2 second between color changes
    streamByte(0)

    repeat i from 1 to numLEDs  '  green
      streamByte( $80 )
      streamByte( $80 )
      streamByte( $FF )

    waitms(500) '  1/2 second between color changes
    streamByte(0)

    repeat i from 1 to numLEDs  '  cyan
      streamByte( $FF )
      streamByte( $80)
      streamByte( $FF )

    waitms(500) '  1/2 second between color changes
    streamByte(0)

    repeat i from 1 to numLEDs  '  blue
      streamByte( $FF )
      streamByte( $80 )
      streamByte( $80 ) ' keep green on

    waitms(500) '  1/2 second between color changes




PRI streamByte(d)

  d REV= d  '  reverse bit order to shift out MSB first

  repeat 8
    if (d & 1) <> 0
      outb[ledData]~~

  outb[ledClock]~~   ' high
  outb[ledClock]~    ' low

  outb[ledData]~

  d >>= 1
«1

Comments

  • JonnyMacJonnyMac Posts: 7,318
    edited 2020-11-16 (10:50 PM)
    When using the outb[pin] syntax, pin is actually a bit field, not a pin number. In your case, using 52 (%00001_10100) is the same as 20 addbits 1, which means your code fill impact pins 52 and 53. Andy pointed this out in the "Moving from Spin1 to Spin2" discussion.

    For low-level IO like this, it's best not to do a direct translation. The P2 has built-in IO commands that are in fact faster than the way you're trying to do it.

    Give this a go:
    pri stream_byte(d)
    
      d <<= 24
    
      repeat 8
        pinwrite(LED_DATA, (d rol= 1))
        pinhigh(LED_CLOCK)
        pinlow(LED_CLOCK)
    
      pinlow(LED_DATA)
    
    If you want even faster output, you can use inline PASM2. Try this, too.
    pri stream_byte(d) | tix
    
      tix := (clkfreq / 2_000_000) >> 1
    
      org
                    shl       d, #24
    
                    rep       #7, #8
                     rcl      d, #1                         wc
                     drvc     #LED_DATA
                     nop
                     drvh     #LED_CLOCK
                     waitx    tix
                     drvl     #LED_CLOCK
                     waitx    tix 
    
                    drvl      #LED_DATA
      end
    

    BTW... when using the P1, I find it best to use direct assignments which are faster versus the cryptic post-set and clear operators. For you P1 version:
    pri stream_byte(d)
    
      d <<= 8
    
      repeat 8
        outa[LED_DATA] := d <-= 1
        outa[LED_CLOCK] := 1
        outa[LED_CLOCK] := 0
    
      outa[LED_DATA] := 0
    
    This code is faster and easier to understand. Of course, LED_DATA and LED_CLOCK must be set to output mode before using this method.
  • I don't know if the problem might be too fast for the display as I didn't see anything obviously wrong with your code. Easy to test - try 20_000_000 (20MHz).
  • JonnyMac wrote: »
    When using the outb[pin] syntax, pin is actually a bit field, not a pin number. In your case, using 52 (%00001_10100) is the same as 20 addbits 1, which means your code fill impact pins 52 and 53. Andy pointed this out in the "Moving from Spin1 to Spin2" discussion.

    For low-level IO like this, it's best not to do a direct translation. The P2 has built-in IO commands that are in fact faster than the way you're trying to do it.

    Give this a go:
    pri stream_byte(d)
    
      d <<= 24
    
      repeat 8
        pinwrite(LED_DATA, (d rol= 1))
        pinhigh(LED_CLOCK)
        pinlow(LED_CLOCK)
    
      pinlow(LED_DATA)
    
    If you want even faster output, you can use inline PASM2. Try this, too.
    pri stream_byte(d) | tix
    
      tix := (clkfreq / 2_000_000) >> 1
    
      org
                    shl       d, #24
    
                    rep       #7, #8
                     rcl      d, #1                         wc
                     drvc     #LED_DATA
                     nop
                     drvh     #LED_CLOCK
                     waitx    tix
                     drvl     #LED_CLOCK
                     waitx    tix 
    
                    drvl      #LED_DATA
      end
    


    This code is faster and easier to understand. Of course, LED_DATA and LED_CLOCK must be set to output mode before using this method.

    Thanks for the code Jon! I tried the P2 Spin code first along with changing the clock to 20MHz. I'm getting a response finally from the LEDs, it seems to be a rapid flashing of the green and red portions of the RGBs but at least I'm finally getting some output! Interesting that it takes about 8 seconds after downloading before the LEDs start to flash.

    I'll try out the PASM2 next. I've been reading up on PASM but still getting my head wrapped around it, the last assembly I did regularly was on a 1802 CMOS processor many years ago, finally getting a need to think at that level again. I've been using a nice driver object in the P1 library to run the LPD8806. That driver is all done using pasm but I need to learn more before I attempt to update that driver. In the meantime I thought I'd find a spin-based driver to convert and learn on.
    Cluso99 wrote: »
    I don't know if the problem might be too fast for the display as I didn't see anything obviously wrong with your code. Easy to test - try 20_000_000 (20MHz).
    Good catch, didn't even notice that part. It runs at 20Mhz but not 200Mhz.

  • JonnyMacJonnyMac Posts: 7,318
    edited 2020-11-17 (12:42 AM)
    Another great feature of Spin2 is delays in microseconds -- you can use this to adjust your clock speed.
    pri stream_byte(d)
    
      d <<= 24
    
      repeat 8
        pinwrite(LED_DATA, (d rol= 1))
        pinhigh(LED_CLOCK)
        waitus(2)
        pinlow(LED_CLOCK)
        waitus(2)
    
      pinlow(LED_DATA)
    
    This should slow the shift clock to a bit under 250kHz (irrespective of your system clock speed). I thought that LED chip could handle 2MHz -- maybe not.
  • The P2 is much faster than the P1. Pasm instructions execute in 2 clocks, code can run in hubexec, the bytecode engine is way faster than the P1 alternative, there is buffered reads from hub, and the clock can typically be 2x - 4x faster.
  • After a week of playing around with the code above and not getting the desired results I decided to jump into attempting to convert the Object Exchange LDP8806 driver (LPD8806_20120731.spin) I've been successfully using over from P1 to P2.
    This is my first attempt, got it to compile but I'm not getting any output to the LED strip. I know there are a couple of things in the assembly code (using dira for one) that compiles but should be another command instead. I think I got the code right for bringing in the parameters via the ptra register.
    Suggestions?
    {{
    #######################################
    #                                     #
    # Copyright (c) 2012 Fredrik Safstrom #
    #                                     #
    # See end of file for terms of use.   #
    #                                     #
    #######################################
    
    ############################# DESCRIPTION ########################
    Re-write of driver for P2 usage.
    This is a driver for the LED Strip from adafruit.com
    https://www.adafruit.com/products/306
    
    I took the source code for the Arduino and reversed engineered it to
    figure out the timing and latching.
    My first try in Spin didn't go to that well, the performance of Spin
    was not even close to what was needed.
    So I took the 74HC595 driver from Dennis Ferron and modified it to
    shift out the data for the LPD8806.
    
    ########################### PIN ASSIGNMENTS #####################
    Passed to driver...
    ########################### REVISIONS ###########################
    
    1.0 Got to start somewhere...
    2.0 11/23/2020  Re-write driver for operation under P2 commands
    
    }}
    
    VAR
      long led_data       'test data
    
      ' Block of parameters to pass to the Assembler program.
      long par1           ' Address to LED Array
      long par2           ' Clock pin
      long par3           ' Data pin
      long par4           ' Number of LEDs in string
      long cog
    
    pub main() : i
    '' test new driver code
      start(1, 3, 8, @led_data)
      waitms(250)
      repeat i from 0 to 7
        led_data[i] := rgbColor(0, 127, 0) 'green
        waitms(50)
      'clear display
      repeat i from 0 to 7
          led_data[i] := rgbColor(0, 0, 0)
          waitms(50)
    
    
    PUB start(clock_pin, data_pin, num_LEDs, LED_Address) : okay
    {{ Start the LPD8806 driver in an available cog }}
    
      '' Assign parameters to block
      par1 := LED_Address
      par2 := clock_pin
      par3 := data_pin
      par4 := num_LEDs
    
      '' Stop just in case we have a cog running
      stop()
      '' Start new cog
      cog := coginit(COGEXEC_NEW,@lpd8806_asm, @par1) + 1
    
    PUB stop()
    {{ Stop the LPD8806 driver and free cog }}
    
        if cog
           cogstop(cog~ - 1)
    
    PUB rgbColor(red, green, blue) : rgbValue
    {{ Convert rgb to LED values}}
    
        rgbValue := blue + red << 8 + green << 16
    
    PUB fillColor(color) | i
    {{ Fill array with a specific color }}
    
      repeat i from 0 to par4-1
        LONG[par1][i] := color
    
    PUB clear()
    {{ Clear array }}
    
      fillColor(0)
    
    DAT
    {{ The LPD8806 driver.  Runs continously in a separate cog. }}
    
                  org       0
    lpd8806_asm
                  rdlong    temp1, ptra              ' First parameter is address to LED values
                  add       temp1, #4               ' Move to next parameter
                  mov       temp2, temp1            ' Get clock pin
                  mov       clkpin, #1              ' Prepare mask
                  shl       clkpin, temp2           ' Shift bit into position
    
                  add       temp1, #4               ' Move to next parameter
                  mov       temp2, temp1            ' Get data pin
                  mov       datpin, #1              ' Prepare mask
                  shl       datpin, temp2           ' Shift bit into position
    
                  add       temp1, #4               ' Move to next parameter
                  mov       numled, temp1           ' Save number of LEDs on string
    
                  or        dira, clkpin            ' Set the direction bits for the pins - P2 equivalent for dira??
                  or        dira, datpin
                  mov       temp1, #0  wz           ' Set Z flag.
    
    main_loop                                       ' Main loop
                  mov       ledcnt, numled          ' Load LED counter
                  rdlong    leddata, ptra            ' Get address of LED array
    
    led_loop
                  muxnz     outa, clkpin            ' Set Clock Pin low
                  rdlong    shiftout, leddata       ' Get LED value
                  shl       shiftout, #8            ' Shift into position, we only use lower three bytes
                  or        shiftout, led_mask      ' Add $80 to all three bytes
                  add       leddata, #4             ' Point leddata to next long in array
                  mov       bitcnt, #24             ' Shift 24 bits
    
    shift_data
                  shl       shiftout, #1 wc         ' Shift bit from shiftout and store it in carry flag
                  muxnz     outa, clkpin            ' Set Clock Pin low
                  muxc      outa, datpin            ' Set Data Pin to the value of the carry flag
                  call      #small_del              ' Delay in between clock pulses
                  muxz      outa, clkpin            ' Set Clock Pin high
                  call      #small_del              ' Delay in between clock pulses
                  djnz      bitcnt, #shift_data     ' Continue shift the 24 bits
    
                  djnz      ledcnt, #led_loop       ' Continue shift data for LEDs on string
    
                  mov       bitcnt, #8              ' Shift 8 bits, these are the 'latch byte' for the driver
                  mov       shiftout, #0            ' All zero.
    shift_latch
                  shl       shiftout, #1 wc         ' Shift bit from shiftout and store it in carry flag
                  muxnz     outa, clkpin            ' Set Clock Pin low
                  muxc      outa, datpin            ' Set Data Pin to the value of the carry flag
                  call      #small_del              ' Delay in between clock pulses
                  muxz      outa, clkpin            ' Set Clock Pin high
                  call      #small_del              ' Delay in between clock pulses
                  djnz      bitcnt, #shift_latch    ' Do next latch bit
    
                  mov       delcnt, latchdel        ' Delay for latch to take place
                  getct     delcnt
                  waitx     delcnt
    
                  jmp       #main_loop              ' And continue on forever
    
    small_del
                  mov       delcnt, shortdel        ' Delay in between clock pulses
                  getct     delcnt
                  waitx     delcnt
    small_del_ret ret
    
    '' Data...
    shortdel      long 10           ' Delay in between shift pulses.
    latchdel      long 1000         ' Delay in between sending data to strip.
    led_mask      long $80808000    ' Since all values need to be between $80 and $FF, or color with $80.
    
    '' Parameters...
    
    shiftout      res   1   ' Value to shift out.
    clkpin        res   1   ' Clock pin
    datpin        res   1   ' Data pin
    numled        res   1   ' Number of LEDs in string
    leddata       res   1   ' Address of LED data in the array
    
    temp1         res   1   ' Temp 1
    temp2         res   1   ' Temp 2
    bitcnt        res   1   ' Bits to shift
    ledcnt        res   1   ' LEDs on string
    delcnt        res   1   ' Delay loop
    
    {{
    ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
    │                                                   TERMS OF USE: MIT License                                                  │
    ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
    │Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    │
    │files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    │
    │modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
    │is furnished to do so, subject to the following conditions:                                                                   │
    │                                                                                                                              │
    │The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
    │                                                                                                                              │
    │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          │
    │WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         │
    │COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   │
    │ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         │
    └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
    }}
    
  • Is "led_data" supposed to be an array of longs? Looks like only defined as single...
    Maybe should be "long led_data[10]" or so?

    Also, in your assembly, you are reading in only the par1 value with the rdlong.
    But, you need to read in the value of 3 more parameters with rdlongs...

  • An easier way to pass fixed parameters is just to set them in the assembly code before starting the cog.

    Just change the assembly parameters from type "res" to type "long".
    Then, set them in the Spin code before starting the cog...
  • JonnyMacJonnyMac Posts: 7,318
    edited 2020-11-24 (1:20 AM)
    As Ray points out, you're treating led_data like an array, but you declared just a single long. This could cause some of the other variables to get clobbered.

    While you *can* do a near direct translation of P1 to P2 PASM, the P2 instruction set is larger and has many instructions that simplify things. After looking at your code and the Adafruit driver, this is how I coded the P2 assembly section (compiles, but no hardware to test with). Notice that it's shorter due to the nice instructions in the P2.
    dat
    
                    org       0
    
    lpd8806         setq      #5-1                                  ' get 5 parameters from hub
                    rdlong    sclk, ptra
    
                    drvl      sclk                                  ' make outputs low
                    drvl      sdo
    
    led_main        mov       t1, nleds                             ' prep for new colors
                    add       t1, #31
                    shr       t1, #5
                    mov       ledbits, #0
    
    .loop           call      #shift_out                            ' send 0 for every 32 pixels
                    djnz      t1, #.loop
    
                    mov       count, nleds                          ' set count of leds
                    mov       hub, p_buf                            ' point to start of buffer
    
    next_led        rdlong    ledbits, hub                          ' read color value
                    add       hub, #4                               ' point to next
                    or        ledbits, ##$80_80_80_00               ' add sync bits to color bytes
                    call      #shift_out                            ' green
                    call      #shift_out                            ' red
                    call      #shift_out                            ' blue
                    djnz      count, #next_led
    
                    jmp       #led_main
    
    
    shift_out       rep       #7, #8                                ' shift color bits
                     shl      ledbits, #1                   wc      ' get msb in c
                     drvc     sdo                                   ' c --> sdo
                     nop                                            ' let sdo settle
                     drvh     sclk                                  ' clock high
                     waitx    ctix
                     drvl     sclk                                  ' clock low
                     waitx    ctix
                    ret
    
    ' -------------------------------------------------------------------------------------------------
    
    nleds           res       1                                     ' leds to update
    p_buf           res       1                                     ' hub address of buffer
    sdo             res       1                                     ' data pin
    sclk            res       1                                     ' clock pin
    ctix            res       1                                     ' ticks in 1/2 sclk period
    
    count           res       1                                     ' # of leds to update
    hub             res       1                                     ' hub address of buffer
    ledbits         res       1                                     ' color to shift out
    
    t1              res       1                                     ' temp vars
    t2              res       1
    
                    fit       472
    

    I've attached my test file (object and test code in one place). If you can verify that this works, I'll break out the object code and get it posted. I'm guessing you already have some LPD8806 strips -- they don't seem particularly popular, and it's hard to find good info about them. The best info came from the comments of the Adafruit driver.

    Okay... back to a P1 project that has been kicking my butt for far too long....
  • Wow, thanks for the quick responses! I was trying to duplicate the code setup I use for a P1 that works and copied led_data from that P1 code. I’ll check again and see if I missed setting it up as an array. On the P1 I have 24 RGB LEDs but for testing I’m using a left over string of 8 LEDs. I tried going over each PI assembly instruction and looking up the P2 equivalent which is why I wasn’t necessarily using the best instruction for what I needed to do. I knew I was going to have fun when the first P1 instruction was MOV temp1, par and there was no par register in P2. It took some digging but I finally found something that said to use ptra instead of par. Then I ran across something that said this means you need to use rdlong instead of mov to get the data to the right place.
    JonnyMac, I’ll try out your test file and see what happens. I want to go through each line to really understand what is happening there. I spent a lot of time reading and looking at P2 assembly code, is there any written in-depth descriptions for the P2 assembly code out there like what is available for the P1 assembly yet? So far my best resource has been searching the forum and reading applicable P2 threads.
  • Seen the instructions spreadsheet?
    There's a link at propeller.parallax.com
  • JonnyMac wrote: »
    I'm guessing you already have some LPD8806 strips -- they don't seem particularly popular, and it's hard to find good info about them. The best info came from the comments of the Adafruit driver.
    Yea, I got these RGB strips from Adafruit quite a while ago after finding the driver in the P1 Object Exchange. I don’t remember but I think at the time this was the best RGB option available. Luckily they worked very well for my needs and I have quite a bit of code built up around using them. I was almost ready to swap them out for something a bit more modern and common but decided to give it another go at making these work. The original author of the LED driver submitted this back in 2012 and hasn’t posted on the forum since then either.
  • JonnyMacJonnyMac Posts: 7,318
    edited 2020-11-24 (2:00 AM)
    I may order some just so that I can futz with the driver. I came across some mbed code with this:
    // This is how data is pushed to the strip.  Unfortunately, the company
    // that makes the chip didnt release the  protocol document or you need
    // to sign an NDA or something stupid like that, but we reverse engineered
    // this from a strip controller and it seems to work very nicely!
    
    void LPD8806::show(void) {
        uint16_t i, nl3 = numLEDs * 3; // 3 bytes per LED
     
        for (i=0; i<nl3; i++ ) {
            write(pixels[i]);
        }
     
        // Write latch at end of data; latch length varies with number of LEDs
        writezeros(3 * ((numLEDs + 32-1) / 32));
    }
    
    The Adafruit driver sends one zero per 32 pixels; this code sends three per 32 pixels. It is odd that there is no protocol spec floating around anywhere.
  • Rayman wrote: »
    Seen the instructions spreadsheet?
    There's a link at propeller.parallax.com

    The only thing I know of is the OE object that I used in my P1 code. Not sure what you mean by a instructions spreadsheet? The data sheet from the manufacturer doesn’t give any usable info (most info I ever found was from the Adafruit website) on timing or protocols. The manufacturer was really trying to keep everything proprietary, probably that’s why it never became popular/wide spread.
  • Rayman wrote: »
    Seen the instructions spreadsheet?
    There's a link at propeller.parallax.com
    JonnyMac wrote: »

    I misunderstood what Ray was referring to! Yes, I have that spreadsheet up continuously on the screen while studying P2 assembly.
  • There are not a lot of examples out there yet. I've been trying to learn from Chip's code -- you can get that in the PNut download. I have also started converting some of my P1 objects to the P2, though I feel like I should probably loop back a review them as I get more comfortable with PASM2.
  • Jon, sorry for the delay in testing your code, the wife required me to assist with Thanksgiving day preparations and she has priority! She is taking a nap now so I was able to download and try it out.

    After verifying I had Clk and data on the right pins I ran the code. The results are that only the 8 red LEDs are active continuously. No sign of the green or blue LEDs being active. Interestingly P56-P59 on the P2 Eval board LEDs are pulsing between half and full brightness a little faster than once per second.

    I'm going to put in some debugging code into this and see if I can figure out what is happening. I'm running this code using Propeller Tool version 2.3.00 Alpha in case that makes a difference.

  • Been studying your code and have a few questions:
    lpd8806         setq      #5-1                                  ' get 5 parameters from hub
                              rdlong    sclk, ptra
                              drvl      sclk                                  ' make outputs low
                              drvl      sdo
    
    The setq command followed by rdlong downloads the ptra register which contains the addresses for the 4 parameters being passed to the code. rdlong command specifically puts the first parameter in ptra into variable sclk which is driven low. It then drives sdo low but how is sdo assigned to the second parameter? I see the variable list at the end of the code, are the variables automatically loaded in the order that the variable is listed at the end of the code?
  • SetQ sets the number of longs to read (ie repeats the next instruction)
    The rdlong repeats, while both addresses internally are incremented (ie the addresses of clk and ptra get incremented)

    So, reads 5 longs from hub pointed to by ptra, into cog registers starting at clk.
  • Well... there's the problem. I changed the order of variables but forgot to update the code that moves the setup values from the hub to the cog. It should be:
    lpd8806         setq      #5-1                                  ' get 5 parameters from hub
                    rdlong    nleds, ptra
    
    ...since the first variable of the group is the number of LEDs. I am at a friend's house for Thanksgiving, but I have my P2 Eval and a logic analyzer; I'm going to have a look and will post an update when I get it working.
  • JonnyMacJonnyMac Posts: 7,318
    edited 2020-11-25 (11:47 PM)
    While listening to the P2 meeting, I wrote this inline PASM that seems to do the right thing. I'm just having it send the data for one pixel but this looks correct (pixel value was $000C0000).
    pub lpd8806_pasm(dpin, cpin, count, p_buf) | tix, npix, work
    
    '' Test driver for LPD8806 LEDs
    '' -- color values packed long, organized as $GG_RR_BB_00
    ''    * constituent bytes limited $00..$7F
    
      tix := (clkfreq / 100_000) >> 2                               ' slow for testing
    
      org
                    drvl      dpin
                    drvl      cpin
                    waitx     tix
    
                    mov       npix, count
    
    show_pix        rdlong    work, p_buf
                    or        work, ##$80_80_80_00
                    add       p_buf, #4
    
                    rep       #7, #24
                     shl      work, #1                      wc
                     drvc     dpin
                     nop
                     drvh     cpin
                     waitx    tix
                     drvl     cpin
                     waitx    tix
    
                    djnz      npix, #show_pix
    
    latch_pix       drvl      dpin
    
                    add       count, #31
                    shr       count, #2                             ' count := count / 32 * 8
                    
                    rep       #4, count
                     drvh     cpin  
                     waitx    tix     
                     drvl     cpin  
                     waitx    tix   
      end
    
    I will re-visit that other program with the full driver -- we may have this solved!
    1544 x 414 - 25K
  • I updated the PASM2 driver and found a gotcha in a longmove() instruction for setup. This version seems to work. I set it up for four pixels, assigned them red, green, blue, and black, and got the output you see in the attached (annotated) image.
  • JonnyMac wrote: »
    I updated the PASM2 driver and found a gotcha in a longmove() instruction for setup. This version seems to work. I set it up for four pixels, assigned them red, green, blue, and black, and got the output you see in the attached (annotated) image.

    That is fantastic, this version worked right off the bat. Now I need to study the pasm code and understand what it is really doing and how. What logic analyzer are you using?

    I really appreciate your help, this is my first real attempt at pasm, still have a lot to learn! Have a great Thanksgiving tomorrow!

    Bob
  • That's really great news. What I will do now is make a proper object that can be used in any project. I'll review everything and add comments to help others. Will post that later.

    When traveling as I am this week, I use this $13 logic analyzer:
    -- https://www.amazon.com/gp/product/B077LSG5P2
    It's not fancy, but for simple projects like this it works well.

    The software that controls it is:
    -- https://sigrok.org/wiki/PulseView
    It even has built in protocol analyzers that I've used when developing P2 code for 1-Wire and my SPI object.
  • If you would, Bob, give this version a try. I separated the driver code from the application code and did some clean-ups (replaced "led" with "pixel" throughout). I tested as well as I think I can with my logic analyzer, so I don't expect any ugly surprises. If you can verify this works, I'll send it to Parallax for in inclusion in the P2 object library.
  • The new driver tests out fine, no issues as far as I can see using the test demo. Thank you again for your help!
  • Thanks for testing. I will get it submitted to Parallax.
  • @DiverBob Would you mind giving this version of the driver a try? I really want it to be color compatible with my other pixel drivers, so I changed to 8-bit colors, and I store the longs in the buffer in the order of $RR_GG_BB_xx. Under the hood, the movbyts instruction fixes the order (this is really cool), and then a shift right of the long reduces the color bytes to seven bits. The sync bits are added after, so no clean-up is required for the bits that move from bit0 of one byte to bit7 of another.

    There is a set of color constants in this driver.

    Thanks for trying this. I checked it with the logic analyzer and it appears to be working properly.

    -- Jon
Sign In or Register to comment.