Search Microcontrollers

Sunday, October 28, 2012

Analog to Digital Converters - 1

Analog to Digital converters are, in my opinion, one of the most fun peripheral to deal with when working with Microcontrollers.
Why?
Let's face it : most of the measurements you want to do in the real world involve an analog reading, normally a voltage or a resistance returned by some kind of sensor.

Like most of the mcus, the MSP430G2 has an ADC built in (warning : the ADC10 is not included in all the devices of the family, check the datasheet or the specs for the specific device you are dealing with).
I am going to use the MSP430G2553 supplied with the launchpad and that one definitely has an ADC10 onboard.

First of all, it's called ADC10 because it has 10 bits of resolution.
10 Bits is the typical resolution you can find in "cheap"devices, more advanced ones (such as the C2000 or Cortex M3) may have a 12bit adc instead.

What's the difference?
It's about resolution : a 12 bit device, having 2 bits more, achieves a four (2^2) times higher resolution than a 10 bit one.
It's a big difference, however in many (most?) cases it does not really matter.

Let's imagine you are sampling a temperature from a sensor such as the LM35DZ (cheap and common sensor).
This sensor increases it's output by 10mV for each Centigrade, at 0C it will provide 0V (which makes it really simple to use it with mcus).
However this sensor does not provide high accuracy since it's best accuracy is about +/- 0.5C, which can be translated into +/- 5mV.

A 10 bit adc with range between 0 and 3.3V has a resolution of 3.3 / 1024 V/bit =  3mV/bit which is already higher than the accuracy of the sensor itself, therefore a higher adc resolution would be useless.
However this setup would allow us to measure temperatures between 0 and 33C, which it might be ok for some applications, but not enough for others.
The LM35 sensor itself can read temperatures from -55C to 150C (depending of the exact model, check the datasheet) but this would generate voltages from -5.5V up to 15V.
If you apply such ranges to a 3.3V tolerant ADC, you are likely to burn it, so most people normally regard this a as a bad idea.
You can offset and "scale" the signal using a voltage divider (with 3 resistors in case you need to add the offset), but at that point, should you still aim for +/- 0.5C resolution, the 10 bits are not enough anymore.

The example with the LM35 was useful to add some context, but the issue is generic.
Imagine we have a sensor that measures a quantity X and outputs linearly a voltage V(X) = V0+kX.
The constant k is the voltage gain of the sensor, it is a constant if the sensor output is linear.
The range we want to measure is [Xmin,Xmax], which gives us DX = Xmax-Xmin.
Assuming we can exactly map Xmin to the lower reference of the ADC (i.e. 0V) and Xmax to the higher reference (i.e. 3.3V), and we know we want to obtain a specific resolution R, then we can calculate how many bits (minimum) we need to sample the signal.

Say DX = 100X and the needed resolution R is 0.01X, this means that the ADC must have 100X/0.01X = 10.000 "steps".
To get the minimum number of bits you can apply a log2(10.000) or simply compare with the resolution of finite number of bits :
8 bits -> 256
10 bits -> 1.024
12 bits -> 4.096
14 bits -> 16.384
...

in our case we would need a 14 bit ADC, not an easy requirement as very few of the mcu integrated ADCs can get that precision.
At the same time, when sampling with such accuracy, we might have additional issues such as the stability of the reference voltages, the accuracy of the resistors used in an eventual divider, electronic noise etc.
When higher precision and speed must be achieved, the common solution is to use dedicated ADC devices.

If now you feel frustrated because your mcu only supports a 10 bit sampling device, just think this : precision instruments such as Digital Oscilloscopes use 8 bit ADCs (but they are normally extremely fast and equipped with high precision input circuits).
How do they give all that flexibility and precision with 8 bit only?
They allow to set different scales by altering the offset of the signal and the parameters of the input voltage divider.

The other important parameter is the sampling frequency.
Why achieving high frequency and high resolution at the same time can be difficult?
That's related to the way the sampling happens, which I will try to explain in a simple case.
Digital devices only understand two states : on and off, so how do they convert a voltage to an on or off state?
They can compare the voltage with a reference one and say : if the reference is higher, then set to off, else set to on.
Now if the reference voltage V(t) varies starting from V- (lower reference) and linearly climbs to V+ (higher reference) and at the same time we start a timer that counts the number of cpu cycles, at a given point in time, the signal will cross the reference and the comparison will return "on".
At that point the timer count t gives us a measurement of the voltage used in the comparison V(t).
Practically, some more advanced techniques are used, but that's  pretty much the basic concept.
You understand then that the timer speed is affecting the resolution + sampling rate combination.
If we have a 10 bit resolution, the timer must be able to count up to 1024 when V(t) = V+.
Imagine the timer uses the same clock frequency as the CPU being 16MHz -> the maximum sampling rate would be 16/1024 MHz = 15KHz.
As I previously stated, some more advanced techniques (series of approximations etc) are used, allowing the adcs to achieve better performances, but you now probably understand the relation between cpu speed, sampling rate and resolution.

In the next post I am going to experiment with the ADC10 device of the MSP430G2553


5 comments:

Jamie said...

Hi,
I just bought a c2000 launchpad and your tutorials have been really helpful for me to understand the C2000 coding. My aim is to connect a sensor to an analog input and then switch a digital output depending on the sensor value. I'm finding it difficult to understand how the ADC module works. I would really appreciate if you could help me on how to read an external analog input using C2000 launchpad. Thanks a lot..

Francesco Agosti said...

Hello and thanks for your comment, glad you found my tutorials useful.
I have in my plans a tutorial on the c2000 ADC since some time, but never found the time to produce it :/
I will hopefully be able to produce it soon!

Jamie said...

Hi Francesco,

Thanks a lot for your reply. Looking forward to your next tutorial.. I'm not very familiar with C++ coding for C2000 and at the moment, I'm trying to get my head round the "Handle" concept. It would be great if you could let me know any good resource that would help me.
Thanks again..

Francesco Agosti said...

Hello again,
I am also learning how to use the C2000 and the other MCUs, so I am nto really an expert, it's just I like to share whatever I learn.

About Handlers :
Are you familiar with OOP?
With oop you have objects that have methods.
Time ago this was not really popular (yes, I am THAT old) and we had a transition phase (it was visible, as an example, in the Windows 3.11 APIs) when we used handlers a lot.
Basically an object has a pointer and it's methods are defined in the class it represents, so the code itself is not really defined in the object, but it is stored in memory where the class is referenced.
When calling methods the class identifies the object they refer to, so it used its specific properties.
Handlers are just a different way to achieve the same thing : they basically identify objects in an array (sort of, depending on the implementation) which contains the property.

Example :

(OOP)

myClass myObject = new myClass(whatever);
myObject.doSomething();

(with handlers)

int myObject = myCreateObject(whatever);
// this allocates in memory (an array) whatever is needed to store the properties of the object
myDoSomething(myObject);
// this calls a standard procedure which will use the properties located at the location [myObject] -which is an integer because it represents the index of an array- and will do something with them

So, this is basically the usage of handlers, they allow you to use a similar approach to OOP without really instantiating objects and using standard procedures and functions instead (as opposed to methods)

Hope it helps clarifying a bit.

Jamie said...

Hi Francesco,

Thanks very much for your explanation. It really did clarify things.. I'm trying to test ADC_SOC example program for F28027 on the launch pad. Hopefully it would work..

Thanks a lot for your help