modifying SoftPWM for attiny

Hello,

I would like to be able to use an RGB LED with my attiny45, but it only has 2 PWM ports. I found this SoftwarePWM library http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1268806013.

I tried to compile with the arduino tiny and attiny45 @1Mhz but it didn't work. The library uses timer2.

Is there any way to modify this library for use with attiny45 or another working library?

lyron:
Hello,

Hello to you.

I would like to be able to use an RGB LED with my attiny45, but it only has 2 PWM ports.

It has three. (technically it has four but two overlap) I suggest figuring out why the third one is not working rather than trying to port SoftwarePWM.

I saw on the datasheet that it had 2 channel RGB so i tought 2 pins. Now if i try to fade digital pin 3 it works just fine but with all 3 together the one connected to pd3 just flickers.

So is there any working software PWM library that's easy to use with attiny45? i only need 1 software PWM port, there are 2 built in harware PWM's in attiny45

Post your sketch and a link to the core you are using.

I'm using the arduino tiny core (Google Code Archive - Long-term storage for Google Code Project Hosting.)

And this is my code:

 #include <SoftwareSerial.h>

 int txPin = 2;

 SoftwareSerial sSerial =  SoftwareSerial(36, txPin);

 int button = 3;
//Setup the LEDs

// LED outputs
 int blueLED = 4;
 int redLED = 0;
 int greenLED = 1;
 int nullLED = 16; // dummy does nothing on this processor attiny45
 
 void setup(){
 pinMode(txPin, OUTPUT);
 pinMode(blueLED, OUTPUT);
 pinMode(greenLED, OUTPUT);
 pinMode(redLED, OUTPUT);
 pinMode(button, INPUT);
   
 sSerial.begin(9600);     
 }
 
 void loop(){
 rainbow();  
 }
 
 void red(){
 sSerial.print(1);
 digitalWrite(redLED, HIGH); 
 }
 
 void green(){
 sSerial.print(2);
 digitalWrite(greenLED, HIGH);   
 }
 
 void blue(){
 sSerial.print(3);
 digitalWrite(blueLED, HIGH);   
 }
 
 void none(){
 sSerial.print(0);
 digitalWrite(redLED, LOW);  
 digitalWrite(greenLED, LOW);  
 digitalWrite(blueLED, LOW);  
 }
 
 void yellow(){ //orange-ish
 sSerial.print(4);
 analogWrite(redLED, 240); //255
 analogWrite(greenLED, 193);
 analogWrite(blueLED, 37); 
 }
 
 void rainbow(){
 sSerial.print(5);  
 for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=5) { 
      analogWrite(redLED, fadeValue);
      delay(30);    
    } 

    for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=5) { 
      analogWrite(redLED, fadeValue); 
      delay(30);
    }
    
    for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=5) { 
      analogWrite(greenLED, fadeValue);
      delay(30);    
    } 
    
    for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=5) { 
      analogWrite(greenLED, fadeValue); 
      delay(30);
    }
    for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=5) { 
      analogWrite(blueLED, fadeValue);
      delay(30);    
    } 
    
    for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=5) { 
      analogWrite(blueLED, fadeValue); 
      delay(30);
    }
    for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=5) { 
      analogWrite(blueLED, fadeValue);
      analogWrite(redLED, fadeValue);
      analogWrite(greenLED, fadeValue);
      delay(30);    
    } 
    
    for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=5) { 
      analogWrite(blueLED, fadeValue);
      analogWrite(redLED, fadeValue);
      analogWrite(greenLED, fadeValue);
      delay(30);
    }
 }
 
    
    
    void violet(){
    sSerial.print(6);
    analogWrite(redLED, 148);
    analogWrite(greenLED, 0);
    analogWrite(blueLED, 211); 
    }

So the colors fading one by one works fine, but when they fade together the blue one (witch is not connected to a hardware PWM pin) flickers.

lyron:
SoftwareSerial sSerial = SoftwareSerial( 36, txPin );

Do NOT try to access invalid pins. The Tiny Core (like the Arduino Core) DOES NOT VALIDATE PIN NUMBERS



