0

lpd8806 bare minimum wait during latch propogation
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

lpd8806 bare minimum wait during latch propogation

by mtbf0 on Mon Apr 23, 2012 9:08 am

has anyone managed to determine the bare minimum wait that can be used in the show routine in the lpd8806 library?

i notice that the library uses 3 ms. the posts about using hardware spi used 2 ms. has anyone gone lower? is this value dependent on the length of the strip? has anyone hung a logic analyzer downstream of the first chip to see what's happening during this delay?

seems like i saw a port to one of those li'l nxp dip boards whose name escapes me that did away with the wait altogether.

i know. all questions. no results. i'll wander into the other room, now, and try 1ms, then maybe no wait at all. like i should have done to begin with...
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am
Location: oakland ca

Re: lpd8806 bare minimum wait during latch propogation

by pburgess on Mon Apr 23, 2012 12:06 pm

Not certain on this, but I think that delay might be a vestige of library code adapted from one of the other LED types, and possibly not needed with the LPD8806. Try commenting out the line and see if things still work reliably for you.

pburgess
 
Posts: 4020
Joined: Sun Oct 26, 2008 2:29 am

Re: lpd8806 bare minimum wait during latch propogation

by mtbf0 on Mon Apr 23, 2012 3:48 pm

oh, yeah! commented out that wait and everything appears to work just fine. took me awhile to get it right, because i forgot that the example sketches are read only and could not figure out why only the first 32 of my 160 leds were working. duh.

now to see how fast i can push data onto the thing.
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am
Location: oakland ca

Re: lpd8806 bare minimum wait during latch propogation

by ericgu on Wed Apr 25, 2012 12:44 am

You don't need the wait.

You also don't need to write more than one latch byte, despite what the library does.

If you use either the SPI or the optimized library, you can push data out very fast.
ericgu
 
Posts: 58
Joined: Mon Oct 18, 2010 11:24 pm

Re: lpd8806 bare minimum wait during latch propogation

by mtbf0 on Wed Apr 25, 2012 7:58 am

ericgu wrote:You don't need the wait.

You also don't need to write more than one latch byte, despite what the library does.

If you use either the SPI or the optimized library, you can push data out very fast.


thanks. i may need even faster. not sure. i was thinking of using an output compare pin to clock data out of an spi flash chip at 8MHz. sadly the choices are 5v tolerant in soic or not 5v tolerant in dip.

still curious about what happens downstream of the first lpd8806 after it sees that first zero. i guess that as soon as it gets the high order bit and sees the zero it may start propagating that, in which case it would take one bit rater than one byte to set the latch.
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am
Location: oakland ca

Re: lpd8806 bare minimum wait during latch propogation

by mtbf0 on Wed Apr 25, 2012 8:23 am

ok. tried that. it works. sort of. made the following change to the library.

Code: Select all | TOGGLE FULL SIZE
//  writeLatch(numLEDs); // Write latch at end of data
    *dataport &= ~datapinmask; // Data is held low throughout
    *clkport |=  clkpinmask;
    *clkport &= ~clkpinmask;
  // We need to have a delay here, a few ms seems to do the job
  // shorter may be OK as well - need to experiment :(
  //delay(pause);


running the strandtest sketch almost everything works. it freezes after the magenta chase and after the blue fill so i missed the red fill and the first rainbow fade.

running 5m. not using hardware spi.
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am
Location: oakland ca

Re: lpd8806 bare minimum wait during latch propogation

by mtbf0 on Wed May 02, 2012 8:47 am

now running strandtest using hardware spi with the clock divider set to 2, writing a single byte of zeroes to latch with no delay. runs lickety-split with no anomalies so far.

here's the modified lpd8806.cpp that i'm using.

Code: Select all | TOGGLE FULL SIZE
#include "SPI.h"
#include "LPD8806.h"

// Arduino library to control LPD8806-based RGB LED Strips
// (c) Adafruit industries
// MIT license

/*****************************************************************************/

// Constructor for use with hardware SPI (specific clock/data pins):
LPD8806::LPD8806(uint16_t n) {
  alloc(n);
  updatePins();
}

// Constructor for use with arbitrary clock/data pins:
LPD8806::LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin) {
  alloc(n);
  updatePins(dpin, cpin);
}

// Allocate 3 bytes per pixel, init to RGB 'off' state:
void LPD8806::alloc(uint16_t n) {
  // Allocate 3 bytes per pixel:
  if(NULL != (pixels = (uint8_t *)malloc(n * 3))) {
    memset(pixels, 0x80, n * 3); // Init to RGB 'off' state
    numLEDs = n;
  } else numLEDs = 0;
  begun = slowmo = false;
  pause = 3;
}

