Pages: [1]   Go Down
Author Topic: A really fast software PWM library  (Read 2712 times)
0 Members and 1 Guest are viewing this topic.
Taipei, Taiwan
Offline Offline
Full Member
***
Karma: 1
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Well it's the old story: I was interfacing multiple RGB LEDs, and there's not enough built-in PWM pins.

There're libraries like ShiftPWM (http://www.elcojacobs.com/shiftpwm/) and SoftPWM (http://code.google.com/p/rogue-code/) which does software PWM, but they're just not ehough for me.

ShiftPWM is fast and elegant, but requires extra hardwares (the shift registers), that's around USD $0.6 each from the local retail electronics store.
But I just want 16 outputs and there are 20 pins on atmega328p (+2 more if the internal RC is used, +1 more if the reset pin is disabled), why should I spend 2 extra 74*595?

The SoftPWM library has some other problem, it's slow (high CPU usage), fat (high memory usage), and hard to configure (such as brightness levels, PWM frequencies, etc.).

So I wrote this library - just like ShiftPWM - to do PWM for all pins in background (timer interrupt).
It produces REALLY FAST code - 6 instructions per output in the ISR routine, and only use 1 byte per pin + 1 byte for the counter.
However, currently it only knows about timer1, and doesn't know about arduino pins (has to tell it the port register and bit number).

It is capable to drives 16 PWM outputs with 256 brightness levels up to 200hz on a 8Mhz ATmega328p (but this way you have almost no time loop()-ing, so you might want to use something lower like 120hz).

the code is hosted on github:
https://github.com/Palatis/arduino-softpwm
Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 538
Posts: 27089
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

External hardware might be advisable too.
TLC5940 from ti.com. 16 channels.
WS2803 from world semi, 18 channels, but apparently only available via e-bay.  I just received a tube of 25, have not tried them out yet.

* WS2803-preliminary-En.pdf (436.67 KB - downloaded 22 times.)
* tlc5940.pdf (1084.92 KB - downloaded 24 times.)
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Taipei, Taiwan
Offline Offline
Full Member
***
Karma: 1
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

External hardware might be advisable too.
TLC5940 from ti.com. 16 channels.
WS2803 from world semi, 18 channels, but apparently only available via e-bay.  I just received a tube of 25, have not tried them out yet.
they're expansive.

TLC5940 is over USD $6 from local retail store, no idea about WS2803.

together with 2 ULN2803 ($0.6 each), i can drive 16 PWMs (350mA each, 2A per one ULN2803).

i'm thinking about to disable the reset pin (so it can be used as an IO pin) and enable Internal RC (free the 2 xtal pin, too) to get 23 digital IO, with 2 pins for TWI, it can drive 7 RGB LED with the rest 21 pins (and the chip can be powered with only 2.7V!).
or... maybe I should just get an ATtiny48 to do this? :-P
Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 538
Posts: 27089
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'd go the other way smiley-cool ATMega1284 with 32 IO!
Drive 10 RGBs, leave serial, reset, xtal pins free still.
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Taipei, Taiwan
Offline Offline
Full Member
***
Karma: 1
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'd go the other way smiley-cool ATMega1284 with 32 IO!
Drive 10 RGBs, leave serial, reset, xtal pins free still.
the matters is the price.

with the price of 1 ATmega1284, u can get 2 ATmega48 or 3 ATtiny48.
Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 538
Posts: 27089
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Depends what you want to do with them too:
ATTiny48
Core Size- Speed- Connectivity- Number of I /O- Program Memory Size- Program Memory Type- EEPROM Size- RAM Size- Data Converters
8-Bit        12MHz  I²C, SPI          24                    4KB (2K x 16)              FLASH                        64 x 8            256 x 8     A/D 6x10b
Other:
Brown-out Detect/Reset, POR, WDT

ATMega1284P
8-Bit        20MHz  I²C, SPI,         32                     128KB (64K x 16)          FLASH                      4K x 8             16K x 8    A/D 8x10b
                         Dual UART/USART
Other:
Brown-out Detect/Reset, POR, PWM, WDT

The limited FLASH and RAM,and lack of a UART, would be a killer for most everything I've done with 328's so far.
If you have a smaller program, and '48 fits your needs, go for it.
Lots of variations out there to meet everyone's needs.
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Colorado
Offline Offline
Edison Member
*
Karma: 47
Posts: 1562
Reviving dead brain cells with Arduinos.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

External hardware might be advisable too.
TLC5940 from ti.com. 16 channels.
WS2803 from world semi, 18 channels, but apparently only available via e-bay.  I just received a tube of 25, have not tried them out yet.


Meh, I get my 2803s directly from Shenzhen.  But then, I also order them in large quantities (though not recently ... haven't had a project needing a crap load of LEDs in recent months.)  Same with 2801s - 15 cents a pop.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 35
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Palatis (or anyone who wants to help),

I looked at the other two libraries you mentioned before finding yours, and I think yours is better (more efficient and uses TIMER1 instead of TIMER2, which I'm clocking asynchronously for an RTC).

But! I have a problem.

I'm making a modified version of Daniel Andrade's binary clock. What he does is strip the HH:MM into its separate digits, and decide whether each LED turns on or not depending on what the digit is. An exemplary line:
Code:
if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {digitalWrite(3, HIGH);} else {digitalWrite(3, LOW);}
What I'm doing is using your PWM to allow me to dim the LEDs, like so:
Code:
if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {SoftPWM.set(3, brightLevel);} else {digitalWrite(3, LOW);} //brightLevel is the divided analogRead of a pot
But when I do this, the LEDs that should be off stay on, and don't respond to the pot—as if they've been written HIGH! "Huh," I think to myself. "Must be something funny about his code. I'll just do this, then:"
Code:
if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {SoftPWM.set(3, brightLevel);} else {softPWM.set(3, 0);}
And when I do that, sure enough, the LEDs that should be off stay off. But the window for brightLevel shrinks almost completely: the LEDs are full-on for most of the dial, then near-instantly switch to full-off. There are only a few degrees in which they're actually dimmed.

Any ideas as to what's going on, and how I can fix it?

Thanks!
Logged

Offline Offline
Newbie
*
Karma: 4
Posts: 37
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Randomizer,

I've used this most excellent SoftPWM library successfully for a few projects, I too found it very fast and efficient compared to the others you and the OP mention...

I think you may need to incorporate LED gamma correction in your code as humans don't percieve PWM'ed brightness linearly. Simply put, we discern the difference in LED brighness when we set the PWM from 0  to 5 much more than when we set the PWM from 250-255.

Using a lookup table to convert linear PWM values to exp gamma corrected values is a good solution.

For example:

//------------add this include at the top of your program---------------
#include <avr/pgmspace.h> // enables you to store the gamma table into flash memory instead of Static Ram.

uint8_t const exp_gamma[256] PROGMEM=
{0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3,3,
4,4,4,4,4,5,5,5,5,5,6,6,6,7,7,7,7,8,8,8,9,9,9,10,10,10,11,11,12,12,12,13,13,14,14,14,15,15,
16,16,17,17,18,18,19,19,20,20,21,21,22,23,23,24,24,25,26,26,27,28,28,29,30,30,31,32,32,33,
34,35,35,36,37,38,39,39,40,41,42,43,44,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,
61,62,63,64,65,66,67,68,70,71,72,73,74,75,77,78,79,80,82,83,84,85,87,89,91,92,93,95,96,98,
99,100,101,102,105,106,108,109,111,112,114,115,117,118,120,121,123,125,126,128,130,131,133,
135,136,138,140,142,143,145,147,149,151,152,154,156,158,160,162,164,165,167,169,171,173,175,
177,179,181,183,185,187,190,192,194,196,198,200,202,204,207,209,211,213,216,218,220,222,225,
227,229,232,234,236,239,241,244,246,249,251,253,254,255
}; // end gamma correction LUT
//-------------------------------------------------------------------------------


//----------- add this line before your SoftPWM.set call----------------------------------------

brightLevel=pgm_read_byte_near(exp_gamma+brightLevel);  // gamma transform

//--------------------------------------------------------------------------------------------------------


Logged

FRANCE
Offline Offline
Sr. Member
****
Karma: 0
Posts: 338
Arduino rocks .... oulalalalala
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

hello, i m trying this lib but PullUp keyword seems no to be valid ?
does the actual lib works with arduino 023 or 1.0 ?
Logged

Valencia, Spain
Offline Offline
Faraday Member
**
Karma: 146
Posts: 5507
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

they're expansive.

TLC5940 is over USD $6 from local retail store

About $1 on eBay: http://www.ebay.com/sch/i.html?_nkw=tlc5940
Logged

No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

Offline Offline
Newbie
*
Karma: 0
Posts: 1
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This is a great library, very easy and quick. I was wondering, however, if anyone can tell me if doing SoftPWM.set( i, 0 ) and SoftPWM.set( i, SoftPWM.brightnessLevels() -1 ) is the same as setting that PIN to LOW and HIGH, or is it still doing some PWM (I wish I had a scope...) ?
Logged

Taipei, Taiwan
Offline Offline
Full Member
***
Karma: 1
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Palatis (or anyone who wants to help),

I looked at the other two libraries you mentioned before finding yours, and I think yours is better (more efficient and uses TIMER1 instead of TIMER2, which I'm clocking asynchronously for an RTC).

But! I have a problem.

I'm making a modified version of Daniel Andrade's binary clock. What he does is strip the HH:MM into its separate digits, and decide whether each LED turns on or not depending on what the digit is. An exemplary line:
Code:
if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {digitalWrite(3, HIGH);} else {digitalWrite(3, LOW);}
What I'm doing is using your PWM to allow me to dim the LEDs, like so:
Code:
if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {SoftPWM.set(3, brightLevel);} else {digitalWrite(3, LOW);} //brightLevel is the divided analogRead of a pot
But when I do this, the LEDs that should be off stay on, and don't respond to the pot—as if they've been written HIGH! "Huh," I think to myself. "Must be something funny about his code. I'll just do this, then:"
Code:
if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {SoftPWM.set(3, brightLevel);} else {softPWM.set(3, 0);}
And when I do that, sure enough, the LEDs that should be off stay off. But the window for brightLevel shrinks almost completely: the LEDs are full-on for most of the dial, then near-instantly switch to full-off. There are only a few degrees in which they're actually dimmed.

Any ideas as to what's going on, and how I can fix it?

Thanks!

by telling the library to manage the pin:
Code:
SOFTPWM_DEFINE_CHANNEL( 6, DDRD, PORTD, PORTD6 );
it manages the pin forever.
there's no way it can tell if it's been digitalWrite()-ed. (digitalWrite() doesn't tell the lib that this pin should stay LOW).

so you have to write
Code:
SoftPWM.set(pin, 0);

as for the brightness problem you had, humen eyes are not linear to brightness level, apply gamma correction as other post mensioned.
Logged

Taipei, Taiwan
Offline Offline
Full Member
***
Karma: 1
Posts: 101
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This is a great library, very easy and quick. I was wondering, however, if anyone can tell me if doing SoftPWM.set( i, 0 ) and SoftPWM.set( i, SoftPWM.brightnessLevels() -1 ) is the same as setting that PIN to LOW and HIGH, or is it still doing some PWM (I wish I had a scope...) ?

the pin is still managed by SoftPWM even when you do
Code:
SoftPWM.set(pin, 0);
SoftPWM.set(pin, SoftPWM.brightnessLevels() - 1);

the codes update the counter and update pin states according to the counter on each timer interrupt.
it will be inefficient to check if a pin should be managed or not.

but by setting a pin to 0 or brightnessLevels() - 1, it stays LOW or HIGH because the cbi and sbi won't modify pin state.
for example, if it was LOW, after applying cbi it's still LOW; if it was HIGH, after applying sbi it's still HIGH.
Logged

Pages: [1]   Go Up
Jump to: