Update August 9 2012:
I finally had the time to completely rewrite the documentation. You can now find schematics for normal RGB LED's, LED strips and high power LED's on www.elcojacobs.com/shiftpwm
I also updated the library to include load balancing and made the initialization much cleaner. Switching between SPI and no-SPI has become a lot easier. And I added Arduino Leonardo compatibility.
Update May 9 2012:
Support for NOT using the SPI pin. 2.5x slower, but the freedom to use your SPI port for something else.
Download here: https://github.com/elcojacobs/ShiftPWM/downloads
Extract files to libraries/ShiftPWM/ and open one of the examples.
Update May 8 2012:
Led setup flexibility: http://arduino.cc/forum/index.php/topic,66988.msg786810.html#msg786810
Based on some great suggestions that I got in this thread I have updated ShiftPWM to use the SPI bus.
That took down the number of clock cycles per shift register from 108 to 43.
I have created a page for ShiftPWM on my website www.elcojacobs.com/shiftpwm
where you can find more info and download the newest version.
Even Older stuff:
First some pictures to get your attention:
I study Electrical Engineering, but have a job for 8 hours a week at the Electronics Atelier of the Industrial Design department of the Technical University of Eindhoven. My job is to help the ID students with the electronics in their projects. A request I often get is to control many, many LED's. TLC5940 were often used, but they are very expensive compared to shift registers (74HC595).
For shift registers you have to write the PWM code in software and drive the control lines of the registers fast enough.
Especially doing it fast enough is difficult, but I think I have reached the optimum at 13 clock cycles (worst case) to update each pin. This drives the clock line of the registers at 1.33 MHz.
The interrupt function is optimized to be as fast as possible, but the other functions to set duty cycles and configure the library are as easy to use as possible.
I put the library in a class as much as possible without slowing down the code too much. There are many tricks needed to make the library easy to use and configurable, but still as fast as possible. I have written a lot of commentary in the source files to explain these tricks, but I might write a blog post on writing fast Arduino code if there is much demand.
Features of the library:
- Control the duty cycle of many PWM outputs in a single shift register chain.
- Easily configure frequency, number of registers and number of brightness levels.
- Outputs can be inverted for common anode RGB LED's
- Function to print information on timer settings and interrupt load
- Switches to timer2 if timer1 is in use by the servo library
- Checks for input to functions that is out of range and prints error messages to the serial port
- Can be placed in the libraries directory of Arduino
- Includes an example (see video) which can be accessed by file->examples
- Example includes HSV to RGB function for easy color shifting with RGB LED's.
- The load of the interrupt function on your program can be calculated as follows:
* L = Interrupt frequency * interrupt duration / clock frequency
* L = F*(Bmax+1)*(96+108*N)/F_CPU
* Quick reference for load:
* 3 registers 255 maxBrightness 75Hz load = 0.50
* 6 registers 100 maxBrightness 75Hz load = 0.35
* 24 registers 50 maxBrightness 75Hz load = 0.64
* 48 registers 32 maxBrightness 75Hz load = 0.81
* 96 registers 16 maxBrightness 75Hz load = 0.83
* ShiftPWM.Start(int ledFrequency, int max_Brightness) Enable ShiftPWM with desired frequency and brightness levels
* ShiftPWM.SetAmountOfRegisters(int newAmount) Set or change the amount of output registers. Can be changed at runtime.
* ShiftPWM.PrintInterruptLoad() Print information on timer usage, frequencies and interrupt load
* ShiftPWM.OneByOne() Fade in and fade out all outputs slowly
* ShiftPWM.OneByOneFast() Fade in and fade out all outputs fast
* ShiftPWM.SetOne(int pin, unsigned char value) Set the duty cycle of one output
* ShiftPWM.SetAll(unsigned char value) Set all outputs to the same duty cycle
* ShiftPWM.SetGroupOf2(int group, unsigned char v0, unsigned char v1);
ShiftPWM.SetGroupOf3(int group, unsigned char v0, unsigned char v1, unsigned char v2);
ShiftPWM.SetGroupOf4(int group, unsigned char v0, unsigned char v1, unsigned char v2, unsigned char v3);
ShiftPWM.SetGroupOf5(int group, unsigned char v0, unsigned char v1, unsigned char v2, unsigned char v3, unsigned char v4);
--> Set a group of outputs to the given values. SetGroupOf3 is useful for RGB LED's. Each LED will be a group.
** Very technical stuff alert **
I have optimized the C code to compile to the most efficient instructions available. The interrupt function loops over all shift registers and for each output executes the following instructions:
- Write clock output low (cbi, 2 clock cycles)
- Load the duty cycle for this pin from memory (ldd, 2 clock cycles)
- Compare the duty cycle with a counter (cp, 2 clock cycles)
- Branch on compare result (brcs, 2 clock cycles)
- Write the datapin high or low (cbi or sbi, 2 clock cycles)
- Jump instruction (1 clock cycle, not always executed)
- Write clock output high (sbi, 2 clock cycles)
This takes 12 or 13 clockcyles per output and I don't think it can be done any faster.
** End technical stuff **
So unzip the attachment in your Arduino library directory and try it. The circuit is exactly the same as the ShiftOut example, but leave out the latch pin capacitor.
This library is not tested with the atmega1280, if you use it please report back to me.
I would really appreciate some feedback on usability and possible bugs. If someone could test this with ShiftBrites would be great.
If you would like to include this library as a standard library for Arduino, I would be honored.
Video of ShiftPWM_example1: