As mentioned at the outset, the analog input system may be used to obtain data from a variety of sensors. One potential issue is that the range of sensor output voltages may not conveniently “line up” with the ADC input range, thus requiring some form of data scaling and offsetting. That is, the input voltages will need to be mapped onto a range of desired numeric output values. Sometimes this can be done with hardware but other times a simple mathematical process will suffice.
For example, suppose we have a sensor that we’ll use for measuring the temperature of liquid water at standard pressure. The specifications indicate that the unit will produce one volt at freezing (32 degrees Fahrenheit) and three volts at boiling (212 degrees Fahrenheit). Assuming that we’re using the default reference which yields an input range of zero to five volts, we’ll only be using 40% of the available dynamic range (i.e., two volts out of five). To make full use of the ten bit ADC, we’d need to run the sensor through a signal conditioning circuit to map the two volt output range onto the five volt input range. This would require an amplifier with a voltage gain of 2.5 and an output level shift of one volt. That is, we subtract one volt from the sensor output and then multiply the result by 2.5. By doing so, the 32 degree output of one volt will produce zero volts and the 212 degree output of three volts will produce five volts. A modest op amp or discrete transistor stage can be designed to achieve this. At the ADC input the total range of 180 degrees will be divided up into 1024 levels, yielding a resolution of better than 0.2 degrees per step. Note that the numeric values from the ADC are not the temperature values. A temperature of 32 degrees yields an ADC output of zero while 212 degrees produces 1023 from the ADC. If we need to display the Fahrenheit temperatures, perhaps on a multiplexed seven segment or LCD display, we’ll need to map these. This can be done with a simple formula that we’ll look at in a moment or with the Arduino map() function.
If a more modest resolution can be tolerated, a simple software mapping can be used. If we take the sensor output “as is”, we have to accept that we’ll only be getting 40% of the available resolution in this example. That will still give us around one-half degree resolution. Assuming this is sufficient, for the numeric readout just mentioned, all we need to do is map an input voltage of one volt to a numeric output of 32 and an input of three volts to a numeric output of 212. The one volt input represents one-fifth of the ADC’s maximum input signal and would produce a value of approximately 205. The three volt input would produce approximately 614. 205 represents the offset of 32 degrees and the range of 614−205 or 409 needs to represent the temperature range of 180 degrees. We can do this with one small formula:
// av is the value returned from analogRead() and // dv is the value to be displayed dv = 32+180*(av-205)/409;
Note that this computation is done entirely with integer math rather than floating point for best possible speed and minimal compiled code. For very large values of the variable
av the product could overflow an
unsigned int so a
long computation would be needed. Note that the multiply must be done before the integer divide or a horrendous loss of precision will occur due to truncation.
dv itself could still be a
short (or even an
unsigned char), so a little creative casting might yield something like this:
dv = (short)(32+180*(long)(av-205)/409);
As we are using integers, the displayed temperature resolution will be one degree (truncating).