FPGARelated.com
Blogs

MSP430 LaunchPad Tutorial - Part 3 - ADC

Enrico GaranteJune 25, 20138 comments

In this new episode of our journey into MSP430 I will explain the basics of Analog to Digital Conversion on the MSP430G2231.We will write a program that will read an ADC channel and will toggle some leds based on the result of the conversion. 

Quick Links

We start as usual with the inclusion of the header file for the MSP430G2231, the leds stuff and with the definition of a variable that will store the result of the conversion. We also declare a function that will initialize the ADC module.
[code c]

#include "msp430g2231.h"

#define LED0 BIT0 

#define LED1 BIT6 

unsigned int value=0;

[/code]

The ConfigureAdc function will setup the ADC module. 
With the ADC10CTL1 register we select the ADC input channel and the ADC clock division (from the ADC10 internal oscillator, 5MHz).
Then we select the internal voltage reference for the ADC module (Vcc) with SREF_0, turn it on (ADC10ON) and enable the ADC10 interrupt (ADC10IE).
Finally we connect P1.5 to the ADC module by setting the ADC10AE0 register accordingly.

[code c]

void ConfigureAdc(void)

{

   /* Configure ADC Channel */

   ADC10CTL1 = INCH_5 + ADC10DIV_3 ; // Channel 5, ADC10CLK/4

   ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON + ADC10IE; //Vcc & Vss as reference

   ADC10AE0 |= BIT5; //P1.5 ADC option

}

[/code]

In the main function we stop as usual the Watchdog timer, set up the clocks and the leds. 

Then we select the input pin for the ADC module by setting the P1SEL register. After that, we initialize the ADC with ConfigureAdc() and enable the ADC interrupt, that will let us know when a conversion has been made and its result is ready.

[code c]

void main(void)

{

   WDTCTL = WDTPW + WDTHOLD; // Stop WDT

   BCSCTL1 = CALBC1_1MHZ; // Set range

   DCOCTL = CALDCO_1MHZ;

   BCSCTL2 &= ~(DIVS_3); // SMCLK = DCO = 1MHz

   P1DIR |= LED0 + LED1;

   P1SEL |= BIT5; //ADC Input pin P1.5

   P1OUT &= ~(LED0 + LED1);


   ConfigureAdc();

   __enable_interrupt(); // Enable interrupts.

[/code]

After the initialization code, we wait some cycles to let the ADC voltage reference settle to a stable level and then we start the conversion by setting the ADC10CTL0 register. 

Then we enter a low-power mode to save battery juice: we'll have the result of the conversion directly in our "value" variable from the ADC10MEM register thanks to the ADC10 interrupt. The interrupt will then return to active mode and continue with the main. At last we simply toggle the leds on the LaunchPad according to the number stored into "value" (don't forget that we have a 10-bit ADC module, so the maximum value will be 1023).
You could do some further calculations, maybe averaging the results to get a more precise reading.

[code c]

   while(1)

   {

      __delay_cycles(1000); // Wait for ADC Ref to settle

      ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start

      __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled

      value = ADC10MEM;

      if (value>511)

      {

         P1OUT &= ~(LED0 + LED1);

         P1OUT |= LED0;

      }

      else

      {

         P1OUT &= ~(LED0 + LED1);

         P1OUT |= LED1;}

      }

   }

}

[/code]

The next one is the ADC10 interrupt service routine, as we've seen for the TimerA module. 

We simply exit the low-power mode to let the main program make some computations with the result from the ADC.

[code c]

// ADC10 interrupt service routine

#pragma vector=ADC10_VECTOR

__interrupt void ADC10_ISR (void)

{

   __bic_SR_register_on_exit(CPUOFF); // Return to active mode

}

[/code]

And that's it! Pretty simple huh? Let's see something more advanced.

Multiple conversions

Now we want to obtain the result of AD conversion from multiple input pins all at once in each loop.

First of all, we define an array that will store the conversions results from each channel

[code c]

#define ADC_CHANNELS 5 //We will sample 5 channels 

unsigned int samples[ADC_CHANNELS]; 

[/code]

In the main loop, before the start of conversions, we add
[code c]

ADC10CTL0 &= ~ENC;

while (ADC10CTL1 & BUSY);

ADC10SA = (unsigned int)samples;

[/code]

With this line we specify that the conversions results should be automatically stored in the previously created array: in such way, there's no need to access the ADC10MEM register anymore. The data will be immediately ready for calculations. 

