## converting to Percentage value ??

30 posts / 0 new
Author
Message

Hi

My problem is that i dont now how to convert values.

the thing is that i sample a value from AD and bit shift it so i only have 9 bits "dec 509"

i like to calculate the reading so i get a Percentage value of the reading.

the Percentage value shold then controll a motor stepper controller, and i like to set the max stepp/min = 100%.

But i dont now how to do it ?

i tryed to use flote, to convert but with no sucsses

float X_pos;

Try making that 1024 into 1024.0

As it stands 1024 is an INTEGER constant so it'll do integer maths and therefore the bit in parenthesis will always be 0.

But you don't need to use float here. Why not just re-order it to multiply the integer value 'ADC_read' by 100 and THEN divide by 1024 to get an integer result.

(you'll want to use longs rather then ints though because the max value, 1023 would be 1023 * 100 / 1024 and 1023 * 100 = 102,300 does not fit in an unsigned int that only oes up to 65536)

Cliff

Quote:

But you don't need to use float here. Why not just re-order it to multiply the integer value 'ADC_read' by 100 and THEN divide by 1024 to get an integer result.

(you'll want to use longs rather then ints though because the max value, 1023 would be 1023 * 100 / 1024 and 1023 * 100 = 102,300 does not fit in an unsigned int that only oes up to 65536)

Maybe >>you<< would use longs. ;) I might multiply by 25 and then divide by 256. I might even allow the rounding in many apps, and just divide by 10. Or use 25/255 = 5/51. Or have the hardware circuit scale the input from a few counts to about a 1000 counts. (That said, with the last and a "calibrated" circuit the intermediate long is often the right way to go to do a y=mx+b.)

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

hi and thanks.

it worked well white

then i get a value betwen 0 - 100.

but is ADC >> 7; right ?? it feels like its to meny shifts, 7 times ?? is that right ??

// thanks

`ADC_read = ADC >> 7; // converts 10bit to 9 bits `

That will convert 10 bits to 3 bits !!

I think you mean >>1 in fact.

The following is the opening text of a project that I am currently working on. While the range (Y axis) & domain (X axis) are not the same as those that you are using, you can use the Y = mX + b equation for the line of a linear graph to easily solve your scaling problem.

Look thru some of my old (one to two years ago) and you'll find that I have covered this concept on several occaisions.

```/*
The goal...
This PWM control system is set up to use the "Locked Anti-phase" method of motor control.  That is, the motor is in a constant change of diredtion but, at or above 10KHz.

"Locked Anti-phase" allows the motor to be driven to the target velocity.  That is, when the target velocity changes from one velocity value to some lower velocity value, the motor does not coast from the higher velocity to the lesser velocity.  Instead, the motor is driven to the lower velocity from the higher velocity.  Restated, "Locked Anti-phase" can be viewed as a method of employing full time "Dynamic Breaking".

The correct display of motor velocity (in percent of maximum velocity) will require unique calculations to correctly display motor velocity.
To convert the ADC value to +/-100 percent (based on an OCR1A mid-point or zero motor velocity value of 511) we will use the following:

Y_range  = -100% <= Y <= +100%
X_domain = 0 <= X <= 1023

As this is a linear transfer function, we will use the equation for the line of a linear graph:

Y = mX + b

Where:
m = (dY / dX) = (Y2 - Y1) / (X2 - X1), b = Y axis offset
m = (100 - (-100)) / (1023 - 0) = 0.195503

To maintain accruacy, the slope (m) will be multiplied by 1000000,
equaling:
0.195503 * 1000000 = 195503, hense the dinominator of 1000000
in the equation below.

The final equation used for the display of the motor velocity is:
Y = ((195503 * ADC) / 1000000) - 100;

Data type "long int" will be used in the motor velocity calculation.

Below is a graphical representation of the Y = mX + b equation.

Y2   100 |                 +
|               +
|             +
Percent |           +
Motor  |         +
Speed  |       +
|     +
|   +
| +
Y1 -100 + - - - - - - - - -
0                1023

*/```

Edit:
Fixed Numerical errors.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Sun. Jul 22, 2007 - 02:00 AM

Carl--

If your Y domain is +100 to -100 and you use a range of 200, why don't you use a range of 1023 for your X range since it cgoes from 0 to 1023?

[I always confuse myself on that one. Each ADC "step" is, in fact, 1/1024 of Vref. So is that 1023 steps or 1024?]

Anyway, you went the long way around and did a 200/1024 = 0.19... Then you used a huge fractional multiply "to preserve accuracy". But you don't need those big numbers-- it is still 200/1024. And as mentioned above, that is 50/256 or 25/128. Using 25/128 15 bits (plus sign) can be used.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

theusch wrote:
Carl--

If your Y domain is +100 to -100 and you use a range of 200, why don't you use a range of 1023 for your X range since it cgoes from 0 to 1023?

Actually, the Y axis is the range and the X axis is the domain.

theusch wrote:
[I always confuse myself on that one. Each ADC "step" is, in fact, 1/1024 of Vref. So is that 1023 steps or 1024?]

I also debated with myself as to whether or not I should use 1023 or 1024 in the domain (X axis) maximum. I finally decided and verified that for display purposes, it had no significant effect. I stuck with 1023 because it is mathematically correct.

That is, as the number zero is a valid place holder in the number line, it must be included in the equation and, this is the reason why the maximum value of any N-bit number is always (2^n)-1, giving a range of 0 to 1023 for a 10 bit ADC.

Therefore, a 10 bit ADC can have a maximum of 1024 possible values in the range of 0 to 1023 - where, 0 is inclusive.

And, using 1024 did offset the motor torque slightly at zero percent velocity; though, there was no actual motor movement.

theusch wrote:
Anyway, you went the long way around and did a 200/1024 = 0.19... Then you used a huge fractional multiply "to preserve accuracy". But you don't need those big numbers-- it is still 200/1024. And as mentioned above, that is 50/256 or 25/128. Using 25/128 15 bits (plus sign) can be used.

Lee

But... 200/1024, 50/256 & 25/128 gives a fractional number as a result. How can you maintain a fractional number with integer math, without guarding against truncation (in this case leading to m = 0 and, 0X = 0 for each possible ADC value) of fractional results and, without multiplying that fractional number by some multiplier? I know that the range and domain of this function aren't ever going to change so, I multiplied a constant representing the slope (m = 0.195503) of the line of the graph by 1000000, yealding 195503 - thus maintaining the accuracy and providing several ADC counts per least significant (units digit) count of the percent display value. I could have thrown out a few decimal digits of accuracy of the slope (m) but, I didn't want any bobble in the percentage reading on the LCD display.

At this stage of my servo (not the hobby type) motor project, the code is working exactly as calculated, predicted and required - long way around or not. The fact is, anyone who took elementary algebra should know the concepts of Y = mX + b. That is why I documented this part of the project the way I did. And if they don't, they can easily review an elementary algebra text book to gain an understanding of the concepts involved. I can't assume everyone will understand tricks and short cuts.

In fact, if you look at all of the mathematical discussions that I have been involved with on the AVRFreaks forum, I have always explained mathetical concepts from a textbook aproach. If someone understands the concepts of a problem from a mathematically structured point of view, they can then apply their own tricks and short cuts. I perfer not to and choose to leave that up to individual taste.

Long way or not, I will continue to use textbook methods to explain mathematical concepts.

EDIT:
Corrected numerical errors.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Sun. Jul 22, 2007 - 02:19 AM

Quote:

But... 200/1024, 50/256 & 25/128 gives a fractional number as a result.

Carl-- multiply by 25. Divide by 128.

>>You<< are doing the same thing! Only with huger numbers. Substitute 25 for your 19..., and 128 for your gazillion.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

theusch wrote:
Quote:

But... 200/1024, 50/256 & 25/128 gives a fractional number as a result.

Carl-- multiply by 25. Divide by 128.

>>You<< are doing the same thing! Only with huger numbers. Substitute 25 for your 19..., and 128 for your gazillion.

Lee

Lee, why does what you are saying elude me??? Provide an example.

Edit:

Now you know why I don't (can't) resort to shortcuts, at least until I understand them. I'm a fairly smart guy with a fair amount of math under my belt. :lol:

