Search Microcontrollers

Showing posts with label ADC. Show all posts
Showing posts with label ADC. Show all posts

Wednesday, June 11, 2014

Arduino Robot - PSU

The Rover is supposed to have plenty of devices on it, it will have a couple of arduino's (maybe 3), several sensors, two dc motors, a stepper motor, a radio...

That calls for some energy!
The easy solution would be to use the 4xAA battery holder provided with the kit, but there are a few reasons this could be a bit less than optimal :

1) If we plan to use rechargeable batteries, the output would be 4x1.2V = 4.8V which is probably enough to run the 5V arduinos, but a bit weak for the dc motors (they give you full power at 6V).

2) The battery holder consumes a bit of space and the pack will weight a bit

3) As the provided voltage is already quite low, when batteries discharge this will drop further to a level that is not sufficient to power the devices, limiting our ability to fully use the energy stored in the batteries.

I thought a better solution would have been a LiPO battery, a two cells one gives an average 7.4V which is high enough for the motors, the arduinos etc, leaving enough room for voltage drop.


You can easily find batteries like this one on ebay, I am no expert with these, but some common sense reasoning helps in making sure the one you select is ok for your project :
1) The only energy hungry parts in the rover are the two DC motors, they need 6V and can sink a limited amount of current. I found some data, but not really sure about it since no part number was mentioned, some sources say the max current could be 250mA per motor, most of the sources say it is way less... I assumed 250mA to be conservative
2) Most RC models have non geared and power hungry dc motors, so , whatever battery that works for them will do just fine with the motors we have. To be on the safe side I purchased a 25C (high discharge rate)
3) 1200mAH is not much, but comparable to the charge of NiCd rechargeable AA batteries, with the benefit that the higher voltage will allow us to better use the charge.

So, I went for a 1200mAH, 25C 2 cells (7.4V) LiPo battery, and got a balanced charger for it.

Probably I could feed the Motors directly from the battery, but again to play it safe and have a predictable result I decided to step down the voltage to about 6V for them.

We know there are different options to step down DC voltages, at least 3 come to my mind :
1) Use a linear voltage regulator (probably the most common)
2) Use a buck converter
3) Use the drop voltage of  (a series of) diodes

Linear voltage regulators are pretty handy, easy to use, they provide a clean output, but they are normally not efficient.
Here we are dealing with motors, no need for a clean power supply, but conversion efficiency might be important, so buck converters become a good option.
Diode drop voltage is not suitable because of efficiency, and does not regulate the output, it simply reduces the input voltage of a  given amount of volts (usually 0.6V per diode).

 So, I got myself a nice buck converter, they can be sourced really cheap now so they are becoming a good alternative for DC regulation , at least when you want to exceed 1 Amp.


Less than 2$ shipped, 3Amp maximum (leaves room for eventual future upgrades) and a wide range input/ output with a max 92% conversion efficiency (advertised).
Not too bad.

The problem with linear regulators is that the difference between the input and the output voltage is obtained mostly by dissipating energy as heat, therefore their conversion efficiency is pretty low.
If you have a power hungry device then efficiency becomes relevant, in fact if I have to supply only 100mA,5V and I have a 50% efficiency, I am wasting 5*0.1 W =0.5Watts of energy.
Now, if we had the same efficiency with a  3000mA output, then the wasted energy would have been 5*3 = 15W !! which also requires proper heat dissipation.

That said, we still need to power the arduinos and the other connected devices... here we definitely need a clean power source, and the power consumption is going to be pretty low, so linear regulators are a good option.

I selected to use the arduino pro mini because it is really small, cheap and provides the same features as the Uno board... well, almost.


The first thing to consider is that these boards are available at different voltages (3.3V and 5V) and different frequencies (8MHz and 16MHz).
I went for a 5V 16MHz one.
Second thing to know is that they do not include and FTDI interface like the Uno, so if you want to program them you need one external (I have a few always floating around on my desk, normally :) ).

The other important thing is that they do include a voltage regulator (the tiny chip on the middle-left part of the picture), so that you can feed them a non regulated input (5V to 16V)....
BUT the Uno contains a secondary regulator providing 3.3V supply for sensors and whatever you need to power at 3.3V, the pro mini does not have such secondary regulator.

We need 3.3V for the radio, the compass and other sensors... so that will be provided by our PSU board via another linear voltage regulator.
To be fair, here the internal voltage drop of the diodes would have been a decent solution : the input could have been the 5V regulated VCC from the Arduino pro, the current needed is few milliAmps, so 3 diodes in series would provide a 1.8V drop -> 5-1.8 = 3.2V,. good enough for our 3.3V supply.

I just preferred to integrate this in the PSU board, but realized I ran out of 3.3V regulators (actually I do have some SMD ones, but they would be a pain to be soldered to the proto board) so I went for a variable regulator, the famous LM317 (you should always have a few around).



Turns out it was not a good idea, let me explain why.
The LM317 is fairly easy to use, quite versatile, has a poor efficiency (but since we need few mA it would do) and can provide up to 1.5A output if mounted on a heat sink.
Way more than we need, right?
Nope, sometimes it is better to get "just what you need".
Turns out that I always disregarded one of the characteristics of this extremely useful device : it does not work if the load is less than 10mA.
Once I configured the circuit placing proper R1 and R2 (V = 1.25(1+(R2/R1))) I had no load and I was measuring the voltage, discovering it was ranging from 2.3V to 4.8V, oscillating in an unpredictable way.

Now, the radio will sink up to 14mA when transmitting, this would give us enough load, but when not transmitting the sum of all the 3.3V devices connected will be less than 10mA.

I temporarily patched the circuit adding a 330Ohm resistor in parallel to the load, that works but it is a sub-optimal solution as I am now constantly wasting 10mA of energy on the 3.3V rail!
It will have to do until the 3.3V regulators I ordered (got some 100mA ones) arrive.



On the proto board I soldered all the different parts, added an LED for every rail (battery, motor, 3.3v), some pins to connect directly the 3.3V devices etc.

The board itself is mounted with spacers and is protected with a rubber-ish sheet that also provides enough pressure to hold the battery in place.
The battery slides below the board.


(Note, it is all mounted on top of some wooden temporary chassis which I am using to study the optimal position of the various parts before drilling -or better let my young student drill- holes in the final one).

Another feature I wanted to add is the ability to monitor the voltages of the 3 rails, in order to detect when the battery is discharging or if one of the regulators is malfunctioning/ not properly configured.

To achieve that the Arduino's ADC is going to help, however some precautions must be taken.

The maximum voltages on the 3 rails are : 8.5V (maximum voltage of the battery), 8.5V (itf the buck converts 1:1) and 4.5V (if the 3.3V regulator is off).

The 5V arduino can accept maximum 5V on its ADC, exceeding this limit might actually burn it (and no, we don't want that, right?).

So, it is rather safe to feed the 3.3V rail to the ADC, but not the other ones, we will add a voltage divider (a couple of  resistors, you can google it up if you don't  know how it works), dividing by two.
The analogRead function provides a value between 0 and 1023 since the ADC has a 10 bit resolution and, if you do not use an external reference voltage, 1023 corresponds to the Arduino's VCC (roughly 5v).

Let's  say we read adcX on the pin connected to the 3.3V rail.
We know there is no voltage divider on that one, so 1023 would mean 5V ->

adcX : V = 1023 : 5

-> V = adcX * 5 / 1023   being V the calculated voltage.

For the other two rails, we know we have /2 a voltage divider, so 1023 would mean 10V instead of 5.

-> V = adcX * 10 / 1023

I did fetch the values (I have a second arduino connected to my pc via FTDI serial, it acts as a "serial to radio" bridge with a protocol I implemented for it. The radio connects to the arduino pro on the rover which itnerprets the commands and answers) and noticed they were a bit off.

My multimeter was giving me 3.38V on the 3.3V, while Arduino was returning 3.13V.

This is to be expected and it can be corrected easily.
What happens here is that the Vcc  provided by the regulator on the arduino is not exactly 5V, so the reference for the ADC conversion is not 5V either!

Do we need to know the exact Vcc then?
No, not necessarily, we can just compute the error between the value reported by the multimeter and the one calculated by arduino.



The ratio between those two values gives us a correction factor that can be applied to all calculations (in my case it was 1.093).
Normally you would want to check that ratio with a set of different measurements and eventually consider that as the linear regulator becomes warmer it might drift a bit altering again the ratio.
There are technical solutions for this, such as adding an external reference, but that exceeds the purpose of this experiment.

Overall, now the rover has a decent power management which includes feedback control, it has an almost reliable radio communication system... 
We are almost ready for Mars... or, well, maybe the backyard (for now) :)



Thursday, November 1, 2012

MSP430G2 - ADC / 2 - sampling an audio signal

I thought it would have been fun to sample an audio signal using the launchpad.
Before starting : The MSP430G2 is not designed with audio sampling in mind, so if you want to develop some good digital recorder or audio sampling device, you should probably look for more suitable devices.
... still, it can be done.

One issue is the limited amount of RAM in the mcu, which would prevent us to buffer and process the audio data internally (i.e. with an FFT).
The only hope we have is to get the data from the ADC and send it as fast as possible to the serial port, then use it in the pc itself.
You can achieve the same task, much easily and better using the internal sound card of your pc, but that would not be fun at all.

First thing I prepared a stereo cable to get the audio signal out of my computers headphone connection.
I used an old internal CD cable to which I soldered a stereo jack.


Then I checked I could get signals from both channels, using my scope (yup, to play with audio signals, the scope is quite handy).


Since we are going to feed this signal to our ADC converter, we need to ensure that its values are within the limits defined by Vref- and Vref+, meaning the two reference voltages used by the converter to define the minimum and maximum values to be sampled.
Most ADCs, including the MSP430G2 ADC10, can sample only positive signals (Vref- >+0) with a maximum value normally equal to their Vcc (3.3V for the launchpad).

Audio signals are obviously AC signals and typically they have their offset at 0V (you need to check that, it was the case for me, check the image above).
[The offset voltage is the voltage you obtain when you have the jack connected and no sound is played. When sound is played the offset can be eyeballed as the median voltage level]
This means that typically half of your audio signal has negative values, which would not work with the ADC.
We need to add a positive offset in a way that the incoming signal is never negative.

The other important thing is the amplitude of the signal (which you change with the volume settings).
I saw that most of the audio coming out from a youtube video had an amplitude of about 800mV, but pumping up the volume and playing some loud music showed a higher amplitude, reaching about 2V.

The ADC10 can use different Vref values, to make things easier we will use Vref- = 0V (GND) and we need to chose from 1.5V, 2.5V, Vcc (3.3V) for Vref+.

The signal amplitude we can take, assuming we can offset it exactly in the middle of our range is equal to Vref+ - Vref- , so having Vref- = 0V, the amplitude is equal to Vref+.
I would go for 2.5V because it is a bit higher than input signal amplitude, but not too much.

As a general rule, you normally want to amplify (or attenuate) the signal to make sure it fits nicely in your ADC band.

The reason why you want to  match your reference as close as possible is that, when sampling digitally, you divide your reference in a finite number of levels (1024 for a 10 bit ADC).
If the incoming signal has an amplitude which is covering just a small portion of your sampling band, then you are wasting sampling resolution.

Audio signal are pretty much symmetric in respect of their offset, so it's in general a good idea to move the offset in the middle of the sampling range, in our case to 2.5 / 2 = 1.25 V.
The typical way to achieve this is via a simple circuit based on an op-amp, but for this example (remember, this is an experiment, don't design devices in this way, it's probably not  a good idea!) I decided I wanted something simpler.

I just needed something capable to deliver a clean 1.25V and stick it in series with my signal.
That sounds like a fully charged rechargeable AA battery to me :)


A battery holder and a couple of crocodile contacts helped to put the battery in series with the signal before entering the scope probe (easier than dealing with op-amps, right?).
I checked with the scope setting the cursors to my Vref values (0 and 2.5V), this is what I obtained :


Eureka! This looks definitely a signal that could be handled by the ADC10.

Let the fun begin

It's time to start preparing our application.
The idea is to sample both channels (stereo!!) and eventually transfer the sampled data via UART.
The msp4302553 has 8 external Analog inputs mapped on port P1 (P1.0 to P1.7), we need to select two suitable channels in a way that they do not overlap with other functions we might need.
I will use the serial port, most likely UART, so I will steer clear from the pins used by that functionality (even if for this test I am planning to use the onboard FTDI chip that sends the uart data to the usb connection).
The 2553 datasheet has it all : our A3 / A4 channels (P1.3 , P1.4) look like good candidates.

Also in the datasheet we find  that there is no real need to set the multiplex values (P1SEL, P1SEL2) or the P1DIR register when using pins as ADC channels, because the ADC10 will set them for us (everybody say with me : "Thanks mister ADC10!") using the  ADC10AE0 register.


The ADC10 can do some interesting things when sampling multiple channels :
It can automatically sample a sequence of channels (that is actually quite cool for our application).

When sampling a sequence of channels, we need to specify the highest channel  we want to sample and the mcu will start a loop in which it will sample all the channels down to A0,
from the user guide :
"A sequence of channels is sampled and converted once. The sequence begins with the channel selected by INCHx and decrements to channel A0. Each ADC result is written to ADC10MEM. The sequence stops
after conversion of channel A0."

Fortunately, despite what stated in the user guide, it seems it is possible to set how many channels must be sampled, the DTC (a sort of dma controller, dedicated to the ADC10) can transfer the values to a defined memory area.


Time to write some code.


#include <msp430g2553.h>

unsigned int sampling;
unsigned int buffer[2];

void clockConfig()
{
 // configure the CPU clock (MCLK)
 // to run from DCO @ 16MHz and SMCLK = DCO / 4
 BCSCTL1 = CALBC1_16MHZ; // Set DCO
 DCOCTL = CALDCO_16MHZ;
 BCSCTL2= DIVS_2 + DIVM_0; // divider=4 for SMCLK and 1 for MCLK
}

void adcConfig()
{

  // ADC10SSEL_0 :  ADC10OSC
  // ADC10DIV_0 : ADC10 Clock Divider Select 0. Roughly 5MHz
  // INCH_4 : highest channel we are going to sample
  // CONSEQ_1 :  Sequence of channels
  ADC10CTL1 = ADC10SSEL_0 + ADC10DIV_0 + INCH_4 +  CONSEQ_1; //
  // SREF_1 : VR+ = VREF+ and VR- = AVSS
  // ADC10SHT_0 : Sample & Hold = 4 x ADC10CLKs
  // REF2_5V : ADC10 Ref 0:1.5V / 1:2.5V;
  ADC10CTL0 =  SREF_1 + REF2_5V + REFON + ADC10SHT_0 + MSC + ADC10ON + ADC10IE;
  ADC10AE0 |= BIT3 + BIT4;  // adc option for channels 3 and 4
  ADC10DTC1 = 0x02; // number of channels to be sampled
  __delay_cycles(1000);  // Wait for ADC Ref to settle
}

// temporary interrupt routine to check that the ADC sequence is complete
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
  sampling = 0;
}

void main(void)
{
 WDTCTL = WDTPW + WDTHOLD;
 clockConfig();
 adcConfig();
 __bis_SR_register(GIE);  // Enable interrupts
 while (1)
 {
  ADC10CTL0 &= ~ENC;
  while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
  sampling = 1;
  ADC10SA = (unsigned int)&buffer;        // Data buffer start
  ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
  while(sampling>0); // wait until the sampling sequence is complete
 }
}


At this point you should refer to the previous post in which I introduced the ADC10, if you did not read it before.

Basically I opted to use the internal ADC oscillator at its maximum frequency.
For Vref I used VRef+ = internal reference and VRef- = GND
The internal reference is configured at 2.5V (default is 1.5V) and the sample & hold time is the shortest possible (4 cycle of ADC10CLK).
The MSC bit is set, that tells the ADC to keep converting data once the encoding request is triggered, then DTC controller will post it to the buffer starting at the address of the buffer variable (ADC10SA =(unsigned int)&buffer; )
It seems that the buffer start address must be set each time before starting a new conversion.

There are four operating modes selected with the CONSEQ constants :

#define CONSEQ_0               (0*2u)         /* Single channel single conversion */
#define CONSEQ_1               (1*2u)         /* Sequence of channels */
#define CONSEQ_2               (2*2u)         /* Repeat single channel */
#define CONSEQ_3               (3*2u)         /* Repeat sequence of channels */

I chose to sample a single sequence of channels, the DTC will drop the data in my two 16 bit integer buffer.

I also use a sampling variable to check if the sequence is complete or not, this is a simple temporary solution, in future implementations we might simply use the interrupt routine to send the data via uart.

That's about it for today, I debugged the program and -surprise surprise- my buffer is actually filled with values that vary at each loop, indicating that the DTC is sending it samples.
I also notice that with no audio incoming my channels they are at values around 510, which matches pretty closely my ideal offset (1024/2 = 512).

I might develop this experiment further and actually send some data to the pc... but that's going to be another post.  

Another thing to look into is to test if we can get away without the interrupt and just poll the ADC BUSY bit, it might actually be interesting to maximize the data transfer to the pc.


Sunday, October 28, 2012

MSP430G2 - the ADC10 peripheral /1

In the previous post I illustrated a few basic concepts about Analog to Digital Converters.
Having them integrated into MCUs makes it really easier to use them, we are going to play a bit with the MSP430G2553 ADC10 converter.

The two main control registers to drive the ADC10 are ADC10CTL0 and ADC10CTL1.
Using them we can set the reference voltages, which channel to be sampled, we can turn on and off the reference voltage and the peripheral itself.

The MSP430G2 family is designed to be a low power solution, this is why the peripherals and features that may potentially use some energy are turned off by default.

Finally the sampled result can be easily recovered from the ADC10MEM register :
int mySensor = ADC10MEM;

Sample & Hold and conversion

During the sampling process, I explained in the previous post, a reference voltage is V(t) is compared with the voltage coming from the signal.
If the signal fluctuates in this process, it might be impossible to compare it with the reference.
For this reason a "sample and hold" functionality is implemented, you can see some details here.

