Issues with converting RGB LED driver code (LPD8806)
DiverBob
Posts: 1,108
in Propeller 2
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:
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 >>= 1Attempt 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
Comments
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: If you want even faster output, you can use inline PASM2. Try this, too.
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: 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.
Good catch, didn't even notice that part. It runs at 20Mhz but not 200Mhz.
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?
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...
Just change the assembly parameters from type "res" to type "long".
Then, set them in the Spin code before starting the cog...
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.
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....
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.
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.
-- https://docs.google.com/spreadsheets/d/1_vJk-Ad569UMwgXTKTdfJkHYHpc1rZwxB-DcIiAZNdk/edit#gid=0
I misunderstood what Ray was referring to! Yes, I have that spreadsheet up continuously on the screen while studying P2 assembly.
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.
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.
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
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.
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