View previous topic :: View next topic |
Author |
Message |
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
Custom Math function |
Posted: Thu Apr 15, 2004 11:07 am |
|
|
I have a lot of places in code where I need to perform scaling of a number. This is not a problem but I would like to reduce the math operation. Here is an example.
This is required only once.
Code: |
Int16 Analog_Input_1_Low;
Int16 Analog_Input_1_High
Int16 Analog_Input_1_RAW;
Int32 Analog_Input_1_m;
Analog_Input_1_m=Analog_Input_1_High-Analog_Input_1_Low;
Analog_Input_1_m=0x10000000/Analog_Input_1_m;
|
This is required for each reading that is taken.
Code: |
Analog_Input_1_Reading=((int32)(Analog_Input_1_RAW-Analog_Input_1_Low) * Analog_Input_1_m) >> 16;
|
I want to write a function to perform the math with some improvements.
1. Don�t solve the lower 2 bytes of the multiply.
2. Place the function inline to prevent copy operations.
3. Build the shift operation into the function.
I have been looking to find a library with a 32x32 mult function I can re-write to do this. I haven�t been able to find anything. |
|
|
valemike Guest
|
|
|
SteveS
Joined: 27 Oct 2003 Posts: 126
|
|
Posted: Fri Apr 16, 2004 7:07 am |
|
|
Can you give some more detail on what you need to do - like range of the the various values and accuracy looked for? I assume it's all unsigned? I had to do similiar math in a past project- I have to go find it.
- SteveS |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Fri Apr 16, 2004 7:52 am |
|
|
SteveS wrote: | Can you give some more detail on what you need to do - like range of the the various values and accuracy looked for? I assume it's all unsigned? I had to do similiar math in a past project- I have to go find it.
- SteveS |
I'm measuring the input from an analog source. For example I have a measurment that ranges from a low of 200 to a high of 800. To scale this reading from 0 to 4095 I use the formula shown. This method also works to scale an output or any other linear measurement from one range to another. I think this is something a lot of people do one way or another. In my example the value 0x10000000 determines the range of the functions output. |
|
|
ThadSmith
Joined: 23 Feb 2004 Posts: 5 Location: Boulder, CO, USA
|
|
Posted: Thu Apr 22, 2004 4:41 pm |
|
|
Neutone wrote: | I'm measuring the input from an analog source. For example I have a measurment that ranges from a low of 200 to a high of 800. To scale this reading from 0 to 4095 I use the formula shown. This method also works to scale an output or any other linear measurement from one range to another. I think this is something a lot of people do one way or another. In my example the value 0x10000000 determines the range of the functions output. |
In your example, you need to multiply by approximately 6.x.
With some pre-scaling and post-scaling, a 16x16-> 32 bit multiply would work. If you want custom, I can give you a 16x16->16 bit fractional multiply that would work with a simple prescale, but not postscale.
Thad
Last edited by ThadSmith on Fri Apr 23, 2004 11:16 am; edited 1 time in total |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Thu Apr 22, 2004 5:56 pm |
|
|
0x10000000 / (800 - 200) = 447392 // My fractional M value
447392 / 2^16 = 6.82666015625
When the analog input is 800
((800 - 200)*447392 ) / 2^16 = 4095.99609375
This is how I am doing this in software right now. |
|
|
ThadSmith
Joined: 23 Feb 2004 Posts: 5 Location: Boulder, CO, USA
|
|
Posted: Fri Apr 23, 2004 11:12 am |
|
|
Neutone wrote: | 0x10000000 / (800 - 200) = 447392 // My fractional M value
447392 / 2^16 = 6.82666015625
When the analog input is 800
((800 - 200)*447392 ) / 2^16 = 4095.99609375
This is how I am doing this in software right now. |
I am assuming that you are trying to reduce the execution time of the calculation. If so, here is a fractional 16-bit procedure in C (tested in PCM) that runs faster by only retaining 16 bits in the product accumulator and by taking advantage of the limited numbrer of bits in one of the operands (600 in your example).
Code: |
typedef int16 uint16_t; /* C99 standard for unsigned 16 bit */
typedef signed int16 int16_t; /* C99 standard for signed 16 bit */
/* Function: mpy16fs
** Description: This function multiplies two unsigned 16 bit values and
** returns the scaled results. It takes advantage of the
** limited number of bits in one operand and has restrictions
** on the scaling factor in order to produce quicker results.
** Due to the technique used (right shifting of multiplicand)
** there is some precision lost.
** As abits decreases, the operation performs faster and
** with greater accuracy. The limit is determined by the
** size of the operand a.
**
** Example: Assume there are two operands in the known ranges:
** a = 0 to 500 integer
** bf = 0 to 10.0 fractional
** abits can be set to 9 (9 bits maximum).
** bf can be scaled by (1<<11), to maintain maximum precision.
** bscaled = (uint16_t) (bf * (1<<11));
** p = mpy16f (9, a, bscaled, 11);
** This will return p ~= (uint16_t)(a*bf);
*/
uint16_t /* a*bscaled >> shift */
mpy16fs (
byte abits, /* maximum number of significant bits in a,
** 1..16 */
uint16_t a, /* multiplier, 0..(1<<abits)-1 */
uint16_t bscaled,/* multiplicand, 0..0x7fff */
byte shift /* post scaling, abits..abits+15 */
) {
uint16_t prod = 0;
shift -= abits;
/* prescale multiplier for faster result */
a <<= (16-abits);
if ((int16_t)a < 0) prod = bscaled;
bscaled >>= 1;
do {
if ((int16_t)(a <<= 1) < 0) prod += bscaled;
bscaled >>= 1;
} while (--abits);
return prod >>= shift+1;
}
|
This takes about 280 cycles for a product, while the 32-bit version takes about 610. You can customize it with your abits and shift values to slightly reduce the execution time.
If the accuracy isn't good enough, there is another procedure that runs in about the same average time, but has higher worse case timing, which retains more precision.
Thad |
|
|
|