In this case the ConfigureAdc function becomes

[code c]

void ConfigureAdc(void)

{

   /* Configure ADC Channel */

   ADC10CTL1 = INCH_4 + ADC10DIV_0 + CONSEQ_3 + SHS_0; //Multi-channel repeated conversion starting from channel 5

   ADC10CTL0 = SREF_0 + ADC10SHT_2 + MSC + ADC10ON + ADC10IE;

   ADC10AE0 = BIT4 + BIT3 + BIT2 + BIT1 + BIT0;

   ADC10DTC1 = ADC_CHANNELS; //ADC_CHANNELS defined to 5

}

[/code]

CONSEQ_3 enables the multi-channel repeated conversion mode.
With INCH_4 we select the starting channel (in this case we start from channel 5, then we sample channel 4 and so on all the way down to channel 0). We enable ADC on those pins with the ADC10AE0 register.
MSC enables multi-channel conversions.
ADC10DTC1 defines how many blocks of data (one for each channel conversion) we transfer into the array adc_channels that we previously created.

Variable watching

Here's an image that will explain how to watch variables with CCS while debugging (click on it to zoom). In this way we can read the value of the AD conversion at every program loop.

Variable watching in CCS

In the next episode we will learn to use the UART connection, so stay tuned!


[ - ]
Comment by Xavier_ANovember 14, 2016

Thanks a lot for the help, it works on MSPG2553!! Greettings from México

[ - ]
Comment by Mik23November 23, 2014
Hi.
Thanks for the good tutorials, they have been lifesaver so far.
I'm having some trouble with this one, and I've no idea where to start looking for problems.

Granted I'm using a 2553 instead of a 2231, I sheepily tried the fix a guy used to make the first tutorial work on his 2553, but with no result.

Can you show me the right direction where to look to find a fix? Thanks.
[ - ]
Comment by Mik23November 23, 2014
Nevermind, I just didn't know I had to connect the chip to the same gnd the voltage generator was connected to.
That fixed everything.

I'd delete the previous comment if I could, but I can't so I'll post this if there is someone as dumb as me in the future.
[ - ]
Comment by istemihanApril 25, 2015
Hi, this tutorial is absolutely amazing. However, in this chapter I am getting this errors:

//------- code below
P1OUT &= ~(LED0 + LED1);

P1OUT |= LED1;}

}

}

} /// this is where I get first error: #171 expected a declaration

// ADC10 interrupt service routine

#pragma vector=ADC10_VECTOR // this is second error: #609 this pragma must immediately precede a declaration

__interrupt void ADC10_ISR (void)

{

__bic_SR_register_on_exit(CPUOFF); // Return to active mode

}
//--------code end


I work with g2553 however, I checked register names and specifics. They are mostly same and there is no problem there. Can you give me any advice? Thanks.
[ - ]
Comment by istemihanApril 25, 2015
Hi, I figured it out you forgot an extra '}' end of the else line and it is almost impossible to notice :)

Quote:

P1OUT |= LED1;}

[ - ]
Comment by cravisjan97@gmaiMay 22, 2015
Thanks for the tutorials.I want to extend your code in finding the time for which ADC value>511.
This is my code snippet and the if(t<3000000) is always executed.Please help me.
if(value>511)
TA1R=0;
// Assigns the value held in ADC10MEM to the integer called ADC_value

else
{
t=TA1R;

if(t<3000000)
{P1OUT&=~(BIT0+BIT6);
P1OUT|=BIT0;
}
else
{P1OUT&=~(BIT0+BIT6);
P1OUT|=BIT6;

}

}
[ - ]
Comment by cravisjan97@gmaiMay 22, 2015
Thanks for the tutorials.I want to extend your code in finding the time for which adc value>511.
This is my code snippet.The if(t<3000000) is always executed.Please help me.

if(value>511)
TA1R=0;

else
{
t=TA1R;

if(t<3000000)
{P1OUT&=~(BIT0+BIT6);
P1OUT|=BIT0;
}
else
{P1OUT&=~(BIT0+BIT6);
P1OUT|=BIT6;

}

}
[ - ]
Comment by Yara22November 2, 2015
Your tutorials are great! But I have problem undestanding this in particular. I just don't undestand if I have to send voltage to the Pin 5 in order to do conversions; I mean, connects a voltage generator or something like that.

To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.

Please login (on the right) if you already have an account on this platform.

Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: