TLC5940 Updated Library for Teensy 4.0: Multiplexing and Temporal Dithering

stereodan

Active member
I wanted to share this library that takes previous work by Alex Leone and Paul's updates to add Teensy compatibility, and adds functions such as 3-column multiplexing and temporal dithering.

View attachment TLC5940-M3.zip

I won't say it improves it, because it won't be ideal for every project. But if you're driving RGB LEDs, it offers some advantages.

It multiplies the number of outputs of each chip by 3, saving you board space and TLC chips and making it ideal for driving RGBs;

And the dithering adds an additional 2 bits of depth, from 4,096 levels to 16,385.

You might ask, why do you need this? Well, at very low intensity levels, Depending on the physical configuration of your LEDs, you can easily see the difference in illumination as you step from 1 to 2, 2 to 3 and so on, and especially with RGB LEDs because you are actually stepping from 3 to 6 to 9, etc. when all three are illuminated. Dithering adds four steps of apparent illumination for each integer value. And you achieve higher color depth too—this means if you are driving RGB LEDs you go from 68 billion colors to close to a theoretical 4 trillion (!)

Caveats
* It only works on Teensy 4.0 (and 4.1 I assume, but haven't tested). I haven't figured out the 3.x timing and interrupts but it doesn't really have the processor overhead necessary for this anyway.
* No hardware dot correction. It's simply incompatible with this methodology. But the same thing can be achieved in your code with an array of constants applied to the output levels. This means it works with the cheaper and more readily available TLC5941 as well.
* Tlc.get(), Tlc.blank(), Tlc.setAll() are not implemented either. But who really needs those?

Hardware configuration
* Pin 8 and Pin 2 on the Teensy 4.0 must be shorted (there is an attachInterrupt() that needs this).
* Your high-side multiplexing columns should be connected to pins 14, 15, and 16. This is customizable. I recommend the ol' NFET switching a PFET method if you need to source 12V to your LEDs.

Usage
* The library creates a 2nd and 3rd set of "virtual pins" to address all the multiplexed LEDs. So if you had two chips installed with 32 physical outputs, there are now 96 addressable virtual outputs total. The first multiplexed column are channels 0-31, the second column are 32-63, and the third are 64-95. Be sure to continue to set the number of physical chips in your circuit correctly in NUM_TLCS in the config file.
* Tlc.update() is no longer used. Tlc.set(output channel, level) loads the value directly into grayscale data and the chip's registers update every blank cycle. To change an LED's value, call Tlc.set(output, level) and the output changes immediately.
* When calling Tlc.set(output, level), the level is still from 0 to 4095 but is now a float value, and with each .25 increase, adds a step of dithering. So temporal dithering will occur anytime you feed it a value in-between two integers by an increment of .25 or more. For example, a value of 10.25 will mean that for every four blank cycles, 3 will be at value 10, and one will be at value 11, resulting in a perceived illumination of 10.25. The input can be any value and does not have to be a factor of .25, the function will convert it to the nearest dither.

To handle color conversion, I recommend this library by Tom Igoe that I've modified to use float values and handle intensity from 0-4095.

View attachment ColorConverter.zip

Example:
Code:
#include <Tlc5940.h>
#include <ColorConverter.h>

//Number of TLC chips: 2 = 96 outputs total.

void setup() {
Tlc.init();
}

void loop() {
Tlc.set(42, 14.5);     //Sets output 10 on 2nd multiplexing column to 14, with an additional 2 out of 4 steps of dither
Tlc.set(10, 100.75); //Sets output 10 on 1st multiplexing column to 100, with an additional 3 out of 4 steps of dither

//Or we can set an RGB LED using hue, saturation, and intensity:
float h = 130; //set hue, 0-360
float s = 50; //set saturation, 0-100
float i = 2954.2; //set intensity, 0-4095

RGBColor color = ColorConverter.HSItoRGB(h, s, i);
  Tlc.set(0, color.red);    //red LED
  Tlc.set(1, color.green); //green LED
  Tlc.set(2, color.blue);   //blue LED
}

The BLANK pin timing has been modified to remain high for as much as 1500 CPU cycles. This is well more than enough to cover the ghosting that occurs when switching multiplexing columns. There is also a lot of wiring in my projects which creates an inductive load that adds to the ghosting issue, which is why this value is high. You can adjust this value in the timing section of Tlc5940.cpp to your liking if you need more or prefer less, but in the scheme of things, it's a very small fraction of the overall period, and this longer BLANK cycle really doesn't affect the overall illumination to our eyes, so you don't have to go crazy optimizing it.

This library is really hacky and is provided with no guarantees or support, you're on your own. Good luck everyone.
 
Last edited:
Back
Top