This means that the value of the signal to be sampled is maintained constant during the conversion.

    from wikipedia

This time is defined by the SHS (Sample and Hold Source) clock which, in the MSP430 can be one of the following :


00   = ADC10SC  (ADC10 internal oscillator, about 5MHz and can be  divided from 1 to 8)
01   =  Timer_A.OUT1
10   = Timer_A.OUT0
11   = Timer_A.OUT2


In this way we basically define the maximum sampling rate which is the result of the sampling time, being the sum of the Sample and Hold phase plus the conversion phase.

from the TI MSP430 User Guide

The S&H phase is software selectable  (in ADC10CTL0) in 4,8,16 or 64 ADC10CLK cycles, a higher value would be used to reduce high frequency noise.
The conversion part is then executed in 13 ADC10CLK cycles (thanks to a series of approximations performed internally).

Playing a bit with numbers, we can verify that, at a minimum we need 4 + 13 = 17 cycles.
Assuming the ADC10CLK is the 5MHz (note : we should not expect it to be EXACTLY 5MHz, it's a digital oscillator and it might have some tolerance) Internal source

1 cycle = 1 / 5.000.000 s = 200ns

The sampling time is then 17 * 200ns = 3.5us
The sampling time is 1 / 3.5us = roughly 294 Ksps (Kilo Samples Per Second)
Data then needs about 1 or 2 MCLK cycles to be made available in the ADC10MEM register (it uses an internal DMA), so the actual rate is slightly lower and it depends on the CPU frequency.

The data sheet claims a sampling rate exceeding 200 Ksps, which is in line with what we calculated and I believe is pretty good for a 16MHz mcu.

Reference Voltages

The ADC will return 0 if the signal is lower than or equal to the Vref- voltage and will return 1023 if it is higher than or equal to Vref+.

The ADC10 has different options when it comes to Reference voltages, it can use internal precisely  generated voltages (1.5V or 2.5V) , it can use Vcc as Vref+ and Vss (Ground) as Vref- or it can get them from two external pins.
All these parameters are controlled via the ADC10CTL0 register.

In the user guide we find :


SREFx Bits 15-13 Select reference.
SREF_0 000 VR+ = VCC and VR- = VSS
SREF_1 001 VR+ = VREF+ and VR- = VSS
SREF_2 010 VR+ = VeREF+ and VR- = VSS. Devices with VeREF+ only.
SREF_3 011 VR+ = Buffered VeREF+ and VR- = VSS. Devices with VeREF+ pin only.
SREF_4 100 VR+ = VCC and VR- = VREF-/ VeREF-. Devices with VeREF- pin only.
SREF_5 101 VR+ = VREF+ and VR- = VREF-/ VeREF-. Devices with VeREF+/- pins only.
SREF_6 110 VR+ = VeREF+ and VR- = VREF-/ VeREF-. Devices with VeREF+/- pins only.
SREF_7 111 VR+ = Buffered VeREF+ and VR- = VREF-/ VeREF-. Devices with VeREF+/- pins only.


The constants SREF_0 to SREF_7 are conveniently defined in the standard .h file.
In the same file we also find the other constants needed to enable the internal reference and to set it to 1.5 or 2.5 V


REF2_5V  Reference-generator voltage. REFON must also be set.
0 1.5 V
1 2.5 V
REFON Reference generator on
0 Reference off
1 Reference on


There anyways restrictions on the voltages that can be applied to the analog input (should be between 0 and Vcc) and to the eventual external references :
Vref- (specifically for the 2553, check the DS for other devices) should be between 0 and 1.2V
Vref+ (same considerations) should be between 1.4V and Vcc

This said, if you are planning to sample a higher voltage (I tried with the mains ; 250V AC) you need to use a voltage divider (with offset regulation for AC signals) and to make sure the output of your divider is always in the expected range (check with a scope if you can).

If you don't need to sample with a high frequency and you are using an internal reference, what you may want to do is to turn on and off the internal Vref to reduce power consumption.
There is also an option to burst" it so it turns off and on as needed by the ADC, however you should be aware of the fact that the internal Vref  takes a small amount of time to settle, and the sampling can happen only when it is stable.
tref (the time Vref internal needs to settle at 99.9% of its value) is 30us.

Finally, the peripheral can fire an interrupt once the conversion is done, so it's common to start the process, enter in Low Power Mode and then wake up receiving the interrupt, get the value from ADC10MEM, do whatever needed with it and start the process again.

In the next post I will present some sample code using the ADC.


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