I can't seem to get past the shortcuts & tricks. If I can't get it, I can only assume that those with less math experience will have an even tougher time grasping the shortcuts & tricks. I have no witt, I guess!!! :oops:

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Quote:

theusch wrote:
Carl--

If your Y domain is +100 to -100 and you use a range of 200, why don't you use a range of 1023 for your X range since it cgoes from 0 to 1023?

Actually, the Y axis is the range and the X axis is the domain.
...I stuck with 1023 because it is mathematically correct.

Don't you try to confuse me with none of yer book learnin'.

The y values "range" from +100 to -100.
The x values "range" from 0 to 1023.

If you "stuck with 1023" for the range [sic] of the x values, then why did you use 1024 in your formula? THAT is my question.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

theusch wrote:

Carl--

If your Y domain is +100 to -100 and you use a range of 200, why don't you use a range of 1023 for your X range since it cgoes from 0 to 1023?

Actually, the Y axis is the range and the X axis is the domain.
...I stuck with 1023 because it is mathematically correct.

Edit:
Ok! GIGO!!!

I fixed the errors.

But, you still didn't explain your shortcut...

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Sun. Jul 22, 2007 - 04:46 PM

Ok Lee, I ate dinner, all the while thinking about your shortcut.

It's a simple matter of re-ratioing the slope.

