SmartMatrix APA102 Library / Open Hardware Photon APA102 Shield

Hi, I’m Louis, and I made the SmartMatrix Library and SmartMatrix Shield for Teensy to make it easy to display high quality graphics on low cost multiplexed RGB matrix displays. My friend @JasonCoon made the Aurora application that plays Animated GIFs, Patterns, and Audio Patterns using the SmartMatrix Shield, and together we build the SmartMatrix Display. I’m porting the SmartMatrix Library to Photon and will be designing a shield.

https://vine.co/v/eih2bxetUpH

I’m working on a port of the full SmartMatrix Library to Photon, including support for low cost multiplexed matrix panels, but it’s a big challenge because of the DMA differences between the Freescale processor used in the Teensy and the STM32 processor used in the Photon. In the meantime, I’m adding drivers for more types of LEDs to the SmartMatrix Library, starting with support for APA102 addressable LEDs (Adafruit calls these DotStars). The core features of the Photon APA102 driver are working now.

The SmartMatrix Library has these core features that I want to maintain for all displays:

  • Support for multiple layers with transparency, including scrolling text that scrolls automatically in the background
  • High color depth and automatic color correction (gamma correction)
  • Background display refresh using DMA
  • Brightness control without sacrificing color depth

I chose APA102s over more common WS2812s (Adafruit calls these Neopixels) because of the 2-wire interface and Global Brightness Control feature. The 2-wire interface allows the LEDs to be refreshed using the SPI peripheral, with DMA to automate the transfer in the background, freeing up the CPU. The Global Brightness Control (GBC) feature allows you to lower the brightness of individual LEDs while still retaining 24-bit color, and as I found through experimentation, can be used to get >24-bit color out of the LEDs by setting the GBC value for each LED dynamically based on a 16-bit per channel RGB value. Normally to get lower brightness without sacrificing color depth or > 24-bit color out of addressable LEDs you need to use dithering which takes up more CPU and can result in visible flickering at lower brightnesses.

Here’s a demo video showing the SmartMatrix Library in action on the Photon with a APA102 matrix, running two of the example sketches that are included in the library. The video is intended to demonstrate how the Dynamic GBC feature in SmartMatrix can provide similar results to FastLED’s temporal dithering at low (25%) brightness, with much less CPU usage, and can provide better results than temporal dithering at very low brightnesses (~3% brightness) where you see slight flicker with dithering and significant color banding without dithering. Note that the SmartMatrix Library is intended to work alongside FastLED and isn’t a replacement for it. I shared this dynamic GBC idea with @dgarcia a couple months ago, and hope it makes it into a future 16-bit version of FastLED.

https://vimeo.com/160718214/b9a6cb5220

The Library port is still in the early stages, but you can try it yourself. The sketches used to make the demo video above are included as examples.

Don’t let the “SmartMatrix” name scare you away if you want to drive a strip of APA102 LEDs and not a matrix, I intend to make the library usable for both 1-dimensional strips as well as matrices.

5 Likes

Here’s what the SmartMatrix Shield for Teensy looks like for reference, though I really want to design the Photon shield from the ground up.


Photos Courtesy Adafruit

After discussing with my friend @JasonCoon - who has done a bunch of awesome projects using addressable LEDs - we set the below priorities for the SmartMatrix APA102 Shield. I want the design to be useful for many projects, not just the ones we have in mind. Please share how you would want to use this shield and the features that do and don’t interest you so we can make the design useful for more people.

I’ll edit the below list as we get more feedback, nothing’s set in stone yet:

Priorities