// via Michael Vogt/neophob: empty constructor is used when strip length
// isn't known at compile-time; situations where program config might be
// read from internal flash memory or an SD card, or arrive via serial
// command.  If using this constructor, MUST follow up with updateLength()
// and updatePins() to establish the strip length and output pins!
LPD8806::LPD8806(void) {
  numLEDs = 0;
  pixels  = NULL;
  begun   = slowmo = false;
  pause   = 3;
  updatePins(); // Must assume hardware SPI until pins are set
}

// Activate hard/soft SPI as appropriate:
void LPD8806::begin(void) {
  if(hardwareSPI == true) {
    startSPI();
  } else {
    pinMode(datapin, OUTPUT);
    pinMode(clkpin , OUTPUT);
    writeLatch(numLEDs);
  }
  begun = true;
}

// Change pin assignments post-constructor, switching to hardware SPI:
void LPD8806::updatePins(void) {
  hardwareSPI = true;
  datapin     = clkpin = 0;
  // If begin() was previously invoked, init the SPI hardware now:
  if(begun == true) startSPI();
  // Otherwise, SPI is NOT initted until begin() is explicitly called.

  // Note: any prior clock/data pin directions are left as-is and are
  // NOT restored as inputs!
}

// Change pin assignments post-constructor, using arbitrary pins:
void LPD8806::updatePins(uint8_t dpin, uint8_t cpin) {

  if(begun == true) { // If begin() was previously invoked...
    // If previously using hardware SPI, turn that off:
    if(hardwareSPI == true) SPI.end();
    // Regardless, now enable output on 'soft' SPI pins:
    pinMode(dpin, OUTPUT);
    pinMode(cpin, OUTPUT);
    writeLatch(numLEDs);
  } // Otherwise, pins are not set to outputs until begin() is called.

  // Note: any prior clock/data pin directions are left as-is and are
  // NOT restored as inputs!

  hardwareSPI = false;
  datapin     = dpin;
  clkpin      = cpin;
  clkport     = portOutputRegister(digitalPinToPort(cpin));
  clkpinmask  = digitalPinToBitMask(cpin);
  dataport    = portOutputRegister(digitalPinToPort(dpin));
  datapinmask = digitalPinToBitMask(dpin);
}

// Enable SPI hardware and set up protocol details:
void LPD8806::startSPI(void) {
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV2);  // 2 MHz
  // SPI bus is run at 2MHz.  Although the LPD8806 should, in theory,
  // work up to 20MHz, the unshielded wiring from the Arduino is more
  // susceptible to interference.  Experiment and see what you get.

  writeLatch(numLEDs);
}

uint16_t LPD8806::numPixels(void) {
  return numLEDs;
}

// Change strip length (see notes with empty constructor, above):
void LPD8806::updateLength(uint16_t n) {
  if(pixels != NULL) free(pixels); // Free existing data (if any)
  if(NULL != (pixels = (uint8_t *)malloc(n * 3))) { // Alloc new data
    memset(pixels, 0x80, n * 3); // Init to RGB 'off' state
    numLEDs = n;
  } else numLEDs = 0;
  // 'begun' state does not change -- pins retain prior modes

  if(begun == true) writeLatch(n); // Write zeros for new length
}

// Issue latch of appropriate length; pass # LEDs, *not* latch length
void LPD8806::writeLatch(uint16_t n) {

  // Latch length varies with the number of LEDs:
  // n = ((n + 63) / 64) * 3;

  if (hardwareSPI) {
    SPI.transfer(0);
  } else if(slowmo) {
    digitalWrite(datapin, LOW);
    for(uint16_t i = 8; i>0; i--) {
      digitalWrite(clkpin, HIGH);
      digitalWrite(clkpin, LOW);
    }
  } else {
    *dataport &= ~datapinmask; // Data is held low throughout
    for(uint16_t i = 8; i>0; i--) {
      *clkport |=  clkpinmask;
      *clkport &= ~clkpinmask;
    }
  }
}

// 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
 
  // write 24 bits per pixel
  if (hardwareSPI) {
    for (i=0; i<nl3; i++ ) {
      SPDR = pixels[i];
      while(!(SPSR & (1<<SPIF)));
    }
  } else if(slowmo) {
    for (i=0; i<nl3; i++ ) {
      for (uint8_t bit=0x80; bit; bit >>= 1) {
        if(pixels[i] & bit) digitalWrite(datapin, HIGH);
        else                digitalWrite(datapin, LOW);
        digitalWrite(clkpin, HIGH);
        digitalWrite(clkpin, LOW);
      }
    }
  } else {
    for (i=0; i<nl3; i++ ) {
      for (uint8_t bit=0x80; bit; bit >>= 1) {
        if(pixels[i] & bit) *dataport |=  datapinmask;
        else                *dataport &= ~datapinmask;
        *clkport |=  clkpinmask;
        *clkport &= ~clkpinmask;
      }
    }
  }
   
  writeLatch(numLEDs); // Write latch at end of data

  // We need to have a delay here, a few ms seems to do the job
  // shorter may be OK as well - need to experiment :(
  // delay(pause);
}