Here, dY / dX will imply change in Y over change in X - not derivative.

If m = dY / dX = (100 - (-100)) / (1023 - 0) = 200 / 1023 then:

Find a ration of smaller numbers that will provide the same ration as the original dY / dX.

I'll use 1024, as in your case to obtain whole numbers with no fractional parts.

You seem to have chosen 200 / 25. You could have also chosen 1024 / 128

so, I'll choose 200 / 25.

Set up an equality to determine an equal ratio using smaller numbers:

```25     X
--- = ----
200   1024
```

Algerbraiclly re-arrainged:

```25 * 1024
--------- = X = 128
200
```

And so:

``` 200     25
----- = ----- = 0.195313
1024     128
```

So, I admit that I had a typo and I've corrected it.

But, unless you multiply 0.195313 by some number, you still can't use the slope in fractional form with integer data types. I chose 1,000,000 to have a littl more accuracy then I needed to provide a bit better stability in the LCD display readings.

But, when all is said and done, whether I use your 25 / 128, 200 / 1024 or my 200 / 1023, the slope, m, (dY / dX) is never used in the actual program - only the constant representation of the slope, multiplied by 1,000,000. And the multiplier is needed to prevent the slope (m) from being truncated to zero and, to provide some degree of suitable display resolution.

So, I don't know which battle you are fighting here - my typo or your need to justify smaller numbers that will never be used in the actual program.

In the end, 200 / 1024 or, 200 / 1023 makes no noticable change in the LCD reading. Though, 200 / 1023 = 0.195503, is the mathematically correct form.

Sorry to the OP for the numerical error but, the algebra (Y = mX + b) is correct.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Should that not be 201/1024 ?

CirMicro wrote:
Should that not be 201/1024 ?

Actually, on the LCD, the displayed range is -100 < Y < +99.

So, as one count is lost in the Y axis to the zero place holder, it still comes out to 200 / 1023.

But as there are slightly more then 5 ADC states (1024 / 200) for each percentage point, I don't think it makes a whole lot of difference in the over all outcome.

It's just a matter of what is actually mathematically correct. Not the shortcuts or tricks.

But then too, I'm the guy who lost points on a math test question because I seem to think that 7 + 5 = 13. :oops:

Go figure!!! :roll:

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Quote:

But, unless you multiply 0.195313 by some number, you still can't use the slope in fractional form with integer data types.

YOU >>ARE<< USING THE SLOPE IN FRACTIONAL FORM WITH INTEGER DATA TYPES!!! you are multiplying by the fractional part and then dividing by the scaling factor of your fixed point number. this is >>exactly<< like using the numbers in the first place. no accuracy is gained or lost.

```200     25       1953125
----- = ----- = -------- = 0.195313
1024     128    10000000
```