Musts

  • 5V level shifting buffer for APA102 signals
    • According to the spec, APA102s need a minimum of 3.6V on the data and clock lines to recognize a ‘1’.
    • In practice, many times the Photon at 3.3V can drive these strips just fine, but the buffer is important for driving LEDs with a long cable between the Photon and the first APA102 in the chain, and to make sure the shield works with all APA102s.
  • Removable Photon
    • I really like the low profile SMT connectors used on the Particle Internet Button. Not only are they much lower profile than any other header solution I’ve seen, but they have an extra row of headers on each side which make it easy to plug in pins and cables for expansion.
  • Few (ideally no) through-hole parts
    • I want to make this board available for cheap, and using a lot of through-hole parts either drives the cost up, or passes the soldering effort onto the user
  • Easy prototyping connections
    • The shield will only take up two GPIOs to drive the APA102s, so the rest of the pins should be easily accessible for connecting to other parts of your project. Using low-profile connectors like on the Internet Button gives quick access to plugging in removable connectors or cables, and the ability to solder wires or pins to the top side of the board for a more permanent connection
  • Small size
    • The shield and Photon may need to be embedded in a small space depending on the project.

Wants

  • microSD card slot
  • Solutions for easily supplying power to shield and strip
    • USB can only safely supply 500mA to the Photon, the Photon needs up to 100mA on average, which leaves 400mA for driving an LED strip. At 60mA max current draw per LED, that’s only enough to drive six LEDs, a really short strip. Of course you may not be driving all LEDs at full brightness, and products like BlinkyTape get away with using USB to drive a strip of 30 LEDs. USB power is one option for powering a limited number of LEDs.
    • The SmartMatrix Shield has a Barrel Jack connector that’s used to supply power to the shield, Teensy, and matrix panel. The “High Current” jacks I’ve seen have a max rating of 5A, which is only enough for about 80 LEDs at max brightness. This could be a convenient way of supplying power for basic projects. The barrel jack and plug on the power supply is bulky and may not work well for space constrained applications. Maybe there’s a better alternative that is smaller and more flexible.
    • For projects that require more than 5A of current, it’s probably best to supply power to the LEDs directly, and the shield can get power from the strip connection.
  • Mounting hole(s)
  • Circuit/Code to keep APA102s off when board is first powered on
    • It’s annoying when first powering on a large matrix to get blinded by a lot of bright pixels while the Photon starts up.
    • I’m not sure how feasible this is to do with a circuit, or with code without forcing users to use the Photon’s semi-automatic or manual modes.

Likes

  • Electron Compatibility?
    • Is this a priority for you? If there’s enough interest I can prioritize it in the shield design. I have an Electron but haven’t used it yet, and don’t have any Electron APA102 project plans in the near future.
    • I believe all that has to be done is to make the shield compatible with the extra length and pins that the Electron has, but I haven’t done extensive research.
  • Redbear Duo Compatibility?
    • The Redbear Duo I believe has the same pinout and same CPU as the Photon, with the addition of BLE support. The board is a bit longer.

Ideas

These are ideas submitted by the community, that need to be discussed more:

I’ll be keeping the draft SmartMatrix Library and shield design up to date on GitHub during the design process. When the design is complete, you can order the board from OSH Park and assemble yourself, or buy one assembled from me.

Are you interested in using a shield like this? What features on the list are most interesting to you, and what are you interested in that’s not on the list?

5 Likes

Nice work. Do you drive the APA102 directly? I noticed that FastLED for the Photon currently doesn’t use hardware SPI, it just bitbangs (really fast though). Actually using the hardware SPI pins is a bit slower then using the software SPI pins (see my finding in the FastLED gitter). So I was wondering how you’ve implemented it in the end.

Can you control the global brightness for each led? Or does it work the same as FastLED global brightness with dithering?

Hi Kasper, I’m using the Photon’s SPI1 driver (with DMA under the hood) to drive the APA102s directly. Right now I’m not so concerned with SmartMatrix’s SPI speed (only driving 256 LEDs at 30FPS), so SmartMatrix is set to a 1MHz clock, but the important thing is the transfer is handled by DMA in the background.

I’m not sure why I limited the FastLED code to a 1MHz SPI clock. I just increased it to 12MHz and that looks much better, the flicker at 8% brightness isn’t nearly as noticeable, it looks more like some quick moving color banding jumping to black at the end of the fade.

Here’s a link to the relevant code. The GBC of each LED is set based on the overall display brightness (set with matrix.brightness()), and the brightest color (R, G, or B) for the current pixel.

