PDA

View Full Version : Scaling ADC values



purkolator
- 10th April 2007, 22:18
Hello all,

I am trying to build a midi controller that incorporates an accellerometer. I have one of the 2 axis accelerometers from Dimension Engineering. The behavior that I would like is for the midi output to follow the tilt of the accelerometer on each axis from -90 degrees to +90 degrees. In other words, if the accelerometer were in its neutral position, the midi controllers associated with each axis would spit out a value of 64. If tilted to a full 90 degrees in a given direction the midi output for that axis would spit out either 0 or 127. Make sense?

Ok, the problem is that when tilted 90 degrees from neutral, the accelerometer does not reach its limit (0v or 5v). The values I get from the ADC at 90 degrees from neutral are about 45 to 85. Here is a snippet of the code where I get and process the adc value.


ADCIN 1,modval ' Read channel 1 to modval
modval = modval/2

The divide by 2 is just the simplest way I could think of to get the result from 8 bit to 7 bit (for MIDI) that I could think of. I am sure there is an easier way.

So, my question is, what is the best way to go about scaling an adc result of between 45 and 85 to the desired results of 0 to 127.

thanks in advance for the help!
Brad

skimask
- 10th April 2007, 23:00
First off, don't forget, PBP only deals in integers, so if your input range is less than your target output range, you'll lose a bit in the translation.
So, here's a quicky thing I can think of to get you what you want...(in pseudo-code)

adcinput var word : minimumvalue var word : maximumvalue var word
range var word : scalefactor var word : newadcinput var word

Loop:
Get adcinput 'get the numbers from the A/D pin

if adcinput < minimumvalue then 'check for a new minimum value
minimumvalue = adcinput 'if it's less than the old one, replace it
endif

if adcinput > maximumvalue then 'check for a new maximum value
maximumvalue = adcinput 'if it's greater than the old one, replace it
endif

range = maximumvalue - minimumvalue 'get the total range of the input so far

scalefactor = 65536 / range 'get the scale factor for multiplying the number

newadcinput = adcinput * scalefactor 'up to a full 16 bit number to help avoid losing any more precision than we have to

newadcinput = newadcinput >> 9 'shift that big number back down to a 7 bit number

goto loop



Of course this could probably all be shortened up into one decent line of code, but you should get the picture.
Since we're working with integers, if you just took the range of the input and multiplied it to get a number range close to 0-127, you'd lose way too much precision. By taking and multiplying it up to a 16 bit number and dividing it down by a constant, you might save a bit of that precision. If we had floating point, we'd be ok, but we don't...

purkolator
- 12th April 2007, 23:30
Thanks for the help Skimask, it helped a lot. I used your method with a couple of modifications and it seems to work very well.

First, I added to your method was to define minimums and maximums. I was having problems bumps/shakes of the accelerometer making the min and max values go well outside of the range which I wanted to capture (which is neutral to 90 degrees on either axis). The MIN and MAX statements solved that.

I also added a line to subtract the minimum value from the capture adc value. This ensured that the final output was actually 0-127. Without it, my output would never go all the way down to 0.

In the long run (as I am sure many of you will notice) I will probably just define range since I know what the adc values are at +90 and -90 degrees. I haven't done that yet, because I want to be sure that environmental variables don't affect the accelerometer output.

Here is the routine that I ended up using....


modcheck:
ADCIN 1,modval ' Read channel 1 to modval
modval=modval min 166
modval=modval max 91
if modval<minval then minval=modval
if modval>maxval then maxval=modval
range=maxval-minval
modval=modval-minval
scalefactor=65535/range
modvalnew=modval*scalefactor
modvalnew=modvalnew>>9

skimask
- 12th April 2007, 23:34
One thing I forgot to mention (but it seems you already took care of it in a way) is to initially set your minimum value very high (so the only place it can go is down) and your maximum value very low (so the only place it can go is up)...

RodSTAR
- 29th November 2007, 05:14
One thing I forgot to mention (but it seems you already took care of it in a way) is to initially set your minimum value very high (so the only place it can go is down) and your maximum value very low (so the only place it can go is up)...

yes, both near the center