// Convert separate R,G,B into combined 32-bit GRB color:
uint32_t LPD8806::Color(byte r, byte g, byte b) {
  return 0x808080 | ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b;
}

// Set pixel color from separate 7-bit R, G, B components:
void LPD8806::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
  if(n < numLEDs) { // Arrays are 0-indexed, thus NOT '<='
    uint8_t *p = &pixels[n * 3];
    *p++ = g | 0x80; // LPD8806 color order is GRB,
    *p++ = r | 0x80; // not the more common RGB,
    *p++ = b | 0x80; // so the order here is intentional; don't "fix"
  }
}

// Set pixel color from 'packed' 32-bit RGB value:
void LPD8806::setPixelColor(uint16_t n, uint32_t c) {
  if(n < numLEDs) { // Arrays are 0-indexed, thus NOT '<='
    uint8_t *p = &pixels[n * 3];
    *p++ = (c >> 16) | 0x80;
    *p++ = (c >>  8) | 0x80;
    *p++ =  c        | 0x80;
  }
}

// Query color from previously-set pixel (returns packed 32-bit GRB value)
uint32_t LPD8806::getPixelColor(uint16_t n) {
  if(n < numLEDs) {
    uint16_t ofs = n * 3;
    return ((uint32_t)((uint32_t)pixels[ofs    ] << 16) |
            (uint32_t)((uint32_t)pixels[ofs + 1] <<  8) |
             (uint32_t)pixels[ofs + 2]) & 0x7f7f7f;
  }

  return 0; // Pixel # is out of bounds
}
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am
Location: oakland ca

Re: lpd8806 bare minimum wait during latch propogation

by JohnSan on Wed May 02, 2012 11:25 am

I can see you have commented out the delay in LPD8806.cpp
But there is something strange about SPI_CLOCK_DIV2

Looking in the SPI.h version I have, the values are defined as follows:-
#define SPI_CLOCK_DIV4 0x00
#define SPI_CLOCK_DIV16 0x01
#define SPI_CLOCK_DIV64 0x02
#define SPI_CLOCK_DIV128 0x03
#define SPI_CLOCK_DIV2 0x04
#define SPI_CLOCK_DIV8 0x05
#define SPI_CLOCK_DIV32 0x06
#define SPI_CLOCK_DIV64 0x07

So the value change from DIV8 to DIV2 may not be as expected.
Also,
#define SPI_CLOCK_DIV64 is declared twice with two different values.
I don't understand where they are used....
or do I have a corrupt copy of SPI.h maybe?
JohnSan
 
Posts: 10
Joined: Mon Feb 27, 2012 11:53 am

Re: lpd8806 bare minimum wait during latch propogation

by mtbf0 on Thu May 03, 2012 12:03 am

note that all of the values defined can be expressed in 3 bits.

the two low order bits of the spi clock selections are used to set a couple of bits in the microcontroller's spi control register. these bits select a divisor of the system clock frequency that determines the spi clock frequency.

00 selects a divisor of 4.
01 selects a divisor of 16.
10 selects a divisor of 64.
11 selects a divisor of 128.

the next bit is used to set the spi double speed bit in the spi status register and, when set, doubles the spi clock frequency, so

100 selects a divisor of 2.
101 selects a divisor of 8.
110 selects a divisor of 32.
111 selects a divisor of 64, again, since 64 is half of 128.

all of this is described in the atmegax8 datasheet.
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am
Location: oakland ca

Re: lpd8806 bare minimum wait during latch propogation

by mtbf0 on Thu May 03, 2012 12:17 am

JohnSan wrote:I don't understand where they are used....


one of the values is passed to the following function, which is defined in SPI.cpp.

Code: Select all | TOGGLE FULL SIZE
void SPIClass::setClockDivider(uint8_t rate)
{
  SPCR = (SPCR & ~SPI_CLOCK_MASK) | (rate & SPI_CLOCK_MASK);
  SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((rate >> 2) & SPI_2XCLOCK_MASK);
}


JohnSan wrote:I can see you have commented out the delay in LPD8806.cpp


also modified writeLatch, to output only one zero byte instead of (n + 63) / 64 * 3 zero bytes.
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am
Location: oakland ca

Re: lpd8806 bare minimum wait during latch propogation

by JohnSan on Sat May 05, 2012 12:53 pm

All good information.
Thanks.

I have noticed, taking the delay out really increases the brightness of the first 'dim green' led.
(CI/CO continuously running at a number of MHz without the delay).

This is probably because I have a strip.show() free running in the void loop(); This seemed to be the way to go when I first started this project. But. Things change.
Obviousy, because of the delay being present, the void loop time was just over 3ms.
The removal of the delay has increased the loop speed significantly. (Which is good)!
So, I will take the strip.show() out of the loop and just call it when something changes the data to the strip. Probably what I should have done in the first place .....
JohnSan
 
Posts: 10
Joined: Mon Feb 27, 2012 11:53 am

Please be positive and constructive with your questions and comments.