If you look at the SmartMatrix Display demo Vine in my first post, it’s showing a background layer with a FastLED pattern or Animated GIF, and scrolling text on top. The text layer is at 100% brightness, and the background layer is something lower, probably 50% brightness. The background layer also has color correction applied to it.

When refreshing, the SmartMatrix Library uses 16-bit per color channel math, and will convert the original 8-bit per channel background to a 16-bit color to draw to the pixel. For a really dim color, the Dynamic GBC code will shift it up to 4 bits over so a color like {0x0010, 0x0010, 0x0010} would get drawn to the display as {0x01, 0x01, 0x01} with GBC set to 0x01 (the minimum) instead of the LED being off. This is done per pixel, so scrolling text at full brightness could be displayed over a dimmer background image while still keeping good color depth for the background image.

I updated the comparison video after setting FastLED’s SPI clock at 12MHz up from 1MHz originally. FastLED is now refreshing at >400Hz with Dithering enabled, and the flicker is still there but not nearly as noticeable. Using Dynamic GBC is still smoother, with lower CPU usage.

@Pixelmatix, great work! I’ve looked for APA102 matrix panels and they are quite expensive - any thoughts? Do you still plan on having a driver for the “dumb” RGB Matrix panels (eg. Adafruit 32x32)?

Wants - small prototyping area. This has been popular on the simple shield I created.

Solutions - a barrel jack on the board consumes a lot of space. I like the idea of powering the panel and having a pigtail/connector to the board. That keeps the connector footprint on the board very small (and cheaper).

Powering via USB is always tricky since LEDs can suck a lot of power if you’re not careful. Since the (above) power connector essentially connects to the Vin pin of the Photon, it could easily also act as a power output connector (when Photon is USB powered) for small LED strings. A jumper could be used to bypass a protection diode if one is used for power input.

I’ve noticed the display power-up issue as well, even on “dumb” panels. I’m not sure this is an consequence of simply applying power to the panel or as a consequence of the Photon power-up sequence. If the latter is the case, a tri-state level shifter could be disabled via a suitable pull-up or pull-down resistor and activated in code with an extra GPIO pin on the Photon.

Likes - The code which runs on a Photon will run on an Electron. I’ve recompiled the RGBPongClock (using the RGBMatrixPanel library) and it ran unmodified on the Electron. It even worked on my RGB Matrix shield (plugged into headers) with the extra pins remaining unconnected.

I haven’t tested on the Redbear Duo yet but that’s coming up this weekend.

I appreciate your great work and look forward to your continued development of this fantastic library! :wink:

5 Likes

Thanks @peekay123!

Do you still plan on having a driver for the "dumb" RGB Matrix panels (eg. Adafruit 32x32)?

Yes, I'll be using this APA102-focused library as a stepping stone to porting the full SmartMatrix Library with support for "Low cost Multiplexed RGB matrix displays" (for back of a better term) in the future.

Wants - small prototyping area

This is a tradeoff with the "Small Size" requirement. I can see how it would be useful though. Let's open it up for discussion and maybe take some votes in the future.

Good thoughts on power options. Is there any reason you'd want the non-barrel external power connector to output USB power, instead of just supplying USB power to the LEDs through their 4-pin connector?

I've noticed the display power-up issue as well, even on "dumb" panels

