linear interpolation / Assembler / ATMega32

Do you have a question? Post it now! No Registration Necessary

Translate This Thread From English to

Threaded View
Hi all,

i`m searching an ASM-example for 2d lin. interpolation.
(No need for floating point)

In the EEPROM is stored an table, like so

.eseg
LTable:
.db   0,  75
.db  40,  37
.db  60,  48
.db  80, 180
.db 100, 170

(The first byte in every .db line is the index, the second the value.)

Now i need the interpolated value for an index 55.

The calculation for that is not so difficult:

           48 - 37
X = 37 +  --------- * (55 - 40)
           60 - 40

So result is 45 for the index 55

But, how is this do do in Assembler?
And how to handle negative slope?

Perhaps, someone can post example code, or a link?

Thanks for your time,and best regards

Rolf





Re: linear interpolation / Assembler / ATMega32

Quoted text here. Click to load it

But you do need FIXED point..

Quoted text here. Click to load it

Or in integer maths (assembler)

                   11
        X = 37  +  -- * 15
                   15

        X = 37 + 0 * 15 = 37

What precision do you need??

Does your hardware support 8 x 8 or 16 x 16 bit Multiply?

Does your hardware support 16 / 16 bit?

For most cases work out the precision and accuracy you require in BINARY
places, then multiply up numbers that are used in the divide and multiply
by at LEAST that number of places do the calculation and remove fixed
point multiplier to give integer result you need.

Remember 6 binary bits of precision is not the same as 6 binary bits of
accuracy as some fractions are always awkward (prime numbers especially).

Quoted text here. Click to load it

Depends on the target and what instructions it has. Also what register
and data sizes it supports in assembler. As well as the points above.

Quoted text here. Click to load it

That is the least of your worries, you need to trap for divide by zero.
negative slope just means you add a negative number! Remember that
a positive / negative gives a negative number so does a negative / positive
number, similarly a positive * negative number gives a negative number.

Quoted text here. Click to load it

You need to give more info as on controllers I use (H8 series) a lot of
this is easy because it has 32bit registers as well hardware mult and div
instructions.

On some processors you will need to do multi byte arithmetic as they only
support 8 bit data types.

Quoted text here. Click to load it

--
Paul Carpenter        | snipped-for-privacy@pcserv.demon.co.uk
<http://www.pcserv.demon.co.uk/ Main Site
We've slightly trimmed the long signature. Click to see the full one.
Re: linear interpolation / Assembler / ATMega32
paul$@pcserv.demon.co.uk says...
Quoted text here. Click to load it
<snip>
Quoted text here. Click to load it
Or more properly (taking into account finite precision, elementary
numerical analysis and the fact that 60-40 = 20 ;) )

X = 37 + (11*15)/20
X = 37 + 165/20
X = 37 + 8
X = 45

Of course this chops rather than rounds so the question of precision
still comes up.  Also you need an intermediate value of 2n+1 bits (where
n is the number of bits in your original values), check against overflows
and of course against divide by zero.  You also need to be careful
that the table step size isn't too large. Some of those checks can be
eliminated if you can guarantee the composition of the table.

As long as you don't need better than +-1 than there is no need to have
any representation other than whole numbers.

Robert

Re: linear interpolation / Assembler / ATMega32
On Saturday, in article

Quoted text here. Click to load it

Damn retyped it several times to line up everything then put wrong value
in....

Quoted text here. Click to load it

Alternative method..


Doesn't it always come up...

Quoted text here. Click to load it

As one says depending on required precision...

--
Paul Carpenter        | snipped-for-privacy@pcserv.demon.co.uk
<http://www.pcserv.demon.co.uk/ Main Site
We've slightly trimmed the long signature. Click to see the full one.
Re: linear interpolation / Assembler / ATMega32
paul$@pcserv.demon.co.uk says...
Quoted text here. Click to load it
I hate it when that happens :)

Quoted text here. Click to load it
Mind you if the OP needs better than +/- 1 the first thing to address is
the data representation in the table.

I don't know if the AVR has (or has a SW library that has) an 8bit x 8bit
to 16bit multiply and a 16bit / 8 bit -> 8bit divide but if it does than
with a few 'reasonble restrictions' on the table there will be no worry
about overflows or loss of precision.

Robert

Re: linear interpolation / Assembler / ATMega32
Quoted text here. Click to load it

   That's the MULS instruction.  Divide, if it exists, seems
it would be a library subroutine.  I should check this out
in the WinAVR toolchain.

        Regards.        Mel.

Re: linear interpolation / Assembler / ATMega32

Quoted text here. Click to load it
You'll notice that the denominator is a constant.  There are two ways of
handling this simply.  The first is to multiply by a constant of the
reciprocal of the denominator (i.e. 1/20 - or 2^N * 1/20 in the exemplar
case), or better still change the interval to 2^N (e.g. 8 or 16) and shift
right N to accomplish the division.

Cheers,
Alf.



Re: linear interpolation / Assembler / ATMega32
Hi Paul!



Paul Carpenter wrote:

Quoted text here. Click to load it

I think no, because +/-1 is good enough.

Quoted text here. Click to load it


In the subject of my posting the type of MC is given, AVR ATMega32.
This device does not support DIV, and has only 8 bit Registers.
But math software routines are available.

Unfortunately i'm not an good asm-programmer.
Due to that fact, to write such an interpol function in asm, it will
take the rest of my life.
So i need a piece of code, which i can use as "black box". Values in,
result out...

Anyway many thanks for your answer, it shows me some significant details
and explain the principle.

And, of course, also thanks do Mel and Robert.


Regards and greetings from Petershagen, Rolf


begin 666 wink.gif
M1TE&.#EA#P`/`+,``````+^_O___````````````````````````````````
M`````````````````````"'Y! $```$`+ `````/``\```0T,$@):ITX5,'Y
MQ4 G>E,XC@`EF.MJIJSEQ>PI;C9:YZYGOQK?C12<R8C%7P;7^60TEA0F`@`[
`
end