and many other ratios.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Well Lee, how else would you use a number like 0.195313 in an integer based equasion? No, I'm not scaling the slope up to gain accuracy, I'm scaling the fractional result of the slope up to make it a usable integer - losses or not. After massaging with the ADC data, I scale product of the ADC value and the slope back to it's original decimal place.

I sure wish you would loose the riddles and provide a comprehensive example of what you are talking about. Lord knows, I have done my part to explain myself. Get specific!!!

In the end, my concept works and, it is well documented - both in text books and in my code.

Edit:
And, as I don't actually perform the slope, m, (Y1 - Y0) / (X1 - X0) in the program, your argument is really quite meaningless.

microcarl wrote:

The final equation used for the display of the motor velocity is:
Y = ((195503 * ADC) / 1000000) - 100;

Any proportional ratio of the slope will yeald the exact same answer, be it 200 / 1024 or 25 / 128. Only the result of this ratio is used in the actual program, in the form of an integer constant. That is, the slope part of Y = mX + b, m, is never calculated in the program - it is provided in type integer constant form only.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Carl,

To see what Lee is saying just pick a few example ADC readings and calculate it both ways.

Lets say 1, 7, 38, 145, 298, 442, 861, 992, 1023

Using * 1953125 then / 10000000 I make that (in rounded down integers), 0, 1, 7, 28, 58, 86, 168, 193, 199

Then using Lee's * 25 / 128 I make that 0, 1, 7, 28, 58, 86, 168, 193, 199

In Lee's case the largest is 1023 * 25 = 25,575 which fits easily into uint16_t and the /128 is obviously just >>7

Cliff

clawson wrote:
Carl,

To see what Lee is saying just pick a few example ADC readings and calculate it both ways.

Lets say 1, 7, 38, 145, 298, 442, 861, 992, 1023

Using * 1953125 then / 10000000 I make that (in rounded down integers), 0, 1, 7, 28, 58, 86, 168, 193, 199

Then using Lee's * 25 / 128 I make that 0, 1, 7, 28, 58, 86, 168, 193, 199

In Lee's case the largest is 1023 * 25 = 25,575 which fits easily into uint16_t and the /128 is obviously just >>7

Cliff

Cliff,

I understand your point and would agree if the slope of the Y = mX + b equation was being calculated at run time. But, it's not...

My point is:

200 / 1024 = 0.1953125

25 / 128 = 0.1953125

Where, both ratios yeald the exact same results.

I manually calculated the slope as, it will never change at run time and, therefore, is a logical candidate to be used in constant form. I then manually multiply 0.1953125 by 10,000,000 to get a usable integer form.

None of the above are caculated at run time, it is performed manually.

Using Lee's ratio, this will still need to be done as, the whole program is based on the "long int" data type.

The only added extra is the division by 10,000,000 to re-scale product of the ADC & slope back to the proper decimal point position. Using Lee's ratio, this won't change as, his ratio yealds the exact same fractional result as my ratio. And, as a fractional number can't be directly used in integer math without loss of the fractional part (yealding zero in this case) it has to be re-proportioned to a whole number integer form.

Now, if 25 / 128 produced a larger fractional result, ultamately resulting in a smaller value of integer constant for the slope then does 200 / 1024, I'd agree. But Lee's ratio doesn't yeald a slope that is any more benificial then my ratio. Both ratios produce the exact same fractional result.

So, how I go about manually calculating the slope of Y = mX + b really is a moot poing in this case. If the slope was being calculated in real time, then there are probably other methods that would work better, saving processing time and SRAM space - including Lee's proposal. In fact, using the constant form of the slope only uses four bytes of FLASH for the constant, whereas, a real time calculation of the slope would require additional processing time and probably more SRAM resources.

But, for the example provided early on in this thread, manually deriving the slope and using the constant integer form, in real time, is actually the best solution - especially due to the fact that the slope will never be anything other then 200 / 1023.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Carl,

Lee's code is more efficient than yours and requires less clock cycles to perform the same calculation.

A quick and probably sloppy testcase showed that Lee's technique saves 12 cycles - optimized code. In unoptimized code the difference was 675 which seems like a lot but this is possibly due to the long division.

I did not take the time to analyze the emitted code since 12 cycles seems realistic.

C: i = "told you so";

Quote:

