Jeroen Swart

.NET Architect

NetDuino - Getting Started with ADC

The Netduino contains several ports (A0 - A5) that are capable of reading an analog signal and changing it into a digital representation of that signal. These ports can be used for digital I/O as well, but can individually be configured to use an internal ADC (Analog to Digital Converter).

The analog signal at the ADC port can come from different sources. It can come from a sensor, e.g. for measuring light or temperature, or it is simply taken from a variable resistor, that is changed by a user to select some value (e.g. the desired intensity of a dimmed light). The analog signal is a voltage between 0V and 3.3V. The ADC converts this voltage to a number between 0 and 1023, since the Netduino has 10-bits ADC’s. The number 0 represents 0V and 1023 represents Aref. The voltage level at any ADC port can be determined as follows:

float analogValue = (float)digitalValue * (3.3f/ 1023f)

In theory, you should divide by 1024, because there are that many steps. The maximum digital value of 1023 in that case represents a voltage between 3.2967 and 3.3V, but the formula would result in the lower value. In practice it is easier to divide by 1023, so a digital value of 1023 represents the full scale of 3,3V. And with ADC precision-errors and a Vref that's probably not exactly 3.3V, the result is pretty accurate.

The example uses port A0 and continuously reads the digital value and converts it to the actual voltage at the port using the previously mentioned formula. For this example connect a variable resistor to port A0 as shown below.

public class Program
{
    private const int MaximumValue = 1023;
    private const float AnalogReference = 3.3f;

    public static void Main()
    {
        // initialize one of the analog pins
        AnalogInput adcPort = new AnalogInput(Pins.GPIO_PIN_A0);

        // do forever...
        while (true)
        {
            // read a digital value from the ADC
            int digitalValue = adcPort.Read();

            // convert digital value to analog voltage value
            float analogValue = (float)digitalValue / MaximumValue * AnalogReference;

            // show the analog value in the output-window
            Debug.Print(analogValue.ToString());
        }
    }
}

If the default Aref value of 3.3V is not convenient, you can provide your own Aref at the corresponding pin of the Netduino headers. It must however be between 2.6V and 3.3V. To let the Netduino actually use the external Aref, you need to switch port 56 (on the board, but not available on the headers) to low:

OutputPort arefSelect = new OutputPort((Cpu.Pin)56, false);

To use the 3.3V again, switch port 56 to high:

OutputPort arefSelect = new OutputPort((Cpu.Pin)56, true);

Another way to convert the digital value of the ADC port to some meaningful value in code, is by using the SetRange method of the AnalogInput class. It allows you to set the minimum and maximum value returned by the Read method of the AnalogInput class. The conversion of the actual value to the range you have set is then done for you. In the example above we could have added the following line after the declaration of adcPort:

adcPort.SetRange(0, 3300);

The digitalValue can then be used as is, or divided by 100 to get the exact analog value:

float analogValue = (float)digitaValue / 100;

Since Aref can’t be higher than 3.3V, no analog signal higher than 3.3V can be measured by a port directly. Any signal above Aref is read as 1023 and, if considerably higher, may damage the port. To solve this, use a voltage divider using two resistors as shown below. The voltage at the port is Ain x R6 / (R5 + R6). The values of the resistors in the schematic allow for a 5V input to be measured as 3V by the port (5V * 18k / 30k = 3V). You can change the resistor values to accommodate any input voltage, e.g. change R2 to 120k to measure up to 23V. Note however that, when the input value is increased in this way, the accuracy of the value read from the ADC does decrease accordingly. Instead of 1024 steps of roughly 0.003V to reach 3.3V full scale, the ADC will do 1024 steps of roughly 0.022V to reach 23V full scale.

Another trick shown here is the use of a zener diode to protect a port. A zener diode will not allow a higher voltage to pass then the voltage that it was designed for. I won't go into the detailed workings of a zener diode, but effectively it protects the port from any voltage higher than the zener voltage.

Next: using the SD card.

Comments (5) -

  • Wayne McL

    10/16/2011 4:46:10 AM |

    Hi. Great example. Thanks for posting it. I suggest using MaximumValue = 1023, so that when the ADC input is at 3.3V, the float will return 3.3. Otherwise, you'll get 1023/1024*3.3 as a result.

    In practice, my Netduino is showing 5 - 8 lsb of noise, so this probably won't matter to most users. I'm just off to check the sample-hold time in the ADC code to see if the noise can be improved.

  • Jeroen

    10/19/2011 6:04:21 PM |

    Hi, thanks. And I agree. I've change the code and updated the post. I found 1023 was giving better results, but wasn't sure why. So I decided to stick with the theoretical value in my post. After that I started a thread on the netduino forum, which gave me a better understanding why 1023 results in a value that is closer to the actual voltage. I forgot to update this post.

    You can find the discussion on the Netduino forum here: forums.netduino.com/.../2216-adc-value-divide-by-1024-or-1023.

  • Wind Turbines

    2/3/2012 6:50:47 AM |

    thanks for the info !!

  • Jackie

    5/6/2012 11:06:43 PM |

    Thanks for this tutorial.  Just getting started with netduino and really helpful.

  • José Alvarez

    5/15/2013 2:19:28 AM |

    How many samples can I take by second on a single analog channel

Pingbacks and trackbacks (3)+

Comments are closed