Re: linear interpolation / Assembler / ATMega32
On Sun, 13 Jun 2004 07:38:13 +0200, "Rolf Bredemeier"


Quoted text here. Click to load it

Than I'd say: Write it in C, compile it to assembly and see where you
can improve it.
But first: Improve your C code !

--
42Bastian
Do not email to snipped-for-privacy@yahoo.com, it's a spam-only account :-)
We've slightly trimmed the long signature. Click to see the full one.
Re: linear interpolation / Assembler / ATMega32

Quoted text here. Click to load it
Just a suggestion or three: if you drop the index and add a value for 20,
you'll use less eeprom space.  you will know that index = offset * 20.  Your
values are very coarse. and the result is not likely to be very accurate.
This appears to be a complex curve (at least a cubic).  I would throw in
some extra values.  You could insert a value every 10, or perhaps 8 (the
arithmetic becomes simpler/faster/cheaper if the index is a power of 2 as
multiplies and particularly divides can be replaced by shifts.    ..... but
I'm not going to write the assembler for you, sorry.

Cheers,
Alf



Re: linear interpolation / Assembler / ATMega32
Hi Alf!


Quoted text here. Click to load it

My values only an example, in reality there are more entrys in the table,
perhaps 20 for an not linear analog value from 0 to 100 percent.


Quoted text here. Click to load it


I am afraid for that, but i understand ;-))

Best regards, and thanks for the tip to do index steps by eight.

Rolf



Re: linear interpolation / Assembler / ATMega32

Quoted text here. Click to load it

You have already got several good answers, but there are two
things which might help you:

#1 Can the intervals in the EEPROM be a power of two?
#2 There is a simple way for proper rounding of the results.


Quoted text here. Click to load it

If you could make this table so that the intervals are powers
of two, then you can replace the division by a simple shift
(arithmetic shift right, ASR). Then you can also find the
table position by shifting instead of dividing. This will
save a lot of time and code.

Let's say you have the tabulated data with 32-unit intervals.
Then when you need to find the value for f(x):

  ; index to the table (i <- x/32, rounded down)
  i = x / 32

  ; sub-index (remainder of the previous calculation)
  s = x mod 32

  ; base value from the table
  b = t[i]

  ; difference to the next tabulated value
  d = t[i+1] - t[i]

  ; the result
  r = b + s/32 * d

Now, here we still have all the precision and performance worries.
This can be simplified significantly because of the simpler division
and modulo with a power-of-two.

  i = x shr 5
  s = x and 0x1f
  b = t[i]
  d = t[i+1] - t[i]
  r = b + (s * d) asr 5

Beware, be sure to check there is no possibility for any
overflow on the last row. Also, if d can be negative, you have to
use asr intead of shr. Alternatively, you may branch the
algorithm:

  ...
  if t[i+1] < t[i] then
    d = t[i] - t[i+1]
    r = b - (s * d) shr 5
  else
    d = t[i+1] - t[1]
    r = b + (s * d) shr 5

This enables the use of positive (unsigned) numbers only.
This will give you two bits more headroom and sometimes simpler
code, depending on the machine. (This does not apply to ATmega,
since there is a built-in signed multiply instruction.)

There is still one more problem: rounding. Basically, the standard
method is to do something like this:

  r = b + ((s * d shl 1) + 1) asr 6

This adds a one bit bias to the first drop-out bit. Everything
above 1/2 will be rounded up, just as it should be. Of course,
you may shift your look-up table by one if you want to avoid the
shift at this point.

