16 October 2011

Tutorial 15b: Using ADC10

Like the Comparator_A+ peripheral, ADC10 has a wide range of operating modes and features. It can also integrate with other peripherals (such as Timer_A, of course), making it a very powerful tool in scientific measurements. Today we'll look at basic configuration of ADC10 in preparation to do a full scientific experiment using the MSP430. Keep in mind that this tutorial requires a device with the ADC10 peripheral, such as the G2231. The G2211 chip that comes with the LaunchPad will not work in this tutorial.

First, let's examine some of the features of the ADC10 peripheral. ADC10 of course requires a clock, and can source from any of the three clocks in the MSP430 (and subsequently from a crystal, DCO, or VLO). In addition, ADC10 comes with its own internal clock that can be used independently of the system clocks. This clock is typically in the 5 MHz range, but is uncalibrated and thus varies from chip to chip, as well as with operating voltage and temperature. A major advantage to the internal oscillator is that it can remain in operation even when other clocks are powered down in an LPM.

ADC 10 can connect to up to 16 different inputs. Typically, 8 of these are external inputs, 4 are internal inputs, and 4 are other references (on some devices, they are extra external inputs for a total of 12). The G2231 device has 8 external inputs (on each of the pins in P1), and also has a temperature sensor built into the chip as an internal input (in addition to the other three internal inputs, which have to do with voltage reference comparisons).

Like the Comparator_A+, ADC10 requires a reference voltage for operation. In fact, it can operate with two reference voltages for the upper and lower bounds of conversion. The upper reference can be anywhere between 1.4 V and Vcc (up to 3.6 V). The lower reference can be between 0 and 1.2 V. There are two references available inside ADC10 at 1.5 V and 2.5 V, and it can also use Vcc in addition to an external reference.

Finally, the ADC10 also has 4 operating modes. Two of these modes sample only a single channel. The other two modes cycle through a specified set of the 16 possible inputs. Each single/sequence type can be done only once, or repeated. (Note: in a sequence mode, you must use the inputs in order. If you want to sample 3 inputs, you must use A0, A1, and A2. Unfortunately, the only way to sample arbitrary inputs is to use single channel mode and change channels in software.)

That covers the bulk of the dizzying ways to configure the ADC10; it's a lot to sort through, so keep in mind that we're only going over it to have the different things you can do in the back of your mind. The best way to learn how to use all of the features is by example, so let's look at a simple example by modifying the capacitance meter project to a voltage meter. This meter will be restrictively useful, as it will only be able to measure voltages between 0 and 3.3 V, but it will illustrate the idea. We'll display the output on the LCD as before, but the code can easily be modified to pause in the debugger to find the result as was done at first with Comp_A+ if you don't have an LCD. For the LCD display, we'll use the single-channel mode and repeat the measurement in software, which makes it easier to use the debugger to see the result.

The x2xx User's Guide gives a set of diagrams to explain the process used in each of the four modes. For example, in the repeat single-channel mode, the peripheral is turned on and enabled. The ADC10 is triggered to start a conversion, which is stored upon completion. If interrupts are being used, the flag is set, and the ADC10 returns to on of three steps, depending on just how we set it up. Keep in mind this all happens within the ADC10 module itself, leaving the MSP430 free to perform any other actions it needs to. You can use the ADC10 interrupts to do something with the code after samples are taken.

Our code will instead use single-channel mode, which is very similar to the repeat single-channel mode, but without the repeat part. =) I've chosen this mode because I won't be using a low power mode, and it's easier to coordinate timing so that a sample isn't taken and finished while waiting for the LCD to update. While a new conversion is occurring, the code will trap in a loop before writing the measured sample to the LCD for display. Once that's finished, a new conversion will be started to update the measurement.

