DotStar display strips (e.g. Adafruit) use the APA102 RGB LED control IC. This chip uses slightly simplified 32-bit SPI communication (no chip-select line) to receive data. The LEDs must be driven at 5 volts and take about 60 mA/LED at full power. The 3.3 volt PIC32 logic is level-shifted to 5 volts using a 74LS125 as recommended on the Adafruit site. The 32-bit protocol is:

  • Start frame -- 32 bits of zero, 0x0
  • LED frame (repeated for the Data Field below) The following macro constructs a LED frame from intensity, blue, green, and red The intensity (Called global
     #define PIXEL_FRAME(i,r,g,b)(0xe0000000 | (((0x1f & (i)))<<24) | ((0xff & (b))<<16) | ((0xff & (g))<<8) | (0xff & (r)))
    in the diagram) sets the gain of the pixel and is 5 bits. The red, green, and blue intensities are 8-bits
  • End frame -- 32 bits of one, 0xffffffff This suggests that a full-brightness, full white pixel, cannot be distiguished from a end-frame. The FULL_ON macro sets full intensity ot 0x1e rather than 0x1f to avoid generating a false end-frame.

The application must fill an array with colors to display, then invoke a transmit routine which formats the array and blasts it to the SPI channel. The transmit routine sends a start-frame, 150 LED-frames, then an end-frame. Total time to transmit at 10 MHz SPI bus rate is 0.58 milliseconds. The API consists of just a few routines:

  • Setting PixelNum defines the length of the pixel array. Using 1 meter of the high density 144 LED/meter string means: #define PixelNum 144
  • A function to write all the pixels to the array via hardware SPI channel 2. void write_pixels(void)
  • A single pixel r,g,b,intensity values can be read back from the array using void get_pixel_rgb(int pixel_index, char* r, char* g, char* b, char* intensity)
  • A single pixel color is specified using one of two functions:
    • void set_pixel_rgb(int pixel_index, char r, char g, char b, char intensity) This function allows you to specify the LED index number, and red, green, and blue intensities (0-255), plus a coarse intensity scaling value from 0-to-31. It takes practice to map r,g,b values to specific hues, as shown below. (reference)
    • void set_pixel_hsv(int pixel_index, float h, float s, float v, char intensity) This function allows you to specify the LED index number, a hue (0-360 degrees), a saturation (0-1.0 float), and a value (dark-light 0-1.0 float) plus a coarse intensity scaling value from 0-to-31. Setting hue directlymakes it easier for humans to specify the desired color in terms of the standard color wheel, scaled to 360 degrees (reference).

There are demo codes for writing RGB and for HSV which work well on a 1 meter strip. The colors are driven by direct-digital-synthesis sine waves of settable frequency from 0.5 millihertz to faster than you can see. Frequency setting is via the dds_inc variables. As configured a increment of 2000 corresponds to about 1 Hz. With a 16 bit DDS accumulator the frequency of sine output is related to increment as increment = Fout * 2^16 * DDS_sample_time So for example, for 2 Hz and sample time 0f 30 millisec, the inc = 2 * 2^16 * 0.030 = 3932. Since the sine tables are only 256 entries, only the top byte of the accumulator is used as an index.

An application of this system grabs music from a standard line-out signal (about 1 volt peak-to-peak), though a analog high-pass filter (to set the DC level to Vdd/2 shown below) to the ADC, then uses a nonlinear digital low pass filter of the ADC input to drive LED color. The audio is sampled at 8KHz. The mean of the sampled signal is subtracted from the signal, then the absolute value of the difference is lowpassed at approximately the rate of the LED update. But, if the current signal is greater than the low pass output, then in the current signal just replaces the low pass output. This helps to capture fast beat transients.