On the "dumb" panels (I guess that's easier to say than "Low cost Multiplexed RGB matrix displays"), a pull-up resistor on the OE line should fix that, at least to handle the case where that line is floating. SmartMatrix Shield V1 didn't have the pull-up, but I added it in V2 after getting blinded a few times while flashing firmware. :smile:

1 Like

@Pixelmatix, not sure what you mean by this. I was thinking about having the USB power being plugged into the Photon and routing the power via the Vin pin to the external LEDs. However, I think you are referring to the Blinky Tape which has its own USB connector. I was thinking about a short string of Neopixels/APA102s with a larger 2A USB adapter. The Photon's onboard protection diode between Vusb and Vin can handle up to 3A. :smiley:

All the APA102 strips and matrices I've sourced so far (three suppliers) have a JST-SM 4-pin male connector for input, JST-SM 4-pin female connector for output, and 2 loose wires for power and ground. On all the strips and matrices I have, the two loose power wires are connected internally to both the input and output.

(I added the barrel jack to the factory-unconnected power wires)

To my knowledge there's no board-mount version of the JST-SM connector, so the next best thing is probably to put pads with the same pinout on the board, or a terminal block, so a matching cable can be connected. Does this sound good?

For the case where we're getting power from USB, if USB power was routed to the 4-pin connector, it could power the strip or matrix through the 4-pin connector. I don't see any reason why we'd want or need to connect up the loose power wires to the shield. Do you? If you know of any strips/matrices that separate the power wires, that totally changes things.

Keeping the external power supply connection (let's call it Vext) to input only simplifies the design. I believe we could just have a diode OR between Vusb and Vext, and the Vusb diode is already on the Photon.

I was thinking about a short string of Neopixels/APA102s with a larger 2A USB adapter. The Photon's onboard protection diode between Vusb and Vin can handle up to 3A.

Yes, if that can be done in a safe and reliable way, I'm all for it. I'm glad Particle put such a large diode on the Photon, vs the Teensy which has a 500mA fuse on Vusb and no way to bypass it.

1 Like

Here’s another shield for reference: minimal footprint, but lots of features. The “Prop Shield” has 5V drivers for the APA102 lines, 8MB Flash (but no microSD holder), and some sensors and an audio driver which seem out of scope for our design.

Regarding power, the Prop Shield directly connects two 5V pins (one on the APA102 side, one on the speaker side) with the Teensy’s Vin pin. Vin is separated from Vusb by a diode and fuse. (schematic)

The Teensy 3.2 only has one SPI port, so it’s shared with both the APA102s and the SPI Flash. Paul probably has a SPI Flash library that makes it easy to switch between peripherals. On the Photon I’m planning to use SPI1 for the APA102s, and SPI(0) for the microSD card.

Pros:

  • Small Size
  • Cost (wow, hard to compete)
  • SPI Flash (8MB might be enough memory for many use cases)

Cons:

  • No microSD for memory expansion beyond 8MB

  • Requires soldering to add to Teensy and LEDs

  • Only option for removing Teensy is soldering a tall socket to each side of the shield

  • Only option for expansion is tall header pins

1 Like

@Pixelmatix, SPI flash vs microSD is a no-brainer I believe, especially with animated GIFs and images. Working with a microSD is also a lot simpler. Having a removable Photon like the internet button is a big plus though it does add thickness. Soldering parts has not proven to be an issue for folks who got my board. However, surface mount does reduce cost and makes for a nicer unboxing experience.

I agree with the JST-SM connector being common. A terminal block is a good idea and if left off, could serve as pads for connecting JST-SM wires to the board.

Hi Louis,

Thanks for the complete explanation. I was wondering where you write to the APA102 led. I suppose here: https://github.com/pixelmatix/SmartMatrix-Photon-APA102/blob/master/firmware/SmartMatrix_Impl.h#L427

However I have to use the Smartmatrix format isn’t it? Now I’m using FastLED and I was wondering if I could simple write out my current array, directly bypassing the APA102 write out of FastLED.

I’m still learning C++ and I was wondering what this part of the language means: SmartMatrix3<refreshDepth, matrixWidth, matrixHeight, panelType, optionFlags>::matrixUpdateData probably a function call (since :: is part of a class), but what is the < > notation instead of normal parenthesis.

Hi Kasper,

Yes, that’s where the DMA transfer starts.

Can you share a simple example FastLED sketch, and I can tell you how to change it to use SmartMatrix?

SmartMatrix as of 3.0 uses templates, so you can set the size of buffers at compile time without using malloc, switch between using rgb24 and rgb48 at compile time (and for other reasons). I was lucky to have someone contribute a lot of the template code, so I didn’t have to figure it out from scratch.

Thanks. I have a sketch on gist to test Photon APA102 update speeds with FastLED. I’m curious how it goes with SmartMatrix: https://gist.github.com/kasperkamperman/90e6a4f84e5eb2b84b64

@Pixelmatix, I just locally compiled the RGBPongClock code (for Photon) for the Duo and replaced the Photon on an RGB Matrix shield I have connected to an Adafruit 32x32 panel. It ran exactly the same as the Photon :wink:

3 Likes

I ported your sketch. I’m not sure it was working properly for me even from your original code. I get 256 LEDs turned off when I start the sketch, and after a minute or so, they all turn blue. It looks like they should be cycling through combinations of red and blue, but that’s not what I’m seeing. Not sure why.

The “update time” printed out over Serial is much slower for SmartMatrix, but that’s expected as it’s currently using a 1MHz clock.

Here’s the sketch with SmartMatrix modifications.

If you’re compiling this with Web Build, make sure you start out with a new sketch, and import only the FastLEDSmartMatrix library, not FastLED, as FastLED 3.0 has conflicts with SmartMatrix 3.0. This is fixed in FastLED 3.1 which isn’t ported to the Photon yet. More details in the SmartMatrix Library README.

This document doesn’t fully apply to the SmartMatrix APA102 port, but you can see in general how to update a Sketch using a FastLED controller to use SmartMatrix to drive the LEDs, but still fill the buffer with data from FastLED:

Hi Louis,

Thanks. I’ll try it out.

It looks like they should be cycling through combinations of red and blue, but that's not what I'm seeing. Not sure why.

The for-loop has to be placed outside the if-statement. My mistake, I've changed it in my original code.

I've tried your code (I had to change SPI1 to SPI otherwise I had to re-solder my setup) and that works. However I seem to notice some steps, probably when you switch the global brightness of the led.

I use dithering based on an array (GitHub - raplin/HexaWS2811: 16 Channel (optionally) plus gamma correct & dither version of PJRC's OctoWS2811 library for Teensy - very hacked about messy right now. You probably want to use a Teensy3.1 for this due to ram usage) because I want smooth fades of individual leds (FastLED only dithers global brightness), so probably your way of adding "bit-depth" doesn't work for me (although it could on global brightness).

Another question. I turned of swapBuffers, because I probably don't need it (I didn't notice any visual change):

setTime();
    backgroundLayer.swapBuffers(false);
  printTime();

Then I measure an update speed of 1us. Which is like amazingly fast. Is that because of the DMA? So it sends the buffer data in the background to the LEDs? Or is there some other magic happening?

I'm really interested in fast updates (because of my dithering). I tried the ported Adafruit Dotstar library, which also uses hardware SPI transfer. I measure with hardware SPI a speed of around 822us. Am I right that that one doesn't use DMA? Because bitbanging FastLED is around 600us.

The only difference I noticed is that the Adafruit library calls SPI.transfer with one parameter ( https://github.com/technobly/Particle-DotStar/blob/master/firmware/dotstar.cpp#L51 )
while you call SPI.transfer with four parameters ( https://github.com/pixelmatix/SmartMatrix-Photon-APA102/blob/master/firmware/SmartMatrix_Impl.h#L427 ).

In there reference four parameters are mentioned: https://docs.particle.io/reference/firmware/photon/#transfer-void-em-void-em-size_t-std-function-

Although SmartMatrix will do for me, I prefer to use the barebones code of the Adafruit, just for my understanding what happens under the hood. So can I enable that one to use DMA? Would it be as simple as changing the SPI.transfer function call?

Sorry if I went slightly off topic, so if you want me to open a new one I'll do.

Hi Louis,

I've modified the DotStar library so I could use the DMA based SPI transfer:

I opened another topic for an issue I have with this:

Thank you for making me aware of the DMA support of the Photon. I've learned a lot from your code.

I’m glad you got DMA working. I’m interested in the dithering technique you’re using, and I want to give your sketch another try with the fix and see the stepping, but I’ve been away from my laptop for a bit. I’ll check it out Sunday hopefully.

1 Like