Rainboard SPI LED Serial Conflict

Bliptronics lights / SPI / Serial breaking

The Arduino library [1] that comes with the bliptronics lights uses SPI [2] in order to send data to the lights quickly. It was found that once the blip library was included with the project and init was called, hardware serial data receive (rx pin 0) on the arduino would not work. The current explanation is that the blip library spends a large quantity of time inside an interrupt. This is causing the serial rx interrupt to not be fired and the receive data is lost. So cercumvent this issue, SoftwareSerial is used instead of hardware serial.


LEDS

  • LPD6803 chip with 15 bit color (5 bits per LED, 3 LEDs per pixel)
  • Max clock rate of 25mhz
  • 4 Wire hookup (5V, Gnd, Data, Clock)
  • LED diameter - 8mm
  • PCB Size - 20mx10mm
  • Voltage - 5.0V
  • Max Watts - 0.3W (60mA)
  • Beam Angle - 90-120 degrees
  • Luminance - 9 lm

There is good additional information on the LadyAda Page [3]

Here is the chunk of code that causes the Hardware Serial rx to fail.

BLIP_LEDS_SPI.cpp

void CBLIP_LEDS_SPI::init()                     // Initialisation Function
{ 
 ..........
  SPDR = 0x00; 
}

void CBLIP_LEDS_SPI::setDirty() { m_nDirty = 1; } 


#define SPI_DATA(data) SPDR=data;

 //About to send out data stream from start? We need to send 
      
      
ISR(SPI_STC_vect) 
{
      static unsigned char nState=0;             //nState:
                                                 //0-3 set out a 0
      if(nState<=3)                              //4 - init data pointer and send first byte
                                                 //5 - sending data until we hit end
      {                                          //6 - all data sent, just send a zero until dirty is set, then set nstate to 0
        nState++;
        SPI_DATA(0);
        return;
      }

      if(nState==4)
      {
        pData = BL.m_pData;
        nState++;  
      }

      if(nState==5)    
      {
            
        if(pData >= BL.m_pDataEnd)
        {
            nState = 6;
        }
        SPI_DATA (*(pData++)); 
        return;
      }
      if(nState==6)    
      {
            
        if(BL.m_nDirty == 1)
        {
            nState=0;
            BL.m_nDirty = 0; 
        }
        SPI_DATA (0);        
        return;
      }
}


Breakdown

When SPI is initialized, it is set to turn on interrupts. This interupt gets called when a data byte is received on the MISO line or when a data byte is finished being sent on the MOSI line.

Within the init function the SPDR register is set with a byte of data to be sent (0x00)

SPDR = 0x00;

This causes the SPI clock to tick and the byte to be sent out on the MOSI line. When the byte is finished sending, the ISR(SPI_STC_vect) function gets called. This function ends up looping infinitely. Each loop puts a byte into the SPDR register then waits for it to send. Once the byte is sent the interrupt is called again.

The end result looks something like this:

Send: 
0x00 //From the init function
0x00 //State = 0
0x00 //State = 1
0x00 //State = 2
0x00 //State = 3
0x?? //State = 5
    ...... continue sending light data while not at end of light data array
0x00 //State = 6
    ...... continue sending 0x00 constantly until the m_nDirty but is set (by calling the show method)
Loop to state 0

Therefore this loop is called at the rate it takes to send a byte via SPI. In the current library the speed of the SPI is set to frequency oscillator / 2 which should be 16MHz of the arduino board divided by two, so 8MHz. The end result is that the interrupt is called often (perhaps close to 1MHz).

Result

The end result is that the rx interrupt of the hardware serial does not have a chance to get called as the interrupt is disabled while within the LED processing loop. One initial solution was not prevent to the interrupt look from being infinite by only sending data when the lights needs to be changed. It was discovered that this solution did not work because the PWM for the lights operates based on the clock signal from the SPI clock. So, if we stop sending data (0x00) through the SPI MOSI line, the clock stops and the PWM on the lights stop as well. This also causes issue with trying to do the lights via bit banging. It is definetly possible to do but should happen at a much slower rate and a much higher cpu usage than SPI.


SPI Resources

RocketNumberNine - Using SPI on AVR

Arduino SPI

Wikipedia SPI

Information on usage of SPI and Serial

SPI Speed comparisions

External Links


Comments

blog comments powered by Disqus