There are 8 registers associated with ADC10; of these, 4 are used to configure the peripheral. One is used to store the individual samples, and three are used to control transferring the sample data for storage. (More on this later; for now we're going to keep it simple and only worry about 5 registers!)

While this sounds like a lot of configuration, fortunately two of the registers are used solely for configuring the inputs. ADC10AE0 enables the ADC function of the external pins being used. (This is necessary, because P1SEL changes the operation of those pins to a function other than ADC; since it's a binary value, P1SEL can only configure two different operations. This register frees up those pins for uses other than just ADC!) ADC10AE1 performs a similar function, but only for devices with more than 8 analog inputs.

We'll look at the other two configuration registers in more detail. ADC10CTL0 handles some of the base configurations of the peripheral-- the voltage references, sampling time and rate, and handling power and interrupts for the ADC. ADC10CTL1 controls the inputs, clock, mode, and data formatting. Here are the essential pieces for each register (we won't cover all of them today):
  • SREFx (Bits 15-13): These select one of 8 different configurations for the upper and lower references for the ADC.
  • SHTx (Bits 12-11): These select 4 different sampling times for the ADC. The voltage is held constant during conversion by charging a capacitor; these control the amount of time you allow for charging. Obviously more time ensures a more accurate sample, but limits the sampling rate achievable by the device and risks having the voltage being measured change during the sampling time. You can select 4, 8, 16, or 64 clock cycles (of ADC10CLK).
  • REF2_5V, REFON (Bits 6,5): Selects between 1.5 V and 2.5 V references and turns the reference on/off.
  • ADC10ON, ENC,ADC10SC (Bits 4,1,0): Turns on the ADC, Enables Conversion, and Starts Conversion respectively.
  • ADC10IE, ADC10IFG (Bits 3,2): Interrupt enable and flag.
  • INCHx (Bits 15-12): In single channel mode, selects the channel to sample. In sequence mode, selects the highest channel to sample.
  • ADC10DF (Bit 9): change between straight binary data and 2's complement data.
  • SSELx, DIVx (Bits 4-3,7-5): chose the clock source and divide the clock frequency by 1-8.
  • CONSEQx (Bits 2-1): Select the sequence mode.
  • BUSY (Bit 0): a read-only flag that indicates when the ADC is in the middle of a sample/conversion cycle.
 That's a very brief overview; we can't cover all of the features in detail in a reasonable introductory tutorial, so we'll examine more advanced features in the future as they come up. In the mean time, read the User's Guide and documentation to understand more of what all of these do.

Last of all, we'll mention the ADC10MEM register. When conversion takes place, the value is stored and read from here. If ADC10DF is cleared (value 0), we can read this straight away: 0x00 is equivalent to the lower reference, 0x3FF is equivalent to the upper reference, and the intermediate values are a line between the two points. If ADC10DF is set (value 1), the value is stored in 2's complement. This can be useful for transferring data in some configurations, but we'll not need it today.

That does it for a brief (but long!) summary of the basics. Let's look at the simple volt meter now. The code for this project can be seen in VMeterG2231.c. It will require the simple_LCM library from the previous tutorial. There's very little that's new here, and the code should be clear by itself. It uses input A1 on P1.1. You can test the code by using a potentiometer connected between Vcc and ground and connecting the wiper to P1.1. When you turn the potentiometer, you should see the corresponding value change on the LCD.

So much for the basics; next time we'll look at how to store the data for later analysis and start working on an actual experiment.

Reader Exercise: How can you use this same code to measure a larger voltage range? Hint: a simple way to do it uses only two passive components. A trickier task is to be able to measure positive and negative voltages; can you think of a way to do this even though the MSP430 can't use a negative voltage reference? Hint: an op amp might help.


Francesco said...

Excellent BLOG
I have a question about ADC:
If I want a sampling rate of 0.8us (1.25 Mhz) use default configuration with:


to set a specific sampling rate range:
I must set the DCO and then ADC10SSEL_2?

a step by step tutorial for set sampling rate would be very useful!!!

David said...


Thanks for the comment; I'm not entirely clear what you're asking-- do you mean you want the time required to take a single sample to be 0.8 us, or that you want to take a sample ever 0.8 us?

ADC10SSEL_x will select which clock is being used for the ADC10-- the internal, ~5 MHz clock, SMCLK, ACLK, etc. Each sample takes a specific number of clock cycles, so you can slow down the conversion process by using a slower clock if you want.

If you're hoping to take samples at a 1.25 MHz rate, you might look at the Battery Profiler Experiment I just posted today-- this may help you see how to time the samples at a given rate, though I'm doing it at 0.1 Hz rather than 1.25 MHz. The principle should be the same, though. Use Timer_A to trigger conversions at specific intervals. Your ADC10 clock will need to be substantially faster than the sample rate, of course-- for 1.25 MHz you will probably need about 16 MHz to be able to complete a sample in time for the next conversion. I don't know of hand how fast you can get with ADC10, though the Family User's Guide and datasheets may tell us that.

Let me know if you can get it to work, or if you have any other questions!

Steven said...


I seem to be having a bit of trouble in understanding how to set a specific pin to be used as ADC10.

e.g., I am trying to use P2.2 as ADC10, and I am unsure how to actually set this as the pin in code.

Any help would be much appreciated,

David Olson said...

It does depend on which device you're using, but assuming you're using a LaunchPad you won't be able to use P2.2. The ADC10 is only able to connect to certain GPIOs, which are listed in the device datasheets as having connections to A1, A2, etc. Using single channel mode, you can set which pin is being used by configuring the INCHx bits. As far as I can tell, none of the devices that plug into the LaunchPad have ADC10 connections on P2. You can use any of the P1 pins instead, of course.

Anonymous said...

hello david! i have a question. can you use adc to generate pwm?

David Olson said...

Anon: not that I'm aware; my understanding is that ADC is essentially an input: it takes an analog value external to the chip and turns it into a digital value inside the chip. PWM is very easily implemented using the timer A capture compare features, and I'll probably do a tutorial on that sometime soon as it's been requested a number of times.

Do you mean can you use ADC as an input to control the PWM output? If that's the case, then yes the MSP430 can do that quite easily. Maybe I'll use that as an example for the tutorial.

Mattias said...

David: Thanks for a set of very helpful blog posts!

A minor detail: Close to the end you mention that "0xFF is equivalent to the upper reference". Since this is a 10-bit ADC, I guess the value matching the upper reference should be 0x3FF?

David Olson said...

You're right! Thanks for catching that; I've fixed the error in the post.

Eric Starr said...

In VMeterG2231.c, you define dv to be 0.0032258 and multiply this value with ADC10MEM to convert to volts. Can you explain this? What does 0.0032258 represent?

David Olson said...

That value is the step size for the ADC as configured; since we're using the Vcc as our reference voltage, the ADC creates digital values from 0 to 1023 to represent 0 to Vcc, in this case 3.3 V. If you divide 3.3V by 1023, you get the value of dv. The measured voltage is n * dv, where n is the value returned by the ADC.

Note that if you use a different voltage as the reference (say, 2.5 V instead), you'd use that to figure your dv value.

Hope that helps!