|
|
View previous topic :: View next topic |
Author |
Message |
Sydney
Joined: 13 Feb 2009 Posts: 71
|
Scale, offset, percentage, is there a better way? |
Posted: Tue Feb 24, 2009 7:02 am |
|
|
Ok basically I want to calculate how far open a window is in percent. pot1avg is a 10-bit a/d read so can be anywhere from 0 - 1023. opened1 is the fully opened position, and closed1 is the fully closed position, both can be anywhere between 0 - 1023 also. Thanks to the forum, this following code works fine, but is quite slow and inefficient. Surely there is a better way to do it with out using float calculations? TIA, Ryan. Oh and BTW the percentage it output only needs to be an integer.
Code: |
static signed int16 pot1avg; // eg pot1avg = 189
static int16 closed1, opened1; // eg closed1 = 104, opened1 = 376
void scale_offset_pots(void){
if(closed1 < opened1){
pot1avg -= closed1;
pot1avg = (float)pot1avg / (opened1 - closed1) * 100;
}
else if(closed1 > opened1){
pot1avg = 0 - (pot1avg - closed1);
pot1avg = (float)pot1avg / (closed1 - opened2) * 100;
}
if(pot1avg > 100)
pot1avg = 100;
else if(pot1avg < 0)
pot1avg = 0;
} |
|
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Tue Feb 24, 2009 7:27 am |
|
|
As a first remark, the case selection for different scale sign isn't necessary, I think. The first case should work for both.
If the 10 bit ADC range is utilized, you have to extend the intermediate result to int32. If the range would be always restricted to 9 bit, as in your example, the calculation can be handled with unsigned int16 (no negative scale supported in this case). Basically you have to change the order of calculation when using integer arithmetic.
Code: | pot1avg = (int32)pot1avg*100 / (opened1 - closed1); |
|
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
|
Posted: Tue Feb 24, 2009 7:48 am |
|
|
Hi, I see what you mean about the case selection, but I think in the 1st case, if closed1 was greater than opened1 it would return a negative percentage, which would still need checking and negating?
I have previously tried the (int32) method, which also works, but is only marginally faster that the way in the OP, IIRC that function takes about 1ms@8mhz, perhaps thats as good as it gets? |
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
|
Posted: Tue Feb 24, 2009 1:41 pm |
|
|
Ok after having a little play the int32 version takes ~640us, and the float takes ~768us, it isnt actually as slow or inefficient as I thought and I guess it isn't going to get much better with a 10-bit adc.
Judging by the math operation time in the help file, it would be more than 3x faster with a 9-bit adc reading, but half the resolution is quite a big tradeoff.
Thanks for the reply FvM it was very helpful, I will go for your code snippet in my application! |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Tue Feb 24, 2009 5:20 pm |
|
|
You won't get a negative percentage using the first case, cause divisor and dividend are both negative then.
If opened1 and closed1 are arbitrary 10 Bit values, then there's no way then using int32. Of course, a user specific number format (e.g. int24) could be used in assembly code, but I doubt, if the effort pays. |
|
|
Sydney
Joined: 13 Feb 2009 Posts: 71
|
|
Posted: Wed Feb 25, 2009 1:20 pm |
|
|
FvM wrote: | You won't get a negative percentage using the first case, cause divisor and dividend are both negative then. |
Ah yes of course it would. I now have:
Code: | pot1avg = (signed int32)(pot1avg - closed1) * 100 / (signed int16)(opened1 - closed1) |
which is working well thanks for your help! |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|