28
Dec 12

Single channel - Single Sequence ADC10 example

This is a quick write-up to show how to configure and use the MSP430's ADC10 in single channel/single sequence mode. Lets start off with the entire section of C-code and go from there:

/*
 * main.c
 *
 * Basic ADC10 Example
 * GetSomeSystems.com
 */

#include "msp430g2553.h"

// Globals variables
volatile int adc10_value;

void main(void) {

 	WDTCTL = WDTPW + WDTHOLD;             // Stop watchdog timer

	// Configure Clock
	DCOCTL = CALDCO_8MHZ;	// MCLK = 8Mhz
	BCSCTL1 = CALBC1_8MHZ;
	// Configure Submain clock for TimerA
	BCSCTL2 &= ~SELM0;	// MCLK comes from DCO
	BCSCTL2 &= ~SELM1;
	BCSCTL2 &= ~SELS;	// Sub main clock comes from DCO
	BCSCTL2 |= DIVS0 + DIVS1;	// SMCLK = 1 MHz (DC0/8)


	// Setup ADC10
	ADC10CTL1 |= INCH_11 + ADC10SSEL_3 + CONSEQ_0  + ADC10DIV_0;
	ADC10CTL0 |= ADC10SHT_0 + ADC10IE + MSC + ADC10ON + SREF_0;

	ADC10CTL0 |= ENC + ADC10SC;	// start ADC10
	_BIS_SR(GIE + LPM0_bits);

}
/* *******************************
 * ADC10 ISR
 *	
 *	
 */
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{

	adc10_value = ADC10MEM;

// Maintanence
	ADC10CTL0 &=~ ADC10IFG;		// reset flag
	ADC10CTL0 |= ENC + ADC10SC;	// Begin new sampling sequence
}

The first thing I do is setup a global variable to hold the result from the ADC10 conversion. The volatile keyword here is important because it tells the compiler that this is a variable that could change dynamically. In other words, this variable could change outside of the normal program flow. Because we are using interrupts, (which can be thrown at any time) this keyword is important because it denotes this variable could change in a very abrupt way.

The next section:


 	WDTCTL = WDTPW + WDTHOLD;             // Stop watchdog timer

	// Configure Clock
	DCOCTL = CALDCO_8MHZ;	// MCLK = 8Mhz
	BCSCTL1 = CALBC1_8MHZ;
	// Configure Submain clock for TimerA
	BCSCTL2 &= ~SELM0;	// MCLK comes from DCO
	BCSCTL2 &= ~SELM1;
	BCSCTL2 &= ~SELS;	// Sub main clock comes from DCO
	BCSCTL2 |= DIVS0 + DIVS1;	// SMCLK = 1 MHz (DC0/8)


configures the clock system and disables the watchdog timer. Since I don't need a software check on my hardware system I've turned the watchdog timer off. Next, I configure the MCLK (main clock) to 8Mhz and the SMCLK (sub-main clock) to 1Mhz. I accomplish this by setting the sub-main clock division to 8. Both of these clocks run off of the DCO which is the digital controlled oscillator. This is software configurable and the equations to figure out your clock speeds (roughly because the DCO varies from part to part) is in the device specific datasheet.

Next I configure the ADC10 with:


	// Setup ADC10
	ADC10CTL1 |= INCH_11 + ADC10SSEL_3 + CONSEQ_0  + ADC10DIV_0;
	ADC10CTL0 |= ADC10SHT_0 + ADC10IE + MSC + ADC10ON + SREF_0;


	ADC10CTL0 |= ENC + ADC10SC;	// start ADC10


	_BIS_SR(GIE + LPM0_bits);

The first thing I do here is set the input channel to 11, which is the (vss-vdd)/2 channel. Effectively, this gives me Vss/2 or ~3.5V/2 ~ 1.75. Next, I set the clock select to sub-main, in this example AD10SSEL_3 sets the ADC10SSELx bits to "11" or 3, sub-main clock. This means that the sub-main clock will be the source clock for the ADC10 module. The CONSEQx is the conversion and sequence mode, which, in my setting would be single channel single sequence. The division (ADC10DIV) is where I can set the division and I set the division to 1. This means I don't divide by anything and send the 1Mhz clock into the ADC10 module.

The second line configures the ADC10 to a sample and hold time of 4 clocks, interrupts enabled, multiple sample and conversion, ADC10-ON, and the reference voltage to Vcc.

The last thing:

	ADC10CTL0 |= ENC + ADC10SC;	// start ADC10


	_BIS_SR(GIE + LPM0_bits);

enables the ADC10 module and starts conversions. After this the MCU goes to sleep. It only wakes up (the CPU because I'm in LMP0 mode) when I interrupt on an ADC sample and conversion completion.


/* *******************************
 * ADC10 ISR
 */
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{

	adc10_value = ADC10MEM;

// Maintanence
	ADC10CTL0 &=~ ADC10IFG;		// reset flag
	ADC10CTL0 |= ENC + ADC10SC;	// Begin new sampling sequence
}

Above is the ADC10 interrupt service routine, which is pretty simple.

/* *******************************
 * ADC10 ISR
 */
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)

This first section, tells the compiler to associate this service routine with the ADC10 interrupt.

Next,

	adc10_value = ADC10MEM;

// Maintanence
	ADC10CTL0 &=~ ADC10IFG;		// reset flag
	ADC10CTL0 |= ENC + ADC10SC;	// Begin new sampling sequence

I store the result of the ADC10 conversion in the "adc10_value" variable. After this, I reset the flag and start another conversion. Thats all you have to do to get the ADC10 up and running in single channel/single conversion mode.

Hope this helps!