Reputation: 7076
I'm experimenting with the following code. I want my LEDs to fade up slowly over, say, 2 seconds/2000ms. This code, as written, in indistinguishable from looping digitalWrite(2,HIGH)
and digitalWrite(2,LOW)
. I suspect that the processor zips through the 255 steps so quickly that the fade duration is imperceptible.
void setup () { # set pin 2 as output
pinMode = (2,OUTPUT);
}
void loop () {
(for int fi=0;fi<255;fi++) { # fade IN pin2 from 0-254
analogWrite(2,fi)
}
(for int fo=0;fo<255;fo--) { # fade OUT pin2 from 0-254
analogWrite(2,fo)
}
}
I'd like a suggestion or two about the how to slow down the fading up and fading down. My initial thought was to add delay(8)
in each of the for steps...but would that appear as more of a stepped power increase than a smooth, linear fade? Is that necessarily a best practice or is there a more acceptable way?
Any other ideas?
Upvotes: 0
Views: 3115
Reputation: 2819
Yes, you are correct that it is not possible to create true linear fade with a microcontroller. The output will always have some minimum step size, or resolution. For you, the question is "does the PWM have enough resolution that the steps will not be perceived?"
The PWN can output 0 to 100% in 255 steps. That is the same as the 8 bit resolution of each pixel on your monitor. Look at any monitor calibration page and you can probably convince yourself that 8 bits of resolution provides what will appear essentially a smooth scale. Example monitor gradient
So here is your sample that fades an LED on and off every 4 seconds. This shows you a couple of ways how to perform a task in the background.
The 4 second change on to off uses the method described in the Blink without Delay Arduino docs. Each pass through the loop() you check if the elapsed time since the last change exceeds the specified interval -- if so, you change the state and mark the current time.
The fade is accomplished slightly different. You leave behind a time when the next change will occur. Each spin through loop() you check if it is time for the next action. If yes, you make the change, and store when to make the next change.
/* unfortunately standard LED on pin 13 does not have PWM
You will need to connect an LED and resistor */
const int pin = 3;
/* this is your step time between changes in light
output in milliseconds */
const unsigned int tstep = 8;
/* this is the time then next change can be made.
The change will happen at or after this value. */
unsigned int tnext = 0;
/* this is the current and target light level
The output will slowly ramp until the current value
meets the target. So to request that the change start,
the code just sets a new target. */
unsigned int target = 0;
unsigned int current = 0;
/* These are from Blink without Delay
Shows another way to execute delays. */
unsigned long previousMillis = 0;
unsigned long interval = 4000;
void setup() {
pinMode(pin,OUTPUT);
tnext = millis() + tstep;
analogWrite(pin, current);
}
/* something in the code calls this
to request a change in the LED state
Pass what you want to be the new value
and the LED will slowly change to that value. */
void newTarget(int value) {
tnext = millis() + tstep;
target = value;
}
/* call this frequently to update the LED
If the time of the next action has been reached,
execute the change and setup when the following
change will occur. */
void driveLed() {
unsigned int tnow = millis();
if(target != current) {
if(tnow >= tnext) {
if(target > current) {
current += 1;
}
else {
current -= 1;
}
analogWrite(pin, current);
tnext += tstep;
}
}
}
/* Note that the main loop has no delays.
Execution will spin rapidly through this, most times
checking and finding nothing to to.
You would add your other functionality here, and this
LED would continue to happen. As long as you don't pack
anything that takes on the order of 8 ms, you won't notice
a change in the LED fade behavior.
void loop() {
unsigned int tnow = millis();
// some other logic here would decide when to change the LED state
// For this sample, just toggle every 4 seconds
if((tnow - previousMillis) >= interval) {
if(0 == target) {
newTarget(255);
}
else {
newTarget(0);
}
previousMillis = tnow;
}
driveLed();
}
Upvotes: 2
Reputation: 2720
I would use the micros function to get finer control over when you issue the writes:
http://arduino.cc/en/Reference/Micros
It is not a delay, but just returns the number of micro seconds that have passed since the board came online, with it you can do some logic like this:
setup()
{
t0 = micros()
}
loop()
{
t1 = micros();
dt = t1 - t0;
if (dt > SOME_SMALL_TIME_DELTA)
{
increment_or_decrement_led_value_by_one();
t0 = t1
}
}
Upvotes: 2