If you want to use this rounding algorithm with the sign branching
shown above, be sure to _subtract_ the half-bit in the negative
branch. Otherwise -1.5 will round to -2 instead of -1. This
will introduce an odd jump around zero. Difficult to spot
and gives very annoying noise (been there, done that).

There are at least umpteen ways to polish the algorithm to
give the fastest performance. Some machines might benefit from
using biased unsigned instead of signed, and the best way to
perform the look-up depends on the machine. If you have a
lot of room in the memory, you may calculate the differences
beforehand.

If you do not have a hardware multiplier, you may be fastest
and smallest off by rolling your own: (Doing this on ATmega
isn't a very good idea, as there is the hw multiplier.)

  i = x shr 5
  
  ; this becomes unnecessary:
  ; s = x and 0x1f

  r = t[i]
  d = t[i+1] - t[i]

  ; check the highest bit of s (16's in x)
  a = 0
  if (x and 0x10) = 0x10 then
    a = a + d

  ; second highest (8's)
  d = d shr 1
  x = x shl 1
  if (x and 0x10) = 0x10 then
    a = a + d

  ; 4's
  d = d shr 1
  x = x shl 1
  if (x and 0x10) = 0x10 then
    a = a + d

  ; 2's
  d = d shr 1
  x = x shl 1
  if (x and 0x10) = 0x10 then
    a = a + d

  ; 1's
  d = d shr 1
  x = x shl 1
  if (x and 0x10) = 0x10 then
    a = a + d

  ; round-up
  a = a + 1
  a = a shr 1

  r = r + a


In the code above, the multiplication loop has been rolled out,
but there is no reason why it couldn't be written as a loop
(apart from slight performance hit and need for yet another
variable). Many small processors offer a simple method for
checking a bit, something like "skip if bit n in register r is
set".

One of the advantages of this method is that it requires only
one extra bit. If you can toss rounding (by taking it into
account in the tabulated data!), then even this bit is not
required.

- Ville

--
Ville Voipio, Dr.Tech., M.Sc. (EE)

Re: linear interpolation / Assembler / ATMega32
Hello Ville, hello Paul!

I'm very astonished about the lot of answers you
have written for me.

Many, many thanks! :-))

So my goal is to do the implementation personally.
While doing this, i will learn much for writing
better asm code.

Perhaps, in 10 or 12 years, i can help other
people for implementing algorithm! ;-)

Thanks again for the lot of time you spend to me.
Now i will try it!

Best regards, Rolf



Re: linear interpolation / Assembler / ATMega32
Quoted text here. Click to load it

Here's a suggestion.  Do you have a C compiler for your CPU.  If not
try to get a demo version.  THen write the solution is C and look at
the asm output.  Then you have the asnwer you're looking for.

George

Rounding of integer divisions (was Re: linear interpolation / Assembler / ATMega32)

Quoted text here. Click to load it

Well, no problem, as:

-17 >> 4 = 11101111b >> 4 = 11111110b = -2
-16 >> 4 = 11110000b >> 4 = 11111111b = -1
...
 -1 >> 4 = 11111111b >> 4 = 11111111b = -1
  0 >> 4 = 00000000b >> 4 = 00000000b =  0
...
 15 >> 4 = 00001111b >> 4 = 00000000b =  0
 16 >> 4 = 00010000b >> 4 = 00000001b =  1

So, everything is rounded down:

-2 <= x < -1   ->   -2  (bin average -1.5)
-1 <= x < 0    ->   -1  (bin average -0.5)
 0 <= x < 1    ->    0  (bin average  0.5)
 1 <= x < 2    ->    1  (bin average  1.5)

The results are half-step lower than they should be (they should
coincide with the bin averages). This can be corrected by
adding 8 (that is 0.5 * 2^4) to the initial value:

(-9 + 8) >> 4 = 11111111b >> 4 = -1
(-8 + 8) >> 4 = 00000000b >> 4 =  0
...
( 7 + 8) >> 4 = 00001111b >> 4 =  0
( 8 + 8) >> 4 = 00010000b >> 4 =  1


Now the limits are exactly where they shoud be:

-9 / 16 = -0.5625 -> -1
-8 / 16 = -0.5    ->  0
...
 7 / 16 =  0.4375 ->  0
 8 / 16 =  0.5    ->  1

Everything is rounded to the nearest integer, halves are rounded
up.

So, the plain ASR works fine apart from the average -0.5 bias in
the result. The bias can be compensated for in the tabulated data
or by adding the half before shifting.

The worst thing you may do is to unsignedify the number, round
it down, and then put the sign back. Because:

-17 / 16 -> -(17 >> 4) -> -1
-16 / 16 -> -(16 >> 4) -> -1
-15 / 16 -> -(15 >> 4) ->  0
...
  0 / 16 ->  ( 0 >> 4) ->  0