I then manually multiply 0.1953125 by 10,000,000 to get a usable integer form.

Carl-- Using 200 and 1024, or 25 and 128, are not calculated at run time.

Quote:

Edit:
And, as I don't actually perform the slope, m, (Y1 - Y0) / (X1 - X0) in the program, your argument is really quite meaningless.

No, it isn't. As you said, "calculated before hand" are (Y1 - Y0) = 200 and (X1 - X0) = 1024.

No matter WHAT you do to them after that, as long as mathematically correct, they are still equal to 200/1024.

Taking the rise/run part of your equation

Quote:

Y = ((195503 * ADC) / 1000000) - 100;

[Where did 195503 come from?]

Since we've been working with these numbers, let's change to
Y = ((1953125 * ADC) / 10000000) - 100;
Can you agree that we can divide both numerator and denominator by 5, and not lose any accuracy? [Note that your original 195313/100000 loses accuracy over 25/128]

Y = ((390625 * ADC) / 2000000) - 100;
and repeating
Y = ((78125 * ADC) / 400000) - 100;
Y = ((15625 * ADC) / 80000) - 100;
Y = ((3125 * ADC) / 16000) - 100;
Y = ((625 * ADC) / 3200) - 100;
Y = ((125 * ADC) / 640) - 100;
Y = ((25 * ADC) / 128) - 100;

The point is that once you calculate your rise and run, these are already integer values that >>can<< be used at run time as the synonym for m = slope = rise/run. You changed to the fractional equivalent, and are still using a rise/run. As in this case, many times the magnitude of rise/run numbers can be reduced by a common factor.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Going back and reviewing Cliff's last post, here is what threw me, and obviously what Lee couldn't make clear...
The following is a partial quote from Cliff's last post.

clawson wrote:

Using * 1953125 then / 10000000 I make that (in rounded down integers), 0, 1, 7, 28, 58, 86, 168, 193, 199

Then using Lee's * 25 / 128 I make that 0, 1, 7, 28, 58, 86, 168, 193, 199

Cliff

Had Cliff put the ratio in the following way:

```Using ADC * 1953125 then / 10000000 I make that (in rounded down integers), 0, 1, 7, 28, 58, 86, 168, 193, 199

Then using Lee's  (ADC * 25) / 128 I make that 0, 1, 7, 28, 58, 86, 168, 193, 199```

That would have made perfect sense to me, though, I guess I;m so dense I'd have still missed the point. I mistakenly disregarded the * and was simply looking at the ratios as stand alone items.

I see it now.

So, cpluscon, you are saying that Lees realtime slope calculation is faster then my constant slope usage - all because I'm using data type long int?

I'm sorry for being so bull headded but, I can't say that Lee was very clear in his explination. If I, with a fairly good mathematics background had this much trouble understanding Lee's shortcut, how much problem will it be for someone to understand mathematicall shortcuts who has a minimum mathematics background - a point that I made eariler in this thread.

But I don't feel bad about pressing the issue, though; as I think I was quite clear about some important math concepts and this thread may make the concepts presented here a bir easier to understand.

Hopefully, it will be usefull to someone in the future.

Thanks to all for clearing up my confusion. You too Lee...

Incidentally, where's the OP???

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

theusch wrote:

Quote:

Y = ((195503 * ADC) / 1000000) - 100;

[Where did 195503 come from?]

`m = 200 / 1023 = 0.195503`

If you haven't reviewed my original post since we started this discussion, I edited the numerical errors that you discovered in that original posting.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Quote:
So, cpluscon, you are saying that Lees realtime slope calculation is faster then my constant slope usage - all because I'm using data type long int?
Yes, and because your divisor is not a power of two. If you used 102400/524288 your long int would take less time since the compiler should determine that the divisor is a power of two and use a shift rather than the much more expensive division subroutine.

Since the slopes are exactly equal, and as Cliff pointed out, the largest X multiplied by 25 will not overflow a 16 bit dual-register, then the long int transform provides NO BENEFIT whatsoever over the 25/128 technique and takes more clock cycles.

If the slope were something like 1911111./10000000 then long int would provide a bit more accuracy since the equivalent uint16 factor would be 24.462 forcing one to use 24 or 25 and losing accuracy. (In some cases that loss of accuracy would be acceptable.) Even then, one could use 49/256; so many choices.