The core you are using includes "Tiny Debug Serial". I strongly suggest using Tiny Debug Serial instead of SoftwareSerial. There are some details available here...
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1285218245/25#25

Oops, typo. I will try to use the Tiny Debug Serial. But i want other attiny45's to receive the message...

I'm looking to use this on the ATTINY2313 to PWM all the outputs. I created a new "TinySoftPWM.c/h" library and changed the failing references in blind hope thus

  TIFR = (1 << TOV0);          // clear interrupt flag
  TIMSK = (1 << OCIE0A);       // enable timer0 output compare match interrupt
  TCCR0B = (1 << CS01);         // start timer (ck/8 prescalar)
  TCCR0A = (1 << WGM01);        // CTC mode
  OCR0A = SOFTPWM_OCR;

And also changed ISR(TIMER2_COMPA_vect) to ISR(TIMER0_COMPA_vect).

The code now compiles, but behaves oddly once uploaded (TinyISP). I'm running using the internal 8MHz osc so would expect it to be a bit slower, but I seem to be getting some rather random blinking running the SoftPWM blink sketch.
No idea what to try next....

Which core are you using?

I'm using the Aurduino-Tiny 2313 core.

Made a bit of progress... First realised it was still running at 1Mhz. Burnt the 8MHz bootloader to set the fuses and verified correct speed with the standard blink.

Went back to the SoftPWM blink and it was flickering fast now rather than random blinking. I've since discovered that setting a very long delay between the on and off provides correct functionality i.e.

void loop()
{
  SoftPWMSet(5, 255);
  delay(50000);
  SoftPWMSet(5, 0);
  delay(50000);
}

From this I'm concluding that the timer the SoftPWM is now using is also the one the delay() function uses and that has now been changed such that the delay() function no longer provides a delay in ms. I can live with that and just make adjustments in my code. Are there any other side effects I should be aware of?

Timer 0 is used for millis (delay). The 2313 has a second timer (timer 1) that is free for you to use.

Hi,

I'm working on a project with the ATtiny84 (same cores mentioned above) and the same SoftPWM library.
I tried to change the library according to this:

I'm looking to use this on the ATTINY2313 to PWM all the outputs. I created a new "TinySoftPWM.c/h" library and changed the failing references in blind hope thus

Code:

TIFR = (1 << TOV0);          // clear interrupt flag
 TIMSK = (1 << OCIE0A);       // enable timer0 output compare match interrupt
 TCCR0B = (1 << CS01);         // start timer (ck/8 prescalar)
 TCCR0A = (1 << WGM01);        // CTC mode
 OCR0A = SOFTPWM_OCR;



And also changed ISR(TIMER2_COMPA_vect) to ISR(TIMER0_COMPA_vect).

The original code in 'SoftPWM_timer.h' was:

#if defined(USE_TIMER2)
#define SOFTPWM_TIMER_INTERRUPT    TIMER2_COMPA_vect
#define SOFTPWM_TIMER_SET(val)     (TCNT2 = (val))
#define SOFTPWM_TIMER_INIT(ocr) ({\
  TIFR2 = (1 << TOV2);    /* clear interrupt flag */ \
  TCCR2B = (1 << CS21);   /* start timer (ck/8 prescalar) */ \
  TCCR2A = (1 << WGM21);  /* CTC mode */ \
  OCR2A = (ocr);          /* We want to have at least 30Hz or else it gets choppy */ \
  TIMSK2 = (1 << OCIE2A); /* enable timer2 output compare match interrupt */ \
})

And I changed it to:

#if defined(USE_TIMER2)
#define SOFTPWM_TIMER_INTERRUPT    TIMER2_COMPA_vect
#define SOFTPWM_TIMER_SET(val)     (TCNT0 = (val))
#define SOFTPWM_TIMER_INIT(ocr) ({\
  TIFR0 = (1 << TOV0);    /* clear interrupt flag */ \
  TCCR0B = (1 << CS01);   /* start timer (ck/8 prescalar) */ \
  TCCR0A = (1 << WGM01);  /* CTC mode */ \
  OCR0A = SOFTPWM_OCR;          /* We want to have at least 30Hz or else it gets choppy */ \
  TIMSK0 = (1 << OCIE0A); /* enable timer2 output compare match interrupt */ \
})