...
 15 / 16 ->  (15 >> 4) ->  0
 16 / 16 ->  (16 >> 4) ->  1

The function becomes dead around zero! The result is zero over
two units, and everything linear becomes non-linear. Cross-over
distortion sets in...

Unfortunately, this is exactly what C does when you write:

  int z, x, y;

  z = x / y;

There is a technical reason for this; division is easier to do,
if you do not need to write different branches for different
signs. C rounds towards zero. Not up, not down, but towards
zero. (This may even be a desired behaviour in some cases,
but with measurements it is a very bad thing.)

One simple way around this is to use biased unsigned divisions:

  // bias by 15:
  z = (x + 15 * y) / y - 15;

If x + 15 * y always >= 0. This is rather easy with constant divisors.
With variable divisors finding a suitable constant is very difficult.
Pick a too low one, and the sum will underflow. Pick a too high one,
and the sum will overflow.
  
The case with the possibility of a negative divisor is even
more complicated. Then the only alternative is to branch the
calculations by the signs before doing anything;

  int x, y, z;
  unsigned int ax, ay, az;
  bool sign;

  // find out the sign of the result and the absolute values of x, y
  if (x < 0)
    {
    ax = -x;
    sign = false;
    }
  else
    {
    ax = x;
    sign = true;
    }

  if (y < 0)
    {
    ay = -y;
    sign = ~sign;
    }
  else
    ay = y;

  // if the sign of the result is negative, round away from zero
  if (sign == false)
    {
    az = (ax + ay - 1) / ay;
    z = -az;
    }
  else
    {
    az = ax / ay;
    z = az;
    }

Yes, there are a lot of extra variables which may be avoided
by using type casts. However, I think it is better avoid the
type casts and let the compiler get rid of the extra variables.

So, twenty-something lines of code to achive a very simple
thing. The good thing is that this does not give a very large
performance hit, as the signed division algorithm does this
anyway.

To make the algorithm round right (towards nearest, halves up)
requires only the following changes:

negative branch:
  az = (ax + ay - 1 - ay/2) / ay

positive branch:
  az = (ax + ay/2) / ay

Even though it is very tempting to say:

  ay - 1 - ay/2 == ay/2 - 1,

don't. Because it isn't. For, e.g., ay = 17:

  17 - 1 - 8 == 8 - 1
           8 == 7

Oops.

I know this is somewhat complicated. The effect of false rounding
is usually quite small. I personally crashed into this well-known
(generally, that is, not well-known by me) problem when I was
looking at a F-transform of some real-world data. There were some
harmonics there shouldn't've been. I scratched through
the skin of my head before finding out the problem.

If off-by-one is bad, off-by-half is not much nicer, either.

- Ville

--
Ville Voipio, Dr.Tech., M.Sc. (EE)

Re: Rounding of integer divisions (was Re: linear interpolation / Assembler / ATMega32)
Quoted text here. Click to load it
---snip---
Quoted text here. Click to load it
---snip---

Hi
 Most high level languages round towards zero, mostly
because it is traditional. Doing floored rounding makes
more sense but even that has to be done carefully.
In something like DSP, one can accumulate a DC offset
because of floored rounding. As you point out, you
get a cross over distortion for rounding towards zero
so there is no absolutely right way to do this.
One has to look carefully at the application one intends
to use it in before determining which way to go on this.
One might even use both methods in the same stretch of code.
 I once designed a XY table that requires 24 bit math
that a fellow was doing 16 bit math by simply extending
it to 32 bits while using the round towards zero.
 It was a disaster. The table would occasionally jump
by a sizable amount while incrememnting by a joy stick.
This was a table that should have positioned to about
two 10/1000th of an inch. This was traced to his round
towards zero math in the Pascal he was using to do the
extended math.
 It would have been nice if the uP makes had included
both types in the hardware so we could choose which
to use and where.
Dwight

Re: Rounding of integer divisions (was Re: linear interpolation / Assembler / ATMega32)
On 14 Jun 2004 17:48:02 -0700, snipped-for-privacy@hotmail.com (dwight elvey)

Quoted text here. Click to load it

I think it should be more appropriate to talk about truncation in this
case and this behaviour makes sense in many situations.

IMHO, rounding should be reserved for rounding towards the nearest
integer.

At least old Pascal did not allow direct assignment  of floating point
values to integer, but instead the trunc or round functions had to be
used.
 
Quoted text here. Click to load it

This is a third method and makes sense in this interpolation case,
however, this is more of a special case.
 
Quoted text here. Click to load it

It should be noted that if the hardware uses 1's complement or
sign/magnitude representation, there is the problem with +0 and -0 and
thus the potential for "cross over distortion".

Paul


Site Timeline