Hi all. I've been playing around with software PWM controlling some
LEDs. Problem is, as the eye's response to brightness isn't linear, when
I simply control the LEDs brightness using an 8 bit value (with no
compensation) they don't appear to dim smoothly. So my thought was to
take the 8 bit value, use it as an index into a lookup table which would
give me a higher bit value (ie a 12 or 14 bit).
Here is the problem (as I see it anyways). If I increase the bit
resolution of my PWM, the frequency will have to go down (as the PIC is
already running at full speed. yes, I could go to an SX chip...I know).
So, is there a resolution vs speed balance that anyone has reached that
gives optimal frequency and smoothness of the dimming? Also, how do you
choose the values for the lookup table? Most commercial theatrical
dimmers use a "modified S curve" which is essentially the integral of
the area under a sine wave (I think). I'm sure if I had Maple or
something (and knew how to use it) I could divide the area into 256
sections and come up with the proper values for the lookup table. But
since I don't have access to this high powered math software, what are
my options? Plotting out by hand seems only an invitation for
frustration.
Ideas welcome.
Thanks,
Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
-Douglas Adams
Problem is, as the eye's response to brightness isn't linear, when I
simply control the LEDs brightness using an 8 bit value (with no
compensation) they don't appear to dim smoothly. So my thought was to
take the 8 bit value, use it as an index into a lookup table which
would give me a higher bit value (ie a 12 or 14 bit).
Do you really need 256 steps? Why not try something like a 4 bit
lookup into your 8bit pwm table. That would be easier (IMO) than
implementing a >8bit PWM on an 8 bit controller, and it'd also be
realtively easy to move the values around to make it look the way
you want it to...
which is essentially the integral of the area under a sine wave
That'd be unlikely, since the integral of a sine wave is another sine wave!
> Hi all. I've been playing around with software PWM controlling some
> LEDs. Problem is, as the eye's response to brightness isn't linear, when
> I simply control the LEDs brightness using an 8 bit value (with no
> compensation) they don't appear to dim smoothly. So my thought was to
> take the 8 bit value, use it as an index into a lookup table which would
> give me a higher bit value (ie a 12 or 14 bit).
>
> Here is the problem (as I see it anyways). If I increase the bit
> resolution of my PWM, the frequency will have to go down (as the PIC is
> already running at full speed. yes, I could go to an SX chip...I know).
> So, is there a resolution vs speed balance that anyone has reached that
> gives optimal frequency and smoothness of the dimming? Also, how do you
> choose the values for the lookup table? Most commercial theatrical
> dimmers use a "modified S curve" which is essentially the integral of
> the area under a sine wave (I think). I'm sure if I had Maple or
> something (and knew how to use it) I could divide the area into 256
> sections and come up with the proper values for the lookup table. But
> since I don't have access to this high powered math software, what are
> my options? Plotting out by hand seems only an invitation for
> frustration.
The preceived brightness is essentially the logarithm of the actual
brightness, and the actual brightness of LEDs is reasonably linear to
current. First you have to decide what dynamic output range you can
produce, then map that to the non-zero intended brightness values
logarithmically. Since the log will never let you get to zero, you
arbitrarily assign 0 input value to LED completely off. Note that you
will lose resolution in the conversion, but how many different brightness
levels do you really need?
Let's do an example. You are using 8 bit PWM output, so the linear
brightness can vary from 0 to 255, with 0 completely off and 255
completely on. The dynamic range of the non-zero output values is 255/1.
Now map this range logarithmically to the non-zero range of input number.
If the desired perceived brightness values are 0-100, then 1-100 maps to
output values 1-255 lograithmically. In other words, each single step up
in the 1-100 range increases the output value by a fixed *multiple* (fixed
increment would be linear). There are 99 steps from 1-100 to cover the
dynamic range ratio of 255. The step multiple is therefore the 99th root
of 255, or
> Hi all. I've been playing around with software PWM controlling some
> LEDs. Problem is, as the eye's response to brightness isn't linear, when
> I simply control the LEDs brightness using an 8 bit value (with no
> compensation) they don't appear to dim smoothly. So my thought was to
> take the 8 bit value, use it as an index into a lookup table which would
> give me a higher bit value (ie a 12 or 14 bit).
>
> Here is the problem (as I see it anyways). If I increase the bit
> resolution of my PWM, the frequency will have to go down (as the PIC is
> already running at full speed. yes, I could go to an SX chip...I know).
> So, is there a resolution vs speed balance that anyone has reached that
> gives optimal frequency and smoothness of the dimming? Also, how do you
> choose the values for the lookup table? Most commercial theatrical
> dimmers use a "modified S curve" which is essentially the integral of
> the area under a sine wave (I think). I'm sure if I had Maple or
> something (and knew how to use it) I could divide the area into 256
> sections and come up with the proper values for the lookup table. But
> since I don't have access to this high powered math software, what are
> my options? Plotting out by hand seems only an invitation for
> frustration.
>
> Ideas welcome.
Well, if you're using a software PWM then a really simple solution may be
to just let the time base be logarithmic. For example, suppose you had a
PWM with 8 levels. Normally, the time step is constant so that a pulse of
4 units, is on exactly twice as long as a pulse of 2 units. In terms of a
time line:
Start
V V
+---+---+---+---+---+---+---+---+---+---+---+
0 1 2 3 4 5 6 7 0 1 2 3
At the 0's the PWM turns on and at the 1-7 steps it turns off. Now,
suppose you made the time step like so:
So now, a pulse 4 units wide takes more than twice the time of a pulse 2
units wide.
The difficult part here will be to perform the non-linear time stepping.
There are several ways to go about doing this, but they depend on the
particulars of your system.
Note that this really is the same thing that you're suggesting; I've only
partioned the time differently. In your case, you're suggesting that the
dynamic range of the PWM duty cycle be increased. In my case, I'm
suggesting that the dynamic range is kept the same, but the time base
adjusted.
If you're using my software PWM (number of cycles = 7 + 2*Number PWM
outputs or 23 for 8 outputs), then the first few iterations could be run
back to back while the latter ones interrupt driven. You're still going to
want the roll over rate to be large enough so that there's no perceivable
flicker.
Another possibility is to consider a Pulse-Modulation scheme based on
phase accumulators. It gives you a non linear output that may be
suitable. For example:
phase_step = some number --- controls the output level
phase_accumulator --- accumulates phase, i.e. an integrator
The algorithm is simple:
phase_accumulator = phase_accumulator + phase_step
if phase_accumulator rolls over, then set the output.
In PIC assembly, it'd look something like this:
DECFSZ MainCounter,F
goto update_pulses
CLRF PWM_port
update_pulses:
MOVF phStep0,W
ADDWF phAccum0,F
RLF Rollovers,F
MOVF phStep1,W
ADDWF phAccum1,F
RLF Rollovers,F
...
MOVF phStep7,W
ADDWF phAccum7,F
RLF Rollovers,W ; the last shift goes into W
IORWF PWM_port,W
That's 28 cycles instead of 23, but look closely how it works. The
rollover bit is simply the carry from the addition to the phase
accumulator. If you wrote the rollover bit to an I/O pin, it would look
almost like a square wave DDS. In other words, if phStep = 1, then the
rollover rate would be 256 iterations and the frequency would be 512
iterations. If phStep = 2, then the rollover is 128 and the frequency is
256. It's twice as fast. If phStep is 3, then the rollover is in about 85
iterations (256/3) and the frequency is about 512/3 iterations. In
general, the rollover rate is 256/N iterations and the frequency is 512/N.
However, notice that the above code *does not* toggle the output at every
rollover. Instead, it latches the rollover. In effect, the pulse outputs
are just the first cycle of the DDS frequency.
In other words, the output pulse is inversely proportional to the pwStep.
Also note that there are way fewer than 256 steps. OTOH, it's
computationally simple. You can also do a few tricks to sustain a constant
output, (either on or off). Also, the IORWF can be changed to ANDWF to
invert the polarity of the drive (and the outputs would all have to be set
instead of cleared when the main counter rolls over).
You need non-linear curve, and the amount of bits depends on how fast you
wish the LEDs to dim, so that the eye may not notice the brightness changes.
The link below leads to page from which you'll find my code for controlling
16 model railway light signal LED's with individual PWM (I needed to get the
LEDs emulate low voltage filament bulbs used in railway signals, and also to
have the property to first dim out the previous aspect (light) before
brightening the next one. The system also has flashing lights, also slowly
brighting and dimming with PWM.
I calculated the brightness values as 0..FF as it was the easisest way to
figure out if the brightness value was full on or fully off (is it zero or
if adding one would cause overflow). There are 16 such varialbles and the
interlocking logic managed these values by adding one or subtracting one
from these varialbles. Another loop copied the values, cut lower nibble
("swap" and "and") and with lookup table converted these into x^3 form:
another loop was then used to count from 32 to zero and decrement the newly
calculated values until the value was zero and the led switched out. The
output LEDs were in 2 x 6 matrix. after the complete LED operation phase
(both brances of the 2x6 matrix), another calculus phase came and new values
to 0..FF registers were calculated to be again lookup'ed and outout...
The code is a bit clumsy, but this was my first PIC project that I did from
start to finish!
The main loop has multiple entries of certain "subs" but that was at the
time the easisest way to do final adjustment of the blink rate;) B.6 was
driving the other ends of the leds (inverter amplifier and amplifier) and
the B.0..B.5 pins the other ends of the LEDs thus driving 12 LED's
independently w. 7 pins.
Pekka Siiskonen [PS: 8 spaces for a tab in asm file, sorry!]
I don't see why you have to go to *more* then
8 bit resolution. You just have to "map" your
8-bit *linear* value through a table into a 8-bit
*non-linear* value (that controles the PWM regs).
This means that in one end of the table you will map into
a sequense of equal values, and in the other end
you will have "gaps". All to give the impression
of a "smooth" dim.
And the question is also, how many "steps" do you need
to get a smooth dim. Will you see the differense between
two steps in a 128 step dim ? Or a 64 step dim ? I'd guess
not.
Jan-Erik Söderholm
Josh Koffman wrote:
>Hi all. I've been playing around with software PWM controlling some
>LEDs. Problem is, as the eye's response to brightness isn't linear, when
>I simply control the LEDs brightness using an 8 bit value (with no
>compensation) they don't appear to dim smoothly.
Olin, what have you been talking about so long?
Was it in few words:
At full load: the PWM duty cycle = PWM Period, say 255
At each step when dimming divide PWM duty cycle by some value, say 2. (255,127,63,...) Loop until LED brightness will get less then brightness of an environment (you may use A/D with photosensor to catch the level) . By the way, the divider value may be modified at each step to get desired curve.
You style is really cool:
...
> 1 1
> 2 1.06
> 3 1.12
> 4 1.18
> 5 1.25
...
> Of course you will have to round the output values above.
Of course we will, and what do you suggest we'll get?
> The preceived brightness is essentially the logarithm of the actual
> brightness, and the actual brightness of LEDs is reasonably linear to
> current. First you have to decide what dynamic output range you can
> produce, then map that to the non-zero intended brightness values
> logarithmically. Since the log will never let you get to zero, you
> arbitrarily assign 0 input value to LED completely off. Note that you
> will lose resolution in the conversion, but how many different
brightness
> levels do you really need?
>
> Let's do an example. You are using 8 bit PWM output, so the linear
> brightness can vary from 0 to 255, with 0 completely off and 255
> completely on. The dynamic range of the non-zero output values is
255/1.
> Now map this range logarithmically to the non-zero range of input
number.
> If the desired perceived brightness values are 0-100, then 1-100 maps
to
> output values 1-255 lograithmically. In other words, each single step
up
> in the 1-100 range increases the output value by a fixed *multiple*
(fixed
> increment would be linear). There are 99 steps from 1-100 to cover
the
> dynamic range ratio of 255. The step multiple is therefore the 99th
root {Quote hidden}
> of 255, or
>
> StepRatio = DynamicRange ** (1 / NSteps)
> = 255 ** (1 / 99)
> = 1.057568
>
> The output value resulting from each non-zero input value is then:
>
> output = StepRatio ** (input - 1)
> = 1.057568 ** (input - 1)
>
> Since logarithms don't go to zero, you arbitrarily assign input 0 to
> output completely off. So here are a few sample input and output
Just wanted to thank you guys for the ideas. I will be digesting them
tomorrow when I get the chance.
Just didn't want to think all your brilliance went unappreciated :)
Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
-Douglas Adams
> Well, if you're using a software PWM then a really simple solution may be
> to just let the time base be logarithmic. For example, suppose you had a
> PWM with 8 levels. Normally, the time step is constant so that a pulse of
> 4 units, is on exactly twice as long as a pulse of 2 units. In terms of a
> time line:
>
> Start
> V V
> +---+---+---+---+---+---+---+---+---+---+---+
> 0 1 2 3 4 5 6 7 0 1 2 3
>
> At the 0's the PWM turns on and at the 1-7 steps it turns off. Now,
> suppose you made the time step like so:
>
> ++-+--+---+------+---------+--------------+---------------------++-+
> 01 2 3 4 5 6 7 01 2
>
> So now, a pulse 4 units wide takes more than twice the time of a pulse 2
> units wide.
>
> The difficult part here will be to perform the non-linear time stepping.
> There are several ways to go about doing this, but they depend on the
> particulars of your system.
>
> Note that this really is the same thing that you're suggesting; I've only
> partioned the time differently. In your case, you're suggesting that the
> dynamic range of the PWM duty cycle be increased. In my case, I'm
> suggesting that the dynamic range is kept the same, but the time base
> adjusted.
>
> If you're using my software PWM (number of cycles = 7 + 2*Number PWM
> outputs or 23 for 8 outputs), then the first few iterations could be run
> back to back while the latter ones interrupt driven. You're still going to
> want the roll over rate to be large enough so that there's no perceivable
> flicker.
>
> Another possibility is to consider a Pulse-Modulation scheme based on
> phase accumulators. It gives you a non linear output that may be
> suitable. For example:
>
> phase_step = some number --- controls the output level
> phase_accumulator --- accumulates phase, i.e. an integrator
>
> The algorithm is simple:
>
> phase_accumulator = phase_accumulator + phase_step
> if phase_accumulator rolls over, then set the output.
>
> In PIC assembly, it'd look something like this:
>
> DECFSZ MainCounter,F
> goto update_pulses
>
> CLRF PWM_port
>
> update_pulses:
>
> MOVF phStep0,W
> ADDWF phAccum0,F
> RLF Rollovers,F
>
> MOVF phStep1,W
> ADDWF phAccum1,F
> RLF Rollovers,F
>
> ...
>
> MOVF phStep7,W
> ADDWF phAccum7,F
> RLF Rollovers,W ; the last shift goes into W
>
> IORWF PWM_port,W
>
> That's 28 cycles instead of 23, but look closely how it works. The
> rollover bit is simply the carry from the addition to the phase
> accumulator. If you wrote the rollover bit to an I/O pin, it would look
> almost like a square wave DDS. In other words, if phStep = 1, then the
> rollover rate would be 256 iterations and the frequency would be 512
> iterations. If phStep = 2, then the rollover is 128 and the frequency is
> 256. It's twice as fast. If phStep is 3, then the rollover is in about 85
> iterations (256/3) and the frequency is about 512/3 iterations. In
> general, the rollover rate is 256/N iterations and the frequency is 512/N.
>
> However, notice that the above code *does not* toggle the output at every
> rollover. Instead, it latches the rollover. In effect, the pulse outputs
> are just the first cycle of the DDS frequency.
>
> pwStep pulse width out
> -----------------------
> 1 256/1 = 256
> 2 256/2 = 128
> 3 256/3 = 85
> 4 256/4 = 64
> 5 256/5 = 51
> ...
>
> 128 256/128 = 2
> 129 256/129 = 1
> ...
> 255 255/255 = 1
>
> In other words, the output pulse is inversely proportional to the pwStep.
> Also note that there are way fewer than 256 steps. OTOH, it's
> computationally simple. You can also do a few tricks to sustain a constant
> output, (either on or off). Also, the IORWF can be changed to ANDWF to
> invert the polarity of the drive (and the outputs would all have to be set
> instead of cleared when the main counter rolls over).
At 11:11 PM -0600 2/9/03, Josh Koffman wrote:
>Just wanted to thank you guys for the ideas. I will be digesting them
>tomorrow when I get the chance.
>
>Just didn't want to think all your brilliance went unappreciated :)
>
>Josh
A couple weeks ago I asked a related question and got some useful feedback.
Not sure everyone here is getting the trade-off between resolution,
trying to match the visual response of the eye, and avoiding obvious
(to the eye) steps as brightness ramps up.
Here is what I did to make lookup tables easily...
Using Excel I made a spreadsheet 256 cells across (256 columns). The
formula I put in Cell(1,1) was...
=CELL("col",A1) - 1
And then I cut and pasted Cell(1,1) into all the other columns.
This just makes the value of each cell it's column number minus one. e.g.
0 1 2 3 4 5 6 ...
Then in Cell(1,2) I put...
=ROUND( ((A1^2)/255), 0 )
And cut and pasted it to all the other columns. This yields something like
So the 2nd row is an exponentially increasing table of numbers from 0 to 255.
You can then export that as a comma delimited text file format and you get...
which can be cut and pasted into your code as a look up table. No
tedious hand calculation or typing!
To my eye when I use this table to set the pulse width within a loop
that indexes up from 0 to 255 the ramp-up (and down) of the LED is
very linear to the eye.
The problem is that with only 8 bits of resolution the low intensity
values in the curve round off to the same integer...and thus when the
LED is just turning on and dim-getting-brighter the steps are quite
visible...
I don't think there is any free lunch here. If you want an
exponential response then whether the loop timing is regular (just
looping through the table above), or the steps for the PWM are
updated in time non-linearly (which the above, in effect, does
anyway), when you only have 8 bits of resolution there are going to
be visible steps in the early ramp up.
Of course I could be wrong, and if so I'd like to see how it can be
done in terms of the above specifics...
>I don't think there is any free lunch here. If you want an
>exponential response then whether the loop timing is regular
>(just looping through the table above), or the steps for the
>PWM are updated in time non-linearly (which the above, in
>effect, does anyway), when you only have 8 bits of resolution
>there are going to be visible steps in the early ramp up.
Of course you could always use the 10 bit mode of the PWM, and have the
spreadsheet calculate a 255 entry table of 10 bit values :)). It will
require slightly more programming effort, but you can also make your
spreadsheet do the 8bit/2bit split, and arrange them so the 2 bits are in
the correct bit positions to be OR'd into the appropriate register.
FYI I take it from the way you presented the info on the tables, that you
did it in rows across the spreadsheet. I find it easier to do it in columns,
and the copy and paste still works across multiple rows and columns. Which
ever you find works best :))
At 5:44 PM +0000 2/10/03, Alan B. Pearce wrote:
> >I don't think there is any free lunch here. If you want an
>>exponential response then whether the loop timing is regular
>>(just looping through the table above), or the steps for the
>>PWM are updated in time non-linearly (which the above, in
>>effect, does anyway), when you only have 8 bits of resolution
>>there are going to be visible steps in the early ramp up.
>
>Of course you could always use the 10 bit mode of the PWM, and have the
>spreadsheet calculate a 255 entry table of 10 bit values :)). It will
>require slightly more programming effort, but you can also make your
>spreadsheet do the 8bit/2bit split, and arrange them so the 2 bits are in
>the correct bit positions to be OR'd into the appropriate register.
Yep...already in mind to try that...
>FYI I take it from the way you presented the info on the tables, that you
>did it in rows across the spreadsheet. I find it easier to do it in columns,
>and the copy and paste still works across multiple rows and columns. Which
>ever you find works best :))
I tried it in columns first (seems more natural) but then the export
to CSV (comma separated values) comes out wrong...it seems to write
out all the columns for row 1, all the columns for row 2, etc...there
may be some clever way around that...dunno....
part 1 1360 bytes content-type:text/plain; (decoded 7bit)
> Here is what I did to make lookup tables easily...
>
> Using Excel I made a spreadsheet ...
This topic has gotten a surprising amount of interest. To give the result
explicitly and also show how easy it is to compute, I wrote a program to
generate an MPASM table of logarithmic values. Here is the program:
{ Program BR
*
* Write a linear to logarithmic lookup table in PIC
* assembler format.
}
program br;
begin
writeln (' retlw 0 ; 0'); {first entry defined explicitly}
ov := 1.0; {init output value for first entry}
for iv := 1 to max_in do begin {once for each remaining entry}
writeln ( {write this table entry}
' retlw ', round(ov):3, ' ;',iv:3);
ov := ov * m {make value for next table entry}
end; {back to do next table entry}
end.
To avoid 11111111111111111 picture one can change PWM
period (not only PWM duty cycle) by writing to the PR2 register
too. How about extra look-up table for PR2?
Mike.
------------------
P.S. Oleg Blokhin, well-known soccer player ("Dinamo" Kiev)
was 11 number. When "Dinamo" battled against "Spartak" in
Moscow, he was abused badly by "Spartak" fanatics usually.
They shouted extremely offensive jokes based on association
of "1" with some part of human body. :-( :-)
-----
O tempora! O mores!
- Cicero
Olin Lathrop wrote:
> This topic has gotten a surprising amount of interest. To give the
result
> explicitly and also show how easy it is to compute, I wrote a program
to
> generate an MPASM table of logarithmic values. Here is the program:
...
> The table generated by this program is attached.
... {Quote hidden}
At 2:17 PM -0500 2/10/03, Olin Lathrop wrote:
>This topic has gotten a surprising amount of interest. To give the result
>explicitly and also show how easy it is to compute, I wrote a program to
>generate an MPASM table of logarithmic values. Here is the program:
...
>The table generated by this program is attached.
...
Just glancing at the results in the attachment something seems not quite right.
There are fewer 1's than 2's at the start of the table (19 vrs 23)
which seems contrary to a curve continuously gaining slope...
Hi all, and thank you again for all the suggestions. I've spent part of
the day trying to digest them all, and I've realized high math is not my
forte :) Never the less, I have plugged on.
At the moment, I like Scott's idea. One of my concerns was that I would
run out of processor time if I needed to recalculate the proper PWM
value too often. However, if I just modify the timing of the PWM steps
themselves, I can just use the 0-255 value for PWM level directly.
Scott, I'm sure your routine is brilliant (as per usual), but at the
moment it's above my head :)
I have come up with another idea on how to accomplish this though. I am
considering moving the PWM back to the mainline of the code for speed
issues. So the main code will be in a loop. It will do a couple of other
things, then hit the PWM section. My proposal is this. I will keep a
counter. At the start of the PWM section, the counter will get
decremented and checked for zero. If it has hit zero, the PWM section
(basically a single update) will run. If it hasn't hit zero, the PWM
section is skipped, and will get checked on the next iteration of the
main loop.
If the code runs through the PWM section, once it is finished, it will
reload the counter that will determine how many times the PWM routine is
skipped the next time. By varying the counter load value, I should be
able to vary the timing between PWM iterations, correct? I was thinking
of keeping another counter, this one will count up. When I need to
reload the countdown timer, I will use the countup timer as an index to
a lookup table. This table will contain the right values to affect a non
linear PWM curve timing.
So, will this work, and even if it will, is this feasible? I'm sure
Scott's solution is much superior to mine, and I will contine to study
it in the hopes that I can get it to click in my head.
Second part is that the curve I'm looking for is not a straight
logarithmic curve. On a graph, it looks more like a stretched out "S".
The slope of the curve is more extreme in the center of the graph, with
the top end and the bottom end flattening out. I'm trying to remember
the actual way of getting this curve. It has something to do with the
area under a sine curve, or the slope of the curve. I will check into
this further to figure it out.
I figure that if I set the difference between the first and second point
on the S curve as "1" in my lookup table, I should be able to generate
preload values for the rest of the table. Of course, with only an 8bit
counter, I won't end up with smooth dimming, so eventually I will have
to expand to a higher bit value table. I guess I will have to figure out
how to do that somehow. Ah well, future project I suppose :)
Thanks,
Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
-Douglas Adams
> Well, if you're using a software PWM then a really simple solution may be
> to just let the time base be logarithmic. For example, suppose you had a
> PWM with 8 levels. Normally, the time step is constant so that a pulse of
> 4 units, is on exactly twice as long as a pulse of 2 units. In terms of a
> time line:
>
> Start
> V V
> +---+---+---+---+---+---+---+---+---+---+---+
> 0 1 2 3 4 5 6 7 0 1 2 3
>
> At the 0's the PWM turns on and at the 1-7 steps it turns off. Now,
> suppose you made the time step like so:
>
> ++-+--+---+------+---------+--------------+---------------------++-+
> 01 2 3 4 5 6 7 01 2
>
> So now, a pulse 4 units wide takes more than twice the time of a pulse 2
> units wide.
I don't remember how this thread started, so I might be missing details as
to what the final application; but for a simple 8 second bloom / dim routine
I did a while ago, 16 steps were more than was needed...
----- Original Message -----
From: "Jan-erik Svderholm (QAC)" <RemoveMEJan-erik.SoderholmTakeThisOuTPAC.ERICSSON.SE>
And the question is also, how many "steps" do you need
to get a smooth dim. Will you see the differense between
two steps in a 128 step dim ? Or a 64 step dim ? I'd guess
not.
>I tried it in columns first (seems more natural) but then
>the export to CSV (comma separated values) comes out wrong
>...it seems to write out all the columns for row 1, all
>the columns for row 2, etc...there may be some clever way
>around that...dunno....
Do not do an export. Just highlight the range you want to use, and do a
copy/paste into your asm source file, and it will have the appropriate
number of rows and columns pasted as CSV.
An export always does the whole sheet AFAIK.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
> Just glancing at the results in the attachment something seems not quite
right.
> There are fewer 1's than 2's at the start of the table (19 vrs 23)
> which seems contrary to a curve continuously gaining slope...
I hadn't noticed that before, but this is due to the way the output value
was rounded. The table equation maps the 1 input value to the 1 output
value exactly. This omits the output value range of .5 to 1, which also
would have been rounded to integer 1. The table value 2 results from raw
output values from 1.5 to 2.5, which causes more occurrences of 2. The
same is true at 255, but not as obvious due there being gaps between
integer output values.
If you really wanted to get rid of these artifacts (I don't think they
matter for the intended purpose), you would map input 1 to output 0.5 and
input 255 to output 255.5, making sure to clip the output integer to the
1 - 255 range.
*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
<snip idea about putting PWM routine in the main loop>
>
> So, will this work, and even if it will, is this feasible? I'm sure
> Scott's solution is much superior to mine, and I will contine to study
> it in the hopes that I can get it to click in my head.
The only problem with putting the PWM routine in the main loop is that you
have to be very careful in insuring that it takes a constant amount of
time to pass through your loop. I.e. you need to write isochronous code.
In my opinion, using the TMR0 interrupt is a MUCH cleaner way to achieve a
regular cadence.
> Second part is that the curve I'm looking for is not a straight
> logarithmic curve. On a graph, it looks more like a stretched out "S".
> The slope of the curve is more extreme in the center of the graph, with
> the top end and the bottom end flattening out. I'm trying to remember
> the actual way of getting this curve. It has something to do with the
> area under a sine curve, or the slope of the curve. I will check into
> this further to figure it out.
Monotonic functions like this can be accurately approximated with look up
tables. Take a look at the implementation of the arctan or log2 functions:
In general, if you have an equation that is monotonic:
y = f(x)
Where x and y are scaled to 8 bit unsigned integers, you can implement a
look up table with linear interpolation like so:
y:
SWAPF x,W ;The upper nibble is the index
ANDLW 0xf ;into the table
ADDLW 1
MOVWF temp ;Temporarily store the index
CALL y_table ;Get y2=f( (x>>4) + 1)
MOVWF result ;Store temporarily in result
DECF temp,W ;Get the saved index
CALL y_table ;Get y1=f( (x>>4) )
SUBWF result,W ;W=y2-y1, This is always positive.
SUBWF result,F ;y1 = y1 - (y1-W) = W
; now interpolate
CLRF temp ;Clear the product
CLRC
BTFSC x,0
ADDWF temp,F
RRF temp,F
CLRC
BTFSC x,1
ADDWF temp,F
RRF temp,F
CLRC
BTFSC x,2
ADDWF temp,F
RRF temp,F
CLRC
BTFSC x,3
ADDWF temp,F
RRF temp,W
ADDWF result,F
RETURN
; look up table
; insert your monotonic function here. The example below is for
; for the arctan function:
;
; i'th element = atan(i) * 256 / (pi/4)
;
; For an arbitrary monotonic function, y = f(x)
;
; i'th element = f(i) * 256 / max(f(x))
y_table:
ADDWF PCL,F
RETLW 0
RETLW 20 ;atan(1/16) = 3.576deg * 256/45
RETLW 41
RETLW 60
RETLW 80
RETLW 99
RETLW 117
RETLW 134
RETLW 151
RETLW 167
RETLW 182
RETLW 196
RETLW 210
RETLW 222
RETLW 234
RETLW 245
RETLW 0 ;atan(32/32) = 45deg * 256/45 = 256 = 0x100
END
What I'd suggest is starting off with your S curve and placing it into
this table.
> I figure that if I set the difference between the first and second point
> on the S curve as "1" in my lookup table, I should be able to generate
> preload values for the rest of the table. Of course, with only an 8bit
> counter, I won't end up with smooth dimming, so eventually I will have
> to expand to a higher bit value table. I guess I will have to figure out
> how to do that somehow. Ah well, future project I suppose :)
With the lookup table, you effective transform linear steps in time to
linearly (perceived increases in brightness). The best way to get a linear
ramp is by integrating a constant, or in terms of firmware, a phase
accumulator.
Ramprate, r = steps/time
Period, T = time/sample
iteration, i = 0,1,2, ... MAX, 0,1,2, ...
Ramp level, R
R = r * T * i
Or in software:
R = 0;
At a constate rate of once every t seconds compute:
R = R + r;
In a assembly, there are dozens of ways to implement this. For example,
let R be a 16 bit variable, and 'r' be an 8-bit one.
movf r,W ; ramp rate
addwf Rlo,F ; add the ramp rate to the current Ramp Level
skpnc ; propogate the carry to the high byte.
incf Rhi,F
This actually generates a "saw tooth" waveform. For example, if r=100,
then R will step 100, 200, 300, ... 65400, 65500, 64, 164, ... If yo want
a ramp that clamps the high byte of R to 0xff, then consider this:
movf r,W
addwf Rlo,F
rlf kz,W ; copy the carry into W. (kz = known zero)
addwf Rhi,F
skpnc ; If high byte rolls over
decf Rhi,F ; then clamp it at 0xff.
BTW, notice that the ramp rate depend on 'r'. For r=100, it takes about
655 iterations to reach the max. For r=1, it takes 65535.
Scott
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
> The only problem with putting the PWM routine in the main
> loop is that you
> have to be very careful in insuring that it takes a constant amount of
> time to pass through your loop. I.e. you need to write
> isochronous code.
Or you wait in the main loop until a timer reaches the next 'interval'.
More or less standard way of Jal main loop programming:
init_interval_1mS( 2 )
forever loop
next_interval -- this call waits until the next 2 ms interval
.. do a lot of work here ..
end loop
At 9:15 AM +0000 2/11/03, Alan B. Pearce wrote:
> >I tried it in columns first (seems more natural) but then
>>the export to CSV (comma separated values) comes out wrong
>>...it seems to write out all the columns for row 1, all
>>the columns for row 2, etc...there may be some clever way
>>around that...dunno....
>
>Do not do an export. Just highlight the range you want to use, and do a
>copy/paste into your asm source file, and it will have the appropriate
>number of rows and columns pasted as CSV.
>
>An export always does the whole sheet AFAIK.
hmmm...my paste seems to result in tab delimited numbers...but a
quick replace can fix that...easier than exporting...thanks!
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
At 9:12 PM -0700 2/10/03, Dal Wheeler wrote:
>I don't remember how this thread started, so I might be missing details as
>to what the final application; but for a simple 8 second bloom / dim routine
>I did a while ago, 16 steps were more than was needed...
>{Original Message removed}
At 9:35 PM -0600 2/10/03, Josh Koffman wrote:
>Second part is that the curve I'm looking for is not a straight
>logarithmic curve. On a graph, it looks more like a stretched out "S".
>The slope of the curve is more extreme in the center of the graph, with
>the top end and the bottom end flattening out. I'm trying to remember
>the actual way of getting this curve. It has something to do with the
>area under a sine curve, or the slope of the curve. I will check into
>this further to figure it out.
I don't understand why you want to use an "S" curve. I guess on the
high end at some point the eye runs out of range and no longer sees a
linear increase in apparent brightness with an exponential increase
in actual brightness. So for the entire range of vision the curve
may look like an "S".
But I doubt your LED will ever get that bright, so the appropriate
transfer function to use is a simple exponential curve. I mean I've
actually done this, and it sure looks right to me. I haven't,
however, used super-duper high efficiency ultra-bright LEDs...maybe
they *can* reach the upper brightness limits of vision...I don't
know. For the more typical LED's I've used, however, the simple
exponential curve works fine.
(For those who may be wondering, my application is an artwork, so
being really fussy about avoiding visual steps, making the increase
*look* linear, etc really is an appropriately fussy exercise. If I
was designing a more typical display for an appliance or the like
"good enough" would be easier I think...)
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
Well, as the end result will likely involve some sort of colour mixing
using different coloured LEDs, I want as many steps as possible, thus
giving greater mixing options.
Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
-Douglas Adams
Dal Wheeler wrote:
>
> I don't remember how this thread started, so I might be missing details as
> to what the final application; but for a simple 8 second bloom / dim routine
> I did a while ago, 16 steps were more than was needed...
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
Perhaps you know, but there actualy exists what's
called "Full Colour RGB LEDs". 4 LED's and 6 pins
in a standard 5mm LED package. Not cheap, but...
(Farnell order code 621-407 or 621-419)
Jan-Erik.
Josh Koffman wrote :
>Well, as the end result will likely involve some sort of colour mixing
>using different coloured LEDs, I want as many steps as possible, thus
>giving greater mixing options.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
> Well, as the end result will likely involve some sort of colour mixing
> using different coloured LEDs, I want as many steps as possible, thus
> giving greater mixing options.
I don't know that theory is going to help you very much. Color selection
tends to be pretty subjective, and people don't say "I need a 23% blue, 12%
red, 2% green, adjusted for logarithmic response", they twiddle the knobs
until the color is "right." Whether the brightness response to the knobs is
linear or logarithmic or something else may not be relevant.
Back in the early days of computer graphics, our class had this lovely high
resolution (360x240?) color display with 12 bits per color of intensity.
All data downloaded via serial line. Picking a color was a real chore,
and one of the "projects" suggested for the class was to implement some
color picking software...
Nowdays, there are standards (?) for this sort of thing, and maybe you
should think about how to map one of those standards into LED PWM instead
of involving human physiology at all...
BillW
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
I'm guessing isochronus code is code that will always execute with the
same number of total cycles, regardless of choices made in the code? I
don't think this would be too much trouble on my part, but I can see how
a TMR interrupt might be easier. However, I've started eyeing the SX
chips since I have a feeling that with all the extra stuff that's going
on on the chip I'm going to end up running slow in regards to some of
the things I need to do. So my worry is that relying on a TMR might not
work as well on such a fast chip. It might come too quick. I guess I'll
just use a prescaler. Would my idea of loading a value into the TMR
register to end up with a non linear interrupt rate work? I would
probably use a lookup table as most of this math is zooming right over
my head. And then the PIC implementations of the math is about 30,000
feet over my head.
So here's where I am now. I've turfed the "S" curve for now. I think you
all are right, and I just need a logarithmic curve. If I use a lookup
table to get a logarithmic value to preload my TMR, I should be able to
vary the time between interrupts. So my problems now are how to get this
lookup table. In order to increase resolution and not have to round and
end up with less than 256 steps, I want to use a 12, 14, or 16 bit
lookup. It will likely be a 16 bit lookup, as then I can load a 16 bit
timer easier. So the question is how do I get the values? Here is one
way I have thought of. I don't know how to do this using pure math, but
I have a friend who should be able to help me out in that regards. I am
just trying to figure out if the theory is valid. Basically, I plot a
graph of a logarithm, with the origin at 0,0, and the end at 65535,Y
Then I divide the X axis into 255 parts (so I end up with 256 numbers).
The Y value at each one of the corresponding 256 X values would be the
number I put in my lookup table?
I know this won't be the most efficent way of figuring all this out. If
I understood it, I could try to use Scott's log functions, but honestly,
I'm lost, and the visual way seems to make sense in my mind.
Next problem will be if I move to the SX chip. It only has an 8 bit
timer, so I guess I'd have to construct a 16 bit timer, or else I'll run
into only 8 bit resolution and rounding issues again.
I don't think I fully understand phase accumulators. Is there a tutorial
or something about them somewhere? I seem to recall a mention somewhere
in the archives, but due to a problem at the moment I can't search the
archives.
How does all that sound? Thanks everyone for your help. I really do
appreciate it.
Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
-Douglas Adams
Scott Dattalo wrote:
> The only problem with putting the PWM routine in the main loop is that you
> have to be very careful in insuring that it takes a constant amount of
> time to pass through your loop. I.e. you need to write isochronous code.
> In my opinion, using the TMR0 interrupt is a MUCH cleaner way to achieve a
> regular cadence.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
> So here's where I am now. I've turfed the "S" curve for now. I think you
> all are right, and I just need a logarithmic curve. If I use a lookup
> table to get a logarithmic value to preload my TMR, I should be able to
> vary the time between interrupts. So my problems now are how to get this
> lookup table. In order to increase resolution and not have to round and
> end up with less than 256 steps, I want to use a 12, 14, or 16 bit
> lookup. It will likely be a 16 bit lookup, as then I can load a 16 bit
> timer easier. So the question is how do I get the values?
Josh, several people have already shown you the math, posted code to
compute a table, and even posted sample tables. What more do you want?
Note that the program I posted had constants for the number of bits in the
table input and output words.
*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com
Ok, admittedly perhaps I didn't word my post that clearly. The question
that you quoted was not in fact meant to be answered. I use questions to
illustrate where I am in the thinking process. The next section of that
post described a way that I thought I could use to generate the lookup
table values needed. All I was looking for was verification of whether
or not my theory was sound. However, I haven't received a yay or nay on
it, so I am looking back to other ways people have proposed, regardless
of whether I understand them or not.
I have been looking at Olin's program to get MPASM to generate a table.
However, I can't get it to work. I put the text in a file, ran MPASM on
it, yet got tons of errors. Many invalid characters, invalid operands,
etc. How do I get it to run correctly? Do I need to do it within MPLAB?
I'm a bit lost here (which is really the reason I tried to find my own
way to generate the numbers).
Thanks for your help,
Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
-Douglas Adams
Olin Lathrop wrote:
> Josh, several people have already shown you the math, posted code to
> compute a table, and even posted sample tables. What more do you want?
> Note that the program I posted had constants for the number of bits in the
> table input and output words.
Josh Koffman wrote:
> Ok, admittedly perhaps I didn't word my post that clearly. The
> question that you quoted was not in fact meant to be answered. I use
> questions to illustrate where I am in the thinking process. The next
> section of that post described a way that I thought I could use to
> generate the lookup table values needed. All I was looking for was
> verification of whether or not my theory was sound. However, I
> haven't received a yay or nay on it, so I am looking back to other
> ways people have proposed, regardless of whether I understand them or
> not.
>
> I have been looking at Olin's program to get MPASM to generate a
> table. However, I can't get it to work. I put the text in a file, ran
> MPASM on it, yet got tons of errors. Many invalid characters, invalid
> operands, etc. How do I get it to run correctly? Do I need to do it
> within MPLAB? I'm a bit lost here (which is really the reason I tried
> to find my own way to generate the numbers).
>
> Thanks for your help,
Josh, everyone keeps guessing how you put the text into the file, and how
the text looks like, it can be a simple little mistake, that could be
easily identified if you post that part of the file in here, so we could
see it. Cut and paste it into the next reply.
On Wednesday 12 February 2003 01:53 pm, you wrote:
> I have been looking at Olin's program to get MPASM to generate a
> table. However, I can't get it to work. I put the text in a file,
> ran MPASM on it, yet got tons of errors. Many invalid characters,
> invalid operands, etc. How do I get it to run correctly? Do I need
> to do it within MPLAB? I'm a bit lost here (which is really the
> reason I tried to find my own way to generate the numbers).
I don't believe that Olin's program was a MPASM program. He didn't say
it was. He said:
> I wrote a program to generate an MPASM table of logarithmic values
Judging by the format and the syntax, I'd say it was some kind of
Pascal or Pascal variant.
> I have been looking at Olin's program to get MPASM to generate a table.
> However, I can't get it to work. I put the text in a file, ran MPASM on
> it, yet got tons of errors. Many invalid characters, invalid operands,
> etc. How do I get it to run correctly? Do I need to do it within MPLAB?
> I'm a bit lost here (which is really the reason I tried to find my own
> way to generate the numbers).
The program I posted was in Pascal and meant to run on a regular computer.
It's only connection to MPASM is that its output is meant for MPASM as
input. I posted the source code to illustrate the algorithm, and didn't
expect anyone to run it directly. It's so simple that I figured if
someone wanted it, they could re-code it in their favorite high level
language.
I like to use a variant of Pascal derived from Apollo Domain/OS Pascal.
It has a lot of advantages over C, like real type checking, SET data
types, pass by value/reference and in/out defined in subroutine
declarations, better handling of native data types accross platforms, etc,
etc, etc. No compiler exists for this language. It is translated for
different target compilers on different platforms. On the PC the target
language is C using the Microsoft Visual C++ compiler. Below is both the
original Pascal source code and its translation to MSVC++. You can
probably use the C directly if you remove the call to STRING_CMLINE_SET.
That gets inserted automatically by the translator, but I don't think the
features it provides are used in this tiny program.
Here is the Pascal source again:
{ Program BR
*
* Write a linear to logarithmic lookup table in PIC
* assembler format.
}
program br;
begin
writeln (' retlw 0 ; 0'); {first entry defined explicitly}
ov := 1.0; {init output value for first entry}
for iv := 1 to max_in do begin {once for each remaining entry}
writeln ( {write this table entry}
' retlw ', round(ov):3, ' ;',iv:3);
ov := ov * m {make value for next table entry}
end; {back to do next table entry}
end.
And here is the MSVC++ C source code for Windows derived from above:
On Wednesday 12 February 2003 02:50 pm, you wrote:
> The program I posted was in Pascal and meant to run on a regular
> computer. It's only connection to MPASM is that its output is meant
> for MPASM as input. I posted the source code to illustrate the
> algorithm, and didn't expect anyone to run it directly. It's so
> simple that I figured if someone wanted it, they could re-code it
> in their favorite high level language.
And here's the same thing in Ruby (http://ruby-lang.org), a
cross-platform language that's great to use for this kind of thing:
max_in_k = 255
m_k = 1.022056
print(" retlw 0 ; 0\n")
ov = 1.0
(1 .. max_in_k).each do | iv |
printf(" retlw %3d ;%3d\n", (ov+0.5).floor, iv)
ov *= m_k
end
Thank you for the C++ code, I was wondering where all those new MPASM
directives came from. I fed it to Visual C++ 6, with limited success. If
I don't modify the values, I can get it to give me a 0-255 range 255
entry table. What I need is a 0-65535 range 255 entry table. So, first I
modified the max_out_k value to 65535. This didn't do anything, at which
point I realized the C++ code wasn't calculating the value of m_k, it
was in constant form (I guess as a result of the translation between
languages). So, I did the calculation in the Pascal code, and got a
value of 258.01129, which I then defined as m_k. Now when I execute the
code, I get about 3 relevant values, then a bunch of values way out of
my range, then the locations from 9 to 255 are 0.
I don't program in C, and to be honest I have no idea how this code
works. I thought I had figured it out with the constant calculation that
I did by hand, but clearly I didn't. Then again, perhaps just my
constant calculation was wrong. Here is the equation from the pascal
source:
m = max_out ** (1.0 / (max_in - 1));
Does anyone have any idea what I'm doing wrong? I'll leave the C++ code
attached, with my modifications. As per Olin, I've chopped the
translation specific code piece.
I'm sorry to keep bugging the list about this, but all this high math is
above my head. In my mind, the graphing solution I figured out clicks,
but I don't know if this will give me the correct values.
Thanks,
Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
-Douglas Adams
part 1 754 bytes content-type:text/plain; (decoded 7bit)
On Wednesday 12 February 2003 06:22 pm, you wrote:
> Does anyone have any idea what I'm doing wrong? I'll leave the C++
> code attached, with my modifications. As per Olin, I've chopped the
> translation specific code piece.
>
> I'm sorry to keep bugging the list about this, but all this high
> math is above my head. In my mind, the graphing solution I figured
> out clicks, but I don't know if this will give me the correct
> values.
Attached is the Ruby source plus the resultant table.
Thank you Ned :) This is exactly what I needed...for now :)
Any idea why my modification of the C++ code didn't work?
Josh
--
A common mistake that people make when trying to design something
completely foolproof is to underestimate the ingenuity of complete
fools.
-Douglas Adams
Ned Konz wrote:
>
> Attached is the Ruby source plus the resultant table.
Time to move this issue to some C/C++ mail-list, perhaps ?
>Any idea why my modification of the C++ code didn't work?
Hopefully not.
Just mayhaps to [EE]:
IMHO, of course.
This is (possibly :-) ) a general discussion about mapping using tables or
otherwise to transform an N>8 bit address value sparsely populated data
range into an 8 bit address space. As such it is microprocessor relevant
even if not especially PIC. Any implementation difficulties, whether in
assembler or eg C, but especially in a HLL art liable to reflect a
misunderstanding of what is being done and so are liable to be instructive.
(difficulties in assembler are more liable to be difficulties with the
language ;-) ).
> So, I did the calculation in the Pascal code, and got a
> value of 258.01129, which I then defined as m_k.
I don't know how you managed that, but I get 1.044630 from my trusty HP
11C calculator. This value is the ratio from one output step to the next,
so it should be a little larger than 1. This should give you a reasonable
table most of the way, but you will probably end up with roundoff error by
the time you get to the end.
> I don't program in C, and to be honest I have no idea how this code
> works.
> ...
> but all this high math is above my head.
Actually, Josh, this is basic high school math. Unless you actually are a
high school student and haven't gotten to logarithms and exponentials yet,
you have no business trying to be an engineer. How old are you? What
kind of education have you had?
I suggest you learn about logarithms and exponentials before proceeding.
You might want to learn a bit about C or some other high level programming
language while you're at it. As long as you know one of them reasonably
well, you can look at code written in another language and usually figure
out what it's doing. C is actually one of the harder ones to guess at if
you're not familiar with it. You would probably have an easier time
trying to understand the algorithm from the Pascal I sent.
*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com
Actually, Josh, this is basic high school math. Unless you actually
are a high school student and haven't gotten to logarithms and
exponentials yet, you have no business trying to be an engineer.
You can get through high school math, with good grades, without gaining
much of an understanding about how logarithms get applied to the real
world. More people tend to forget their basic calculus, "required" in
college but never really used. I know that the last time I looked at my
college physics text, there were an awful lot of symbols that I don't
remember how to work with (gradients and laplacians and ... stuff.)
One need not be "trying to be an engineer" to want to play with PICs and
pretty colored LEDs. If an artist (for example) gets led into technical
issues in their search for an effect, that's OK. If they end up
understanding that there's method and science behind the scenes, that's
good (IMO), and if they end up actually digging in and relearning their
"high-school-level math", that's quite excellent.
Historically, this has been more common in analog electronics and music,
I think. But I don't think that us "REAL Engineers" ought to be too upset
at having to explain things every once in a while. It can be better than
being asked to IMPLEMENT an effect at a level we don't understand either.
So...
We're trying to get preceived linear (smooth) brightness changes in a LED
by matching the (supposedly) logarithmic response of the human eye/etc.
A "logarithmic series" is one where the next value in the series is
calculated by multiplying the previous value by a constant.
S0 = A
S1 = S0 * C = A * C
S2 = S1 * C = A * C * C
S3 = S2 * C = A * C * C * C
:
Sn = A * C^N
So if S0 = 1, and Sn = 255, for instance, we know that A = 1, and 255 =
C^n, where N is the number of steps in the series. To solve for C, take
the Nth root of both sides C = root(255, N) An Nth root is equivilent to
raising to the power 1/N (this is derived from logarithm theory), and this
is the "m" factor that Olin's program calculates, more or less:
m = max_out^(1.0/max_in - 1)
m = 255^(1/254)
Thank You, Mr. Westfield.
Your explanation was quite enlightening, and leaves
me with more understanding and a lot less "smoke"
on the subject.
Perhaps trying to get an education via Outlook
and Explorer is not the best way, but it does
have it's advantages.
So in essence, we want to influence a main value by a large value at first,
then less and less, or start small and go bigger. The way we increase the
value we add/remove from our main number, and how we let it inlfuence it
creates a non-linear curb that, when translated to brightness in a led, may
or may not look linear to our naked eye.
Even my sister at age 12 understands it when put like this.
You don't need a table either. To get a more advanced shape one adds more
variables to the mix.
If you have A that increases slowly, but starts at a high level, and B that
increases exponentially but start at 0, and do C(n)=C(n-1)+A-B, you can get
a lot of interesting curves. Adding move variables or adjusting their
curves will give even more. If you need a simple curb with a typical
logarithmic-type fade in/out a tiny function can do the trick.
But, a table will be faster and only use program-memory instead of RAM...
Olin, since you've written the code to calculate the
dimming table, could you please enhance it to toggle
some PC Com port output line - DTR or RTS according
to this table. "Port" instruction in Pascal, if I recall
correctly.
Anybody can connect the LED with resistor and diode
to one of these lines.
*>Olin, since you've written the code to calculate the
*>dimming table, could you please enhance it to toggle
*>some PC Com port output line - DTR or RTS according
*>to this table. "Port" instruction in Pascal, if I recall
*>correctly.
*>Anybody can connect the LED with resistor and diode
*>to one of these lines.
And Windows will provide for the flickering candle effect as a free bonus.
Peter L. Peres wrote:
> >Olin, since you've written the code to calculate the
> >dimming table, could you please enhance it to toggle
> >some PC Com port output line - DTR or RTS according
> >to this table. "Port" instruction in Pascal, if I recall
> >correctly.
> >Anybody can connect the LED with resistor and diode
> >to one of these lines.
>
> And Windows will provide for the flickering candle
> effect as a free bonus.
Pascal under MS DOS (or over MS DOS - depends on a
person's attitude towards MS).
Mike.
--------------------
P.S. Hey Peter. Look at a clock; It's a sleeping time in your
country longtitude.
Comrade Singer (RemoveME1073394263TakeThisOuTspamkgb.cccp.nyet) pontificated:
> Olin, since you've written the code to calculate the
> dimming table, could you please enhance it to toggle
> some PC Com port output line - DTR or RTS according
> to this table. "Port" instruction in Pascal, if I recall
> correctly.
> Anybody can connect the LED with resistor and diode
> to one of these lines.
Actually it's not that easy to toggle DTR and RTS. This code sits atop an
operating abstraction layer, so it can't just do a PORT or similar (which
this Pascal doesn't have anyway). This program is so small, you can
recode it in whatever your favorite language is and do it yourself.
Besides, I wouldn't want to do it this way. Windows is not a real time
operating system, and you would probably get rather erratic LED
brightness. If you want to control the brightness of an LED from a PC,
send commands to a PIC and have it control the LED. A good place to start
would be my HOS project at http://www.embedinc.com/pic/hos.htm. An easy
way to experiment with the table mapping would be to send the linear 0-255
raw PWM duty cycle to the PIC, and compute the apparent to linear
brightness mapping on the host.
*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
Olin Lathrop wrote:
> Actually it's not that easy to toggle DTR and RTS. This code sits
atop an
> operating abstraction layer, so it can't just do a PORT or similar
(which
> this Pascal doesn't have anyway). This program is so small, you can
> recode it in whatever your favorite language is and do it yourself.
>
> Besides, I wouldn't want to do it this way. Windows is not a real
time
> operating system, and you would probably get rather erratic LED
> brightness. ...
"Les Mots" (Jean-Paul Sartre). Did anybody ever measure
the non-stability of this delay?
How about to connect RTS(output) to CTS(input) and then
toggling "on" RTS, and on receiving CTS change toggle "off"
RTS, then again on receiving CTS change toggle RTS "on"
and so on.
How should it look like on a scope?
What would show programmatically counting delay between
RTS togglings?
Thank you.
Mike.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
Peter L. Peres wrote:
> >P.S. Hey Peter. Look at a clock; It's a sleeping time in your
> >country longtitude.
>
> Sleep ? You mean you can sleep ? At night ? Really ? Wow.
> Where do I apply to immigrate to your country ?
Mongolia is better for sleeping, Peter.
There is an expert on Outer Mongolia among PICList members.
Olin wrote in "Re: [PIC]: Tost when using intosc on 12F629 ?" :
"I guess it's real important that a herdsman in Outer Mongolia
be able to count yaks on his computer..."
So, don't forget your computer and your APC UPS when
relocating.
Good luck.
Mike.
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
*>> And Windows will provide for the flickering candle
*>> effect as a free bonus.
*>
*>Pascal under MS DOS (or over MS DOS - depends on a
*>person's attitude towards MS).
No, it depends on whether DOS or windows decides to update the clock or
whatever once every 60 timer interrupts or so. I once made a direct drive
stepper like that and it made nice tick stops every half second or so. I
know that the timer and ISR can be augmented etc. I have done that too.
The required programming effort is impressive. Do not mistake my attitude
towards certain things. I used to program under MSDOS for serveral years,
that's where the attitude *comes* from.
Peter
-- http://www.piclist.com hint: PICList Posts must start with ONE topic:
[PIC]:,[SX]:,[AVR]: ->uP ONLY! [EE]:,[OT]: ->Other [BUY]:,[AD]: ->Ads
> "Les Mots" (Jean-Paul Sartre). Did anybody ever measure
> the non-stability of this delay?
> How about to connect RTS(output) to CTS(input) and then
> toggling "on" RTS, and on receiving CTS change toggle "off"
> RTS, then again on receiving CTS change toggle RTS "on"
> and so on.
> How should it look like on a scope?
> What would show programmatically counting delay between
> RTS togglings?
I don't know, but you could certainly try it yourself if you really wanted
the answer. I have personally observed Windows 2000 going out to lunch
for 100 - 200mS at a time for no apparent reason. One of my customers was
(despite my advice) trying to use Windows 2000 to control some industrial
equipment with a loop time of 30mS. It worked fine most of the time, and
the CPU useage was only a few percent, but there was a hiccup every once
in a while. Despite shutting down all other processes we knew about,
making sure there was plenty of real memory, etc, it would still happen
every few minutes to every few seconds. The probability of occurrence
could be greatly increased by just dragging the mouse around without
hitting any buttons, even over the desktop.
*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com
>> "Les Mots" (Jean-Paul Sartre). Did anybody ever measure
>> the non-stability of this delay?
>> How about to connect RTS(output) to CTS(input) and then
>> toggling "on" RTS, and on receiving CTS change toggle "off"
>> RTS, then again on receiving CTS change toggle RTS "on"
>> and so on.
>> How should it look like on a scope?
>> What would show programmatically counting delay between
>> RTS togglings?
>
> I don't know, but you could certainly try it yourself if you really
> wanted the answer. I have personally observed Windows 2000 going out
> to lunch for 100 - 200mS at a time for no apparent reason. One of my
> customers was (despite my advice) trying to use Windows 2000 to
> control some industrial equipment with a loop time of 30mS. It
> worked fine most of the time, and the CPU useage was only a few
> percent, but there was a hiccup every once in a while. Despite
> shutting down all other processes we knew about, making sure there
> was plenty of real memory, etc, it would still happen every few
> minutes to every few seconds. The probability of occurrence could be
> greatly increased by just dragging the mouse around without hitting
> any buttons, even over the desktop.
That's the thing.
If I had enough money, I would sue Microsoft for stealing processor cycles
from my machine.
But then, If I had enough money, I would be running a nice Sun machine, and
not even remembering that Microsoft exist.
Microsoft is failing in a tremendous way regarding industrial application.
There is not a simple way to dedicate all the processor power to one
application, you will have always the Microsoft lack of knowledge of real
multi-task operation at your ankles. If there is just one application
running, why in the heck the processor needs to keep being interrupted? to
check if there is another application trying to launch? and what about it I
want to lock just one application? simply can't.
It is messing up with several, thousands, millions of worldwide
applications that depend on specific timed pulses on ports (most LPT
ports). It is ridiculous to force customers to keep running old versions
of Windows or even DOS platform. In some way, Microsoft is putting our of
business several old partners, people who developed applications under
windows, and now it simply doesn't work under the new versions.
I think Microsoft is spitting over their own food plate. By dedicating all
the attention to office and corporative solutions, Microsoft is just
pushing away the only thing that can ruin then in a long shot; industrial.
Industrial application is about reading input and controlling output ports.
If Microsoft could allow us to lock easily a single application, it would
expand any Windows platform (mostly the newly that would still generating
megadollars to the already filthy rich bill's bank account) to the
industrial area, that is not small. Lets remember that "industrial" can
means "home-small-industrial" also.
The only explanation I see, is that even Bill doesn't trust Windows
platform for industrial application. When you crash your Word you just
blame Bill and restart. If Kodak crashes the sensitive chemical film
temperature application, it can means many thousands of dollars lost, and
bill will not want to be sued hundreds of times a day...
*>Microsoft is failing in a tremendous way regarding industrial application.
*>There is not a simple way to dedicate all the processor power to one
*>application, you will have always the Microsoft lack of knowledge of real
*>multi-task operation at your ankles. If there is just one application
*>running, why in the heck the processor needs to keep being interrupted? to
*>check if there is another application trying to launch? and what about it I
*>want to lock just one application? simply can't.
Read about the Microsoft scheduler and how it bumps priorities of idle
processes from time to time and event-driven. I understood what it does
after reading that info. Then I understood that I was trying to build
something impossible because of that scheduler. It is a totally
nondeterministic state machine. You have to read about how it works to
understand.
Now you can panic. Any timing-sensitive applications fielded ? <insert BSD
logo with sweet smile on face here>
*>It is messing up with several, thousands, millions of worldwide
*>applications that depend on specific timed pulses on ports (most LPT
*>ports). It is ridiculous to force customers to keep running old versions
*>of Windows or even DOS platform. In some way, Microsoft is putting our of
*>business several old partners, people who developed applications under
*>windows, and now it simply doesn't work under the new versions.
Ah, yes, the scheduler works differently under 95, 98, 2k and XP. That is
also explained in the same document.
*>temperature application, it can means many thousands of dollars lost, and
*>bill will not want to be sued hundreds of times a day...
True, that's why they have the EULA. Read it carefully. Free software may
come without warranty etc but some M$ products legally and certifiedly
come without it imho.
BTW on Linux the scheduler can be manipulated to do plain Round-Robin
preemptive multitasking and other things and there are real time kernel
patches.
> If I had enough money, I would sue Microsoft for stealing processor cycles
> from my machine.
>
> ...
>
> Microsoft is failing in a tremendous way regarding industrial application.
> There is not a simple way to dedicate all the processor power to one
> application, you will have always the Microsoft lack of knowledge of real
> multi-task operation at your ankles. If there is just one application
> running, why in the heck the processor needs to keep being interrupted?
to
> check if there is another application trying to launch? and what about it
I
> want to lock just one application? simply can't.
Right. Microsoft has always been quite clear that Windows 2000 is not a
real time operating system. They don't claim it will do the things you are
asking for. The problem is with you, not them. If you want to have RTOS
features go get an RTOS, but please spare us the whining that your hammer
isn't much good as a screw driver.
*****************************************************************
Embed Inc, embedded system specialists in Littleton Massachusetts
(978) 742-9014, http://www.embedinc.com
Peter wrote:
> BTW on Linux the scheduler can be manipulated to do plain Round-Robin
> preemptive multitasking and other things and there are real time
kernel
> patches.
Right, and they may become a permanent part of the kernel since they
speed everything up. ;-)
Olin Lathrop wrote:
> ...you could certainly try it yourself if you really wanted the
answer.
> I have personally observed Windows 2000 going out to lunch
> for 100 - 200mS at a time for no apparent reason. One of my
> customers was (despite my advice) trying to use Windows 2000
> to control some industrial equipment with a loop time of 30mS.
...
If I didn't really want the answer, why would I bother people?
I connected RTS to CTS on COM2, wrote a code in VBA in
ExcelXP workbook to test the speed of this loopback.
Without additional CPU load it goes at 500 loops per second.
When CPU is loaded 100% the speed drops on average to
about 30 loops per second.
Load was: conversion big .wav files to .mp3 in Cool Edit 2000.
Hardware: CPU - Celeron 1700, MB - I845AD, RAM - 256DDR.
Mike.
---------------------------------------------------
Code below is to test RTS - CTS loopback speed; is to be placed
into Excel sheet's VBA page (Open VBA editor, click Sheet1).
Also you need to place MSComm control (MSComm1) and
Command Button control (cmdStart) on the sheet.
(Simbol " ' " is to start a string of comments (for those who
aren't familiar with VBA.))
----------------------------------------------------
' Variable to be incremented on each step of the looping
Private lngCounter As Long
Private blnTestIsRunning As Boolean
' Windows launches this Sub when something
' changed in COM Port
Private Sub MSComm1_OnComm()
Select Case MSComm1.CommEvent
Case comEvCTS
' Change in the CTS line happened.
HandleChangeInCTS MSComm1
End Select
End Sub
'Sub to handle change in CTS line
Private Sub HandleChangeInCTS(oMSComm As MSComm)
DoEvents
With oMSComm
'Toggling RTS on/off
If .RTSEnable = True And blnTestIsRunning Then
.RTSEnable = False
Else
.RTSEnable = True
End If
lngCounter = lngCounter + 1
' Cell in row 1 and column 2 shows current value
' of lngCounter
Cells(1, 2) = lngCounter
End With
End Sub
' Command button cmdStart is used to start and
' stop testing.
Private Sub cmdStart_Click()
With MSComm1
If blnTestIsRunning = False Then
' Button was pressed to start the test
*>Peter wrote:
*>> BTW on Linux the scheduler can be manipulated to do plain Round-Robin
*>> preemptive multitasking and other things and there are real time
*>kernel
*>> patches.
*>
*>Right, and they may become a permanent part of the kernel since they
*>speed everything up. ;-)
Not really. If you have tasks running with nice 20 that run for 3-4 days
you do not want a rtk kernel.