I am trying to write a simple software PWM to fade some LEDs because I don't have enough PWM I/O Ports.
The code evaluates how long you want the LED to fade, and breaks it down into a number of steps. During each step, it will evaluate the period the LED should be on.
I have the following code:
class Led{
public:
Led(int pin);
void setup();
// void updateFadeDirection();
void updateBrightness();
private:
int _pin;
unsigned long _currentTime;
unsigned long _startTime; // Very beginning of fade period
unsigned long _fadePeriod; // How long to fade on and off
int _fadeStep; // current fade step i.e. 1/255
int _fadeSteps; // How many steps to take until full brightness
int _fadeDirection; // low to high or high to low?
float _T_on; // Amount of time to turn LED on
float _T_fadeStep; // Amount of time per step increment of brightness
unsigned long _stepTime; // Start of current step period
bool _evaluated;
};
Led::Led(int pin){
_pin = pin;
}
void Led::setup(){
pinMode(_pin, OUTPUT);
_startTime = micros();
_fadePeriod = 2*1000000; //microseconds(us)
_fadeStep = 50; // start at 0 brightness here
_fadeSteps = 100;
_fadeDirection = 1;
_T_fadeStep = _fadePeriod/_fadeSteps; // i.e. 2000000us/100steps = 20000us/step of brightness
_T_on = (_fadeStep/_fadeSteps) * _T_fadeStep; // i.e. 20/100 * 20000us = 4000us on
_stepTime = micros();
delayMicroseconds(200);
}
void Led::updateBrightness(){
_currentTime = micros();
if(_currentTime - _startTime < _fadePeriod){
if (_currentTime - _stepTime > _T_on){
digitalWrite(_pin, LOW);
} else {
digitalWrite(_pin, HIGH);
}
if(_currentTime - _stepTime >= _T_fadeStep){
// _fadeStep++;
_stepTime = _currentTime;
_T_on = (_fadeStep/_fadeSteps) * _T_fadeStep;
}
}
else{
digitalWrite(_pin, HIGH);
}
}
Led LED0(0);
void setup() {
LED0.setup();
}
void loop() {
LED0.updateBrightness();
}
The current behaviour when powered on is the single LED will be turned off, then will turn on after 2 seconds.
In particular, it never seems to enter the else
part
if (_currentTime - _stepTime > _T_on){
digitalWrite(_pin, LOW);
} else {
digitalWrite(_pin, HIGH);
}
The problem is that the LED will never hit HIGH
until the outer else
is triggered.
I believe it has to do with _T_on
being an int
and the timestamps being unsigned long
and me not casting it.
The expected behaviour is 2 seconds of dimmed (50%) light, and then a full 100% when it hits the outer else
.
1 Answer 1
As @ott-- has mentioned, because of the int
datatype, _fadeStep
/_fadeSteps
will always evaluate to 0.
Here's the updated code along with some timing tweaks:
float fadeSteps = 150; // How many steps to take until full brightness
class Led{
public:
Led(int pin, float fadeStep=0);
void setup();
// void updateFadeDirection();
void updateBrightness();
private:
int _pin;
unsigned long _currentTime;
unsigned long _startTime; // Very beginning of fade period
unsigned long _fadePeriod; // How long to fade on and off
float _fadeStep; // current fade step i.e. 1/255
int _fadeDirection; // low to high or high to low?
unsigned long _T_on; // Amount of time to turn LED on
unsigned long _T_fadeStep; // Amount of time per step increment of brightness
unsigned long _stepTime; // Start of current step period
bool _evaluated;
};
Led::Led(int pin, float fadeStep){
_pin = pin;
_fadeStep = fadeStep;
}
void Led::setup(){
pinMode(_pin, OUTPUT);
_startTime = micros();
_fadePeriod = 2000000; //microseconds(us)
_fadeDirection = 1;
_T_fadeStep = _fadePeriod/2/fadeSteps; // i.e. 2000000us/100steps = 20000us/step of brightness
_T_on = (_fadeStep/fadeSteps) * _T_fadeStep; // i.e. 20/100 * 20000us = 4000us on
_stepTime = micros();
}
void Led::updateBrightness(){
_currentTime = micros();
if (_currentTime - _stepTime > _T_on){
digitalWrite(_pin, LOW);
} else {
digitalWrite(_pin, HIGH);
}
if(_currentTime - _stepTime >= _T_fadeStep){
_fadeStep += _fadeDirection;
_stepTime = _currentTime;
_T_on = (_fadeStep/fadeSteps) * _T_fadeStep;
}
if(_fadeStep >= fadeSteps-1) {
_fadeDirection = -1;
} else if(_fadeStep <= 1) {
_fadeDirection = 1;
}
}
Led LED0(0);
Led LED1(1,fadeSteps/5);
Led LED2(2,2*fadeSteps/5);
Led LED3(3,3*fadeSteps/5);
Led LED4(4,4*fadeSteps/5);
void setup() {
LED0.setup();
LED1.setup();
LED2.setup();
LED3.setup();
LED4.setup();
}
void loop() {
LED0.updateBrightness();
LED1.updateBrightness();
LED2.updateBrightness();
LED3.updateBrightness();
LED4.updateBrightness();
}
Some refactoring is needed to be more modular, but will fade (using software PWM) LEDs asynchronously on every digital OUT port on an attiny85. Can also set the starting brightness so that you can make an LED fading chaser.
int
/unsigned long
- I've just tried it. Not sure why you didn't try it if you thought that was the problem!fadePeriod
). @CharlieHanson: yes it's an LED fader using PWM, which you can set duration from 0 to full brightness, as well as how many steps in that duration (to simulate smoothness or harsh steps). I did try theint
/unsigned long
, but didn't work so thought I'd throw it up here for anyone to correct me. I am not usingOCR1A/B
.