It compiles and uploads but just doesn't work. It doesn't work when uploaded to my Arduino either so I don't know? It would really be great if someone could help me get this working.

P.S> These are the original compile errors I got before changing the library:

C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp: In function 'void SoftPWMBegin(uint8_t)':
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:156: error: 'TIFR2' was not declared in this scope
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:156: error: 'TOV2' was not declared in this scope
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:156: error: 'TCCR2B' was not declared in this scope
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:156: error: 'CS21' was not declared in this scope
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:156: error: 'TCCR2A' was not declared in this scope
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:156: error: 'WGM21' was not declared in this scope
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:156: error: 'OCR2A' was not declared in this scope
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:156: error: 'TIMSK2' was not declared in this scope
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:156: error: 'OCIE2A' was not declared in this scope
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp: In function 'void SoftPWMSet(int8_t, uint8_t, uint8_t)':
C:\Users\Mitchell\Documents\Arduino\libraries\SoftPWM\SoftPWM.cpp:205: error: 'TCNT2' was not declared in this scope

I managed to get it working on the 2313, and also managed to fix some bugs that prevented output 0 being used as PWM (these have now been fixed in the latest Arduino 1.0 version of SoftPWM I noticed the other day)

Can't remember what I did now - I'll have to re-familiarise myself with teh code again....

I did find that the structures consume a huge amount of RAM - I could only get 8 outputs PWMing and more and really strange things start happening....

Marvin-
I, too, am trying to get PWM on a bunch of pins on a 2313. I've got the code compiling, but would love to find out what you did to make your's work. Did you ever get a chance to look at your code again and, if so, could you post it? thx

Never mind - got it. I changed the timers used in .cpp

Sorry for not getting back - I need to pick this all up again soon as I'm now trying to modify it for the Tiny85.

Glad you got it working but keep an eye out if you are trying to control a lot of outputs as the 2313 runs out of RAM before you run out of pins!

Hi,

Sorry if it's wrong to dig out an old thread, but I'm facing the exact same problem and although several people seem to have succeeded in the end, no one posted a clear way to run softpwm on ATTiny2313

So based on this topic, and particularly this advice:

I thought the best was to modify SoftPWM_timer.h as follows to use Timer1 for ATtinyX313. Basically, I defined USE_TIMER1 for X313 chips, and added corresponding macros in that case:

#include <avr/io.h>
#include <avr/interrupt.h>

// allow certain chips to use different timers
#if defined(__AVR_ATmega32U4__)
#define USE_TIMER4_HS // Teensy 2.0 lacks timer2, but has high speed timer4 :-)
#elif defined(__AVR_ATtinyX313__)
#define USE_TIMER1
#else
#define USE_TIMER2
#endif

// for each timer, these macros define how to actually use it
#if defined(USE_TIMER2)
#define SOFTPWM_TIMER_INTERRUPT    TIMER2_COMPA_vect
#define SOFTPWM_TIMER_SET(val)     (TCNT2 = (val))
#define SOFTPWM_TIMER_INIT(ocr) ({\
  TIFR2 = (1 << TOV2);    /* clear interrupt flag */ \
  TCCR2B = (1 << CS21);   /* start timer (ck/8 prescalar) */ \
  TCCR2A = (1 << WGM21);  /* CTC mode */ \
  OCR2A = (ocr);          /* We want to have at least 30Hz or else it gets choppy */ \
  TIMSK2 = (1 << OCIE2A); /* enable timer2 output compare match interrupt */ \
})
#elif defined(USE_TIMER1)
#define SOFTPWM_TIMER_INTERRUPT    TIMER1_COMPA_vect
#define SOFTPWM_TIMER_SET(val)     (TCNT1 = (val))
#define SOFTPWM_TIMER_INIT(ocr) ({\
  TIFR = (1 << TOV1);     /* clear interrupt flag */ \
  TCCR1B = (1 << CS11);   /* start timer (ck/8 prescalar) */ \
  TCCR1A = (1 << WGM11);  /* CTC mode */ \
  OCR1A = (ocr);          /* We want to have at least 30Hz or else it gets choppy */ \
  TIMSK = (1 << OCIE1A);  /* enable timer1 output compare match interrupt */ \
})
#elif defined(USE_TIMER4_HS)
#define SOFTPWM_TIMER_INTERRUPT    TIMER4_COMPA_vect
#define SOFTPWM_TIMER_SET(val)     (TCNT4 = (val))
#define SOFTPWM_TIMER_INIT(ocr) ({\
  TCCR4A = 0; \
  TCCR4B = 0x04; /* CTC Mode */\
  TCCR4C = 0; \
  TCCR4D  = 0; \
  TCCR4E  = 0; \
  OCR4C  = 0; \
  OCR4C  = (ocr); \
  TIMSK4  = (1 << OCIE4A); \
})
#endif

I also defined SOFTPWM_MAXCHANNELS to be 6 for the moment, because 20 requires too much memory.

However, my samples seem to have all timings wrong.

Here is my current test:

#include "SoftPWM.h"

void setup() {
  SoftPWMBegin(SOFTPWM_INVERTED);
  for (int i = 8; i < 14; i++) {
    SoftPWMSet(i, 0);
    SoftPWMSetFadeTime(i, 300, 300);
  }
}

void loop() {
  delay(1000);
  for (int i = 8; i < 14; i++) {
    SoftPWMSet(i, 85);
    delay(1000);
    SoftPWMSet(i, 0);
    delay(1000);
  }
}

I see a flash through all the leds with a small pause between each loop().
The scope indicates I'm getting 6ms pulses, but there's some randomness in the delays between them (see attached captures).

I have the feeling something messes with Timer0, but I fail to see what...

Any idea what could be wrong ?

All comments are welcome of course.

Vicne

Hi,

Here are more tests, but they leave me even more puzzled.

What I did was first to focus on one single led, and triggering it on and off with a 1 second delay (in theory) in between. So the code is:

#include "SoftPWM.h"

void setup() {
  SoftPWMBegin(SOFTPWM_INVERTED);
  SoftPWMSetFadeTime(8, 0, 0);
}

void loop() {
  delay(1000);
  SoftPWMSet(8, 255);
  delay(1000);
  SoftPWMSet(8, 0);
}

What I observe is that the led goes on and off with a 32 second period instead of 2 (so delay takes 16 times more time than it should.

Then I thought I might debug when the interrupt handler is triggered, so I've added "pinMode(14, OUTPUT);" to the setup and "digitalWrite(14, 1-digitalRead(14));" as the first line of the ISR implementation.

The scope shows that pin 14 has a pulse every ms (meaning the ISR gets triggered twice in a row), while the pmw output in active state is almost always ON, except a small glitch every 125ms (See captures).

I really don't understand what's going on, because when I triggered all the leds yesterday, I had the feeling that delay() was passing way too fast (and that was also the observation by Marvin Martian before he found the issue), while now time passes 16 times too slowly...

I also checked the ATTiny core source declaration and for the 2313, it confirms what Coding Badly said:

#define TIMER_TO_USE_FOR_MILLIS 0

So I don't see where the conflict comes from...

(oh, and yes, it's an actual conflict, because I also checked that fuses were properly burnt by removing calls to softPWM and using digitalWrite(), and in that case, the 1000ms delay is OK.)

@vapor20, I know it was 18 months, but if you're still around, do you remember the way you "changed the timers used in .cpp" ? The only static references to timers I find are in the "#ifdef WIRING" sections, which are not active here (I checked). It seems all the rest points to macros in SoftPWM_timer.h...

Kind regards,

Vicne

Update:

By dichotomic trial and error :-), I could determine that the line that messes up is the last one of the SOFTPWM_TIMER_INIT macro:

TIMSK = (1 << OCIE1A); /* enable timer1 output compare match interrupt */

In other words, reconfiguring Timer 1 did not disturb the delay() behaviour. So there is no timer conflict as I thought.

My guess now is that the issue is that both timers (0 for millis() and 1 for pwm) call the same interrupt code, or that the timer 0 interrupt service routine gets called when timer 1 interrupt triggers, causing chaos.

Now I admit it's still vague, but at least that's a small progress...

Of course, any idea or comment is welcome.

Kind regards,

Vicne