EDIT:

Quote:
Lees realtime slope calculation ... my constant slope usage

I think these distinctions are inaccurate. Both approaches are to apply a multiplication followed by division. The question is simply what values, how many registers, and whether a shift is used rather than division.

C: i = "told you so";

Last Edited: Sun. Jul 22, 2007 - 06:22 PM

To make things complete from the point of my contributed confusion in this discussion, I am re-posting the modified version of my original post, hopefully, clearing up any remaining confusion.

My thanks to Cliff and cpluscon for their contributions and, especially Lee, for his patitence with me.

```/*
The goal...
This PWM control system is set up to use the "Locked Anti-phase" method
of motor control.  That is, the motor is in a constant change of
diredtion but, at or above 10KHz.

"Locked Anti-phase" allows the motor to be driven to the target velocity.
That is, when the target velocity changes from one velocity value to some
lower velocity value, the motor does not coast from the higher velocity
to the lesser velocity.  Instead, the motor is driven to the lower
velocity from the higher velocity.  Restated, "Locked Anti-phase" can be
viewed as a method of employing full time "Dynamic Breaking".  In addition,
the "Locked Anti-phase" method of PWM motor control is bi-directional.

The only real penalty with the "Locked Anti-phase" motor control method
is that, for a given number of control bits in the PWM range, there are
only 1/2 the number of steps to control the motor velocity in a given
direction - as opposed to the standard uni-directional PWM motor velocity
control.  Still, 9 bits of resolution or, 512 steps of motor velocity
control in either direction is acceptable in the most typical motor
control applications.

The correct display of motor velocity (in percent of maximum velocity)
will require unique calculations to correctly display motor velocity.
To convert the ADC value to +/-100 percent, based on an OCR1A mid-point or
zero motor velocity value of 511 (50% PWM duty cycle), we will use the
following:

A graphical representation of the required scaling is shown below:

Y2 100 |                 +
|               +
|             +
Percent |           +
Motor  |         +
Speed  |       +
|     +
|   +
| +
Y1 -100 + - - - - - - - - -
0                1023

The equation for the line of a linear graph is:
Y = mX + b

Where:
Y_range = -100% <= Y <= +100%
X_domain = 0 <= X <= 1023
And:
m = (dY / dX) = (Y2 - Y1) / (X2 - X1)
m = (100 - (-100)) / (1023 - 0) = 200 / 1023 = 0.195503
b = Y axis offset from the origin, 0,0

As the data type "long int" will need to be used in the motor velocity
calculation, the fractional quantity of the slope (m) will need to be
multiplied by 1000000 to prevent truncation of the fractional value of
the slope, m.

So:
0.195503 * 1000000 = 195503, hense the dinominator of 1000000
in the equation below.

Yealding:
Y = ((195503 * ADC) / 1000000) - 100;

At the suggestion of an AVRFreaks.net member, Lee, AKA "theusch", the following
is what will actually be employed.

Orginally, I was using:
m = (100 - (-100)) / (1023 - 0) = 200 / 1023 = 0.195503

Lee suggested using:
m = (100 - (-100)) / (1024 - 0) = 200 / 1024 = 0.1953125

And Lee also suggested that, by factoring the slope values, use of smaller
values for the ratio of dY / dX can be used, allowing data type "int" to
be employed, rather then data type "long int" - saving processing time.

So reducing to the smallest possible whole number values:
200    2 * 100    2 * 2 * 50    2 * 2 * 2 * 25
---- = -------- = ----------- = --------------- = 0.1953125
1024   2 * 512    2 * 2 * 256   2 * 2 * 2 * 128

And because:
2      4          8
- = 1, - = 1, and - = 1, etc...
2      4          8

Then:
200    25
---- = --- = 0.1953125
1024   128

The final equation to be used for the display of the motor velocity
is now:
Y = ((25 * ADC) / 128) - 100;

The origial slope of 200 / 1023 and, the factored slope resulting in the
use of 25 / 128, exibit no visible difference in the display of the motor
velocity on the LCD display.
*/ ```

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Mon. Jul 23, 2007 - 07:40 PM

By the way, because /128 is almost bound to be implemented as >>7 then I can't help thinking the compiler might be able to produce the tightest solution with *50 and /256 in fact - because there's no shift in that - it's just taking the high byte and discarding the lower one of the uint16_t holding the value (which still can easily hold 50 * 1023)

Cliff

Quote:

By the way, because /128 is almost bound to be implemented as >>7 then I can't help thinking the compiler might be able to produce the tightest solution ...

True enough in a particular case. In the general case, I've used the setup below with signed 16-bit values for rise, run, and intercept. In special cases with a conversion factor way off from 1, I'll apply a final scaling at the end with like 10**5 one way or the other.

I have a VisualBASIC program that I use when I have an arbitrary conversion factor--say, 6.54321. It runs through all the combinations of 16-bit numerators and denominators, and looks for a ratio that is "close enough". Almost always one or more can be found to 6 or 7 or 8 decimal places.

So, my conversion tables in EEPROM don't have to be "long", and fetching and storing don't have to be long. Only the actual conversion routine needs to have intermediate longs for the multiplication result and subsequent division. YMMV.

```eeprom	struct		signal_convert	// conversion factors to/from counts/units
{
int					rise;
int					run;		// together comprise the slope, or m of y=mx+b
int					intercept;	// the b of y=mx+b
};
...
eeprom	struct signal_convert	ee_convert[MAX_FACTOR] = 	{
{120,	1000,	0},			// Motor Amps*10 (deciamps) from A/D counts (pot = 1 o'clock, 0-12A range)
{20,	55,		0},			// Supply VAC from A/D counts, nominally 230VAC
{-1423,	1000,	1060},		// degrees fahrenheit * 10 from A/D counts) BARREL
{-1423,	1000,	1060},		// degrees fahrenheit * 10 from A/D counts) HOPPER/CABINET
{-3333,	10000,	162},		// degrees fahrenheit from A/D counts) AMBIENT, Vishay 2322640
{1,		1,		0},			// Unity, for "no conversion"
{0,		0,		0}			//  expansion
};
...
//
// **************************************************************************
// *
// *		C O N V _ I N T _ D I S P
// *
// **************************************************************************
//
// Setup routine to call calcy() to convert from internal units to display units
//
int							conv_int_disp		(	int x,
unsigned char units)
{
return calcy (x,
ee_convert[units].rise,
ee_convert[units].run,
ee_convert[units].intercept);
}

//
// **************************************************************************
// *
// *		C A L C Y
// *
// **************************************************************************
//
//	calcy()	--	returns a value after solving y = mx + b = (rise/run)x + intercept
//
int 		calcy	(	int x,
int rise,
int run,
int intercept)
{
return 	( (int)	(	(	(long int)x
* (long int)rise
)
/ (long int)run
)
+ intercept
);	/* Calc. y=mx+b */
}
```

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

clawson wrote:
By the way, because /128 is almost bound to be implemented as >>7 then I can't help thinking the compiler might be able to produce the tightest solution with *50 and /256 in fact - because there's no shift in that - it's just taking the high byte and discarding the lower one of the uint16_t holding the value (which still can easily hold 50 * 1023)

Cliff

Does it matter that the end result is a signed value, between -100 and +99 or something?

I have done fractional math with unsigned only, so if there were no negative numbers involved, I would have just multiplied with whatever fits to 2^16-1, but here we must fit the result to 2^15-1, if it is signed.

Or is it faster to do the multiply by 50, division by 256 (or shift by 8, i.e. take the high value) with unsigned numbers and then assign to signed number and decrease by 100?

But I think the speed increase would be quite marginal anyway , if already the calculations with longs are avoided.

- Jani

```; Digital Caliper interface
; (Raw value * 125/256)
;  Raw value = 20480 per inch
CalcInch:

mov r11,r8
mov r12,r9
mov r13,r10

lsr r8
ror r9
ror r10

sub r10,r12    ; Subtract the adjustment (3/256)
sbc r9, r11
sbc r8, zero

ret```

Here's a little routine I wrote for a Tiny13 some time ago. I'm not sure if a compiler would be able to see these kind of shortcuts though :)