Go Down

Topic: Best approach for changing PWM in certain time? (Read 995 times) previous topic - next topic

Baltasar

Hi, I'm creating a sort of timer for turn/DIM leds.
On this timer I can specify the duration I want that the leds take from 0 to 255 (PWM value), so to speak from off (0%) to full bright (100%).

So whats the best approach to do this and have always a smooth incresed brightness, taking in consideration that the user can setup a diferent duration time from 1 minute to 120 minutes to do the 0 to 100% DIM variation?

Thanks.
Check all my projects based on Atmel/Arduino -> www.aqualed-light.com

PaulS

Quote
Hi, I'm creating a sort of timer for turn/DIM leds.

No, you are using existing timers to make things happen.

Quote
On this timer I can specify the duration I want that the leds take from 0 to 255

Can the time be changed during a cycle? What does that do to the cycle? What is to happen when the LED gets to full brightness?

Are you aware that the human eye can not distinguish 255 different brightness levels?

Quote
So whats the best approach to do this and have always a smooth incresed brightness

What you have is basically a state machine. The state, at any time, defines what the current brightness level is. You need to invoke certain actions (change the PWM level) at each state change, and you need to change states at appropriate times. Since the desire is to have the state change occur at specific intervals, you need to record when the last state change occurred, and compare that to "now" on each pass through loop, to see if it is time to change state.

Recording when the last state change occurred is done using millis(), as is determining the current time.

Look at the blink with delay example.

Under these circumstances is it not appropriate for you to use delay().

Baltasar

#2
Jul 02, 2011, 12:27 pm Last Edit: Jul 02, 2011, 12:28 pm by Baltasar Reason: 1
Hi, thanks for your reply PaulS, when I said timer I meant to say that my machine is as sort of a clock to turn ON/OFF leds, so the user specifies X time and Y duration for the on, the machine at that time starts increasing PWM values from 0 to 255 divided by the Y duration specified, them the leds stay on until reach the time specifyd for OFF and do the oposit, witch is decreasing the PWM from 255 to 0 in another duration specifyed for OFF cycle.

Hope you understand now what I'm doing?

My main problem for now is trying to avoid float vars, since I have this already running with floats.

I take the duration value, convert it to seconds, them divide 255 for those seconds and I get the increment.
Using mils() everytime reach my next changing second I increase the PWM, I think is mor or less like you are saying... but how to take the float vars out so I can save as much memory space as possible?
Check all my projects based on Atmel/Arduino -> www.aqualed-light.com

PaulS

Quote
I take the duration value, convert it to seconds, them divide 255 for those seconds and I get the increment.

Watch your clock for a minute. Tick, tick, tick...

How much time elapses during each tick? Is it 1.0 seconds, or 2.3? or 5.1? The millis() function returns the number of milliseconds that have elapsed since the Arduino was reset, as an unsigned long (i.e. integer) value.

Determining a fractional number of milliseconds to wait is useless, since "now" and "then" are always integer values.

Even if you were able to change the PWM level at other than integral times, the length of time between changes only needs to be calculated when the total interval changes.

Avoiding float calculations that happen many times every pass through loop is a good idea. Avoiding all float calculations is an unnecessary objective.

Though, in this case, float calculations won't be useful anyway.

Baltasar

#4
Jul 02, 2011, 12:55 pm Last Edit: Jul 03, 2011, 03:08 am by Baltasar Reason: 1
Ok I see, let me show the code I have now, so you can comment on that or send me to a better direction...

This are excerpts from the total code as I have lots of other functions than the ON/OFF/DIM for the leds.

Code: [Select]

on_led_increment =  255.0 / (((float)dur_on * 60) );


void loop()
{

 if (on_finished == 0 && sec_on_count == 0){
   sec_on_count = sec_count;
   sec_on_count = sec_count + ldur_on;
 }


if (on_finished == 0 && sec_count == sec_on_count && led_dim < 255) {

   sec_on_count += 1;

   if (led_dim < 255) {
     led_dim += (float)on_led_increment;
     for (led = led_dim - on_led_increment; led <= led_dim; led += 0.10){
       analogWrite(10, led);
     }
   }

   if (led_dim >= 255) {
     on_finished = 1;
     getCloudsSec();
   }
}

void SyncClock() {
 // create a military-time integer from RTC values
 mil_time = (hours * 100) + minutes;
 sec_count = hours * 60;
 sec_count *= 60;
 sec_count += (minutes * 60);
 sec_count += seconds;
}
Check all my projects based on Atmel/Arduino -> www.aqualed-light.com

AWOL

For this sort of thing, I often use an 8.24 fixed-point format (32 bit overall), using the top eight bits as the PWM value, and simply accumulate pre-calculated fractions.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Baltasar

Hummm, sorry... AWOL can you detail that or give a code example, I'm too new on this to understand most of what you are saying.
Check all my projects based on Atmel/Arduino -> www.aqualed-light.com

AWOL

Imagine a "long" variable "x" as an array of four "byte"s "xb[4]" (define a union).
Because the Arduino is little-endian, the most significant eight bits of "x" are held in xb[3].
xb[3] will the value written to our PWM device.

Now, if we set "x" to 0x01000000, xb[3] will contain 0x01.
Assume we want to fade x from 1 to 5 in 300 steps, so we take the difference of 5 and 1 (4) and divide by 300, yielding  0.0133.
Now, multiply 0.013333 by 224 (= 16 777 216) to give 223 696 (0x000369D0)
Store this value in a "long" called "inc".
Now, repeatedly add "inc" to "x", but only write xb [3] to your PWM device each time through the loop.

This technique is most useful when you've got a number of LEDs to fade to and from different values, but in a fixed number of steps.

(it comes out a lot easier than I described it!)
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

MarkT

#8
Jul 03, 2011, 01:57 am Last Edit: Jul 03, 2011, 02:01 am by MarkT Reason: 1
You don't need floats for ratiometric things like this.

Say you want to step 255 steps in 317 seconds.  You initialize a variable accum to 0, and every second you do this:

Code: [Select]

 accum += 255 ;
 while (accum >= 317)
 {
    brightness += 1 ;
    analogWrite (pin, brightness) ;
    accum -= 317 ;
 }


After 317 seconds you have incremented the variable by 255 a total of 317 times.  But you have also decremented it by 317 a total of 255 times (and also stepped the brightness 255 steps).

This integer ratio based technique is the mainstay of all graphics promitives (drawing sloped lines) and is variously known as DDA (digital differential analyzer), or Bressenham's algorithm.  And it avoids floats and is ultra-fast.

If you process every millisecond, just use 317000 instead of 317
[ I won't respond to messages, use the forum please ]

Baltasar

MarkT,thanks for the help you solution is almost perfect for me.

Going now to do more tests and see if I reach the perfect result that I need, since apart the need of diming from 0 to 255 I need to show on the LCD the dimming percentage (0% to 100%), converting is easy, but I get again the float values that I can't compare with the integer pwm values. For example 48% represents 122,4 pwm value, if I want to stop during the DIM on a specific value like this 48% there is no way sinve only going to pass on 122 integer.

:smiley-roll:
Check all my projects based on Atmel/Arduino -> www.aqualed-light.com

Go Up