In this MSP430 ADC tutorial the MSP430G2253 will be used as an example.  Texas Instruments ships the MSP430G2253 microprocessor with the latest Launchpad, the datasheet for the MSP430G2253 can be downloaded here

The MSP430G series devices are supplied in a number of packages with different specification and peripherals, there are 2 types of ADC, ADC10 and ADC12.  The ADC10 is a 10bit analog to digital conversion and the ADC12 is a 12bit analog to digital conversion.  The MSP430G2253 only incorporates the ADC10, apart from having greater resolution the ADC12 does not differ that much from the ADC10. Suggested reading on the main differences between the 2 types of ADC is the MSP430x2xx Family User’s Guide, which at the time of writing is release SLAU144J here

Before reading further if you are having trouble understanding how the registers work, and how the C code updates the individual register settings?  It would be worth reading my MSP430 Programming Tutorial, Part 1 covers the basics and Part 2 gives clear examples.  You can find them here Part 1 and Part 2.

Firstly lets clarify what 10bit resolution is and how this maybe relevant to the accuracy of a project.  10bit means 2 to the power of 10, or 2x2x2x2x2x2x2x2x2x2x2 which equals 1024, the 1024 is the number of sample steps taken over a given range (in the case of microcontrollers it’s actually 0 to 1023).

So now we understand the term regarding bit size, we need to find the range.  The range is defined as what voltage range the ADC accepts, to find this we need to consult the datasheet for the MSP430G2253, an extracted image from the datasheet can be seen below.

So from this the voltage range for the ADC can be determined as 0-3 volts.  So now that we have the range and the number of sample steps, we can now determine the resolution of the ADC with some simple maths.  The ADC12 has also been shown to help illustrate the step size changes and how this affects accuracy.

So using an example we can see what this would mean in real terms, if 1.5V was fed into the ADC input.

Now the value of 512 or 2048 is the value that would be stored as a variable by the ADC, this can actually be seen in Code Composer Studio and will be demonstrated later on in this tutorial.

The ADC10 module on the MSP430G devices is configured by the user through software. There are various registers used to change the way the ADC10 operates dependent on the application requirements.

This allows the user to enable the specific GPIO pin/s for analog input.  The command used for this register looks something like this ADC10AE0 |= 0x02;  (0x02 is selecting the ADC A1 or port P1.1).

This is not available on the MSP430G2253 device.

Control register 0 has various parameters that can be set which are listed below, the MSP430 Family Guide SLAU144J has a full list of all these parameters with more in depth descriptions.    The command used for this register looks something like this ADC10CTL0 = SREF_1 + ADC10SHT_2 + REFON + ADC10ON + ADC10IE;

SREFx – Select Reference.  There are varies parameter options that can be set for select reference.

ADC10SHTx – Sample and Hold Time.  This adjust how many clock cycles the sampling period will be.

ADC10SR – Sampling Rate.  Adjusts the sampling rate between ~200ksps or ~50ksps

REFOUT – Reference Output.  Reference voltage output to GPIO pin with Vref capability

REFBURST – Reference Burst.  Disables the internal reference voltage automatically when the ADC10 is not converting.

MSC – Multiple Sample Conversion.  Allows multiple samples to be taken automatically.

REF2_5V – Reference Generator Voltage. selectable between 1.5V and 2.5V

REFON – Reference Generator On.  Turns on the internal reference voltage.

ENC – Enable Conversion.  Enables the conversion and can be used to stop a conversion by resetting ENC.

Control register 1 has various parameters that can be set which are listed below, the MSP430 Family Guide SLAU144J has a full list of all these parameters with more in depth descriptions.    The command used for this register looks something like this ADC10CTL1 = INCH_10 + ADC10DIV_3 + CONSEQ_2;

INCHx – Input Channel Select.  This selects the GPIO pin/s used for the ADC input.

SHSx – Sample and Hold Source Select.

ADC10DF – Data Format.  Straight binary or 2s compliment.

ISSH – Invert Signal Sample and Hold.

ADC10DIVx – Clock Divider.  Allows the chosen clock source used for the ADC to be divided down.

ADC10SSELx – Clock Source Select.  Allows the user to select which of the MSP430 clocks to be used for the ADC.

CONSEQx – Conversion Sequence Mode.  Allows difference conversion sequences on single or multiple channels.

ADC10BUSY – Busy.  Indicates an active sample or conversion operation.

This is the conversion memory register.  The command used for this register looks something like this ADC_data = ADC10MEM;

#### ADC10 Data Transfer Control Register 0

Data transfer control (DTC) register 0, has various parameters for the control of the data transfer. The DTC automatically transfers conversion results from ADC10MEM to other on chip memory locations.  The command used for this register looks something like this ADC10DTC0 |= ADC10TB + ADC10CT;

#### ADC10 Data Transfer Control Register 1

Data transfer control register 1, defines the number of transfer in each block.  The command used for this register looks something like this ADC10DTC1 = 0x20;

ADC10SAx – Start Address.  The command used for this register looks something like this ADC10SA = 0x0200;

## Example 1

The code pasted below will perform a  single read from GPIO P1.3 and then assigns the value of ADC10MEM to a variable called ADC_value.

### Example 1 Code

The link below contains the zip file with the full example 1 C code, there is a small advert page first via Adfly, which can be skipped and just takes a few seconds, but helps me to pay towards the hosting of the website.

Example 1 Code

I take great care when writing all the tutorials and articles, ensuring all the code is fully tested to avoid issues for my readers.  All this takes time and a great deal of work, so please support the site by using the Adfly links etc.  If you have found this useful or have any problems implementing, please feel free to leave a comment and I will do my best to help.

## Example 2

The code pasted below will perform a repeated read from GPIO P1.0 and then using the DTC, it will copy the value into an array.  The DTC automatically increments the address of the array.  The integer array containing the value is then added together and divided by 10 to give an average value, which is assigned to the variable avg_adc.

The image below shows a screen capture from the debug mode in code composer studio. By using a breakpoint and watching the expressions adc[10] and avg_adc, the values can be viewed changing (almost live) as the input to the ADC changes.  A light dependent resistor circuit was used as input to the ADC in this case.

### Example 2 Code

The link below contains the zip file with the full example 2 C code, there is a small advert page first via Adfly, which can be skipped and just takes a few seconds, but helps me to pay towards the hosting of the website.

Example 2 Code

I take great care when writing all the tutorials and articles, ensuring all the code is fully tested to avoid issues for my readers.  All this takes time and a great deal of work, so please support the site by using the Adfly links etc.  If you have found this useful or have any problems implementing, please feel free to leave a comment and I will do my best to help.

## Example 3

The 3rd example reads 3 separate GPIO pins repeatedly, these pins are p1.1, p1.2 and p1.3. The program was originally written to be used with an analog 3 axis accelerometer, so some of the variables are still named in this manner.  Most of the comments within the code have been removed to reduce the size of the snippet, however the fully commented code can be downloaded below.

### Example 3 Code

The link below contains the zip file with the full example 3 C code, there is a small advert page first via Adfly, which can be skipped and just takes a few seconds, but helps me to pay towards the hosting of the website.

Example 3 Code

I take great care when writing all the tutorials and articles, ensuring all the code is fully tested to avoid issues for my readers.  All this takes time and a great deal of work, so please support the site by using the Adfly links etc.  If you have found this useful or have any problems implementing, please feel free to leave a comment and I will do my best to help.

## 58 thoughts on “MSP430 ADC Tutorial”

1. Kassy says:

Hello! I want I need to use an accelerometer so the 3rd code is very useful. But how do I get each axis to control a servo motor? I am stuck and I was hoping you can give me an example code with servo motors and ISRs.

1. Hi Kassy,

I don’t have any examples for controlling servos. However, I would simply first create a function which checks the value of say the X axis and if this value is over a certain threshold it makes the LED turn on or off. Once you have this working it would be easy to replicate for each axis, then expand on this maybe use the Ellipsis Operator, a good example of how this could be used is here

The best way moving forward would be to take the axis (X,Y or Z) raw ADV value, then depending on the servos movement arc, carry out some basic maths to transform the axis value into a positional movement for the servo.

Cheers,
Ant

2. Hi,

Best thing to do is join Texas Instruments online and you have access to all the documentation you need, also the E-E forum is very useful.

3. akhtar shaikh says:

what is the meaning of this line at modulation UCA0MCTL = UCBRS_1

1. akhtar shaikh says:

hello Ant
i want to just read multi channel one is temp and second one is voltage both are display in lcd ,but it will not working ,what is wrong in my code,
main()
{
}
{
// WDTCTL = WDTPW + WDTHOLD; // Stop WDT
ADC10CTL1 = INCH_10 + CONSEQ_1; // A2/A1/A0, single sequence
ADC10DTC1 = 0x02; // 2 conversions
ADC10AE0 |= 0x03; // Disable digital I/O on P1.0 to P1.
}

{

t = adc[0]; // adc array 0 copied to the variable X_Axis
// Y_Axis = adc[1]; // adc array 1 copied to the variable Y_Axis

t = ((((unsigned int)t-673)*423)/1024);//for tempreture
send_string(“temp=”);
temp2=t;
sprintf(c,”%d”,temp2);
send_string(c);
send_string(“vol=”);
temp1=temp;
sprintf(a,”%d”,temp1);
send_string(a);
send_data(‘.’);
temp3=(temp – temp1)*100;
sprintf(b,”%d”,temp3);
send_string(b);

//return (float)t1;
//return(int) ((t * 27069L – 18169625L) >> 16);
}
thnks and regards
akhtar

1. I don’t have CCS on my PC at the moment so cannot verify your code, but if you use my third example from here http://coder-tronics.com/msp430-adc-tutorial/

Then just verify you are reading 2 of your temperatures, you can then tweak for your needs from there. It also incorporates an interrupt so the CPU saves power whilst the ADC is converting. Once your verify it works, you can change line 20 ADC10CTL1 = INCH_2 + CONSEQ_1; to only read A0 and A1, the MSP430 family guide explains how to do that by modifying the ADC Control Register 1.

Hello Ant

I use msp430G2553

I use pins P1.0 , P1.1 and P1.2

P1.0 Analog control signal
P1.1 LM35dz First sensor
P1.2 LM35dz Second sensor

When i change pins for P1.4 P1.5 and P1.6 a result is excelent.

I read channels sequentially and I change options when I change the pin but on Pin 1.1 and 1.2 it`s not work and I don`t have why

Not sure if you have resolved this and only just had time to go through a backlog of comments and emails. Check how you have set the port pins up and ensure they are not conflicting.

5. hi ..
actually i am your way in explanation is really great ….
i want to know how to send data to lcd that use i2c with msp4302253 …
also if you have the code for measuring the temperature and humidity by using msp4302253
i really appreciate your time ..i know that you have to jobs (study and managing this website )

1. Hi Talal,

Thank you and no worries, glad it was useful

6. François HUANG says:

Hello Ant,
I generated a similar code than your example1 to capture voltage values and it works. As you showed in example1, the results are saved in ADC10MEM and now I want to save datas to a txt file. Is it possible? Im trapped by this problem for a week but i dont work out. Sorry for grammar error Im french student.

François

1. Hi Francois,

There is way to do this but I have not used CCS for awhile and connect remeber off hand, I also don’t have it installed at the moment. Ask on the Texas Instruments EE forums in the CCS section and you should get a response this.

Cheers,
Ant

7. I like the higlightcolor of your code and that lines are grey-black-grey-black. Do you get it so from your IDE or by HTML when you write your blogposts?
If from the IDE, which one do u use?

1. Hi,

This is by a PHP plugin for the website. However most IDE’s give the option to change the colour scheme, CCS I mainly use is based on Eclipse.

Cheers,
Ant

1. Thx Ant for the quick response.
I meant, the option of “alternating Line Color” (in your images code lines are light grey and dark grey alternating) no the complete scheme, but I found was I was looking for as a VB Plugin (AlternatingLineColor from VS Gallery) and also for Eclipse in the Wiki under “Alternate Color On Group Row”.

Thanks a lot 🙂

Cheers,
Rafa

8. Aleks says:

Hello, I can not understand why the ADC does not want to work. Did a single one-channel all the work, I decided to delve deeper and here.
The essence of the program is that would light up LED_green (P2OUT | = BIT3;) when it comes to the number of ADC, but it does not want to give numbers. After doboaleniya ADC10CTL0 | = ADC10SK even interrupt timer stops.
Here’s the code of the program

#include

void Init_IO (void); //Настройка портов, и частоты
void SetupTIMER0_A0 (void); //Настройка таймера прерывания
void ADC_init_1(void); //Настройка АЦП одноканальный режим

//volatile – те переменные, которые могут изменяться асинхронно

void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;

Init_IO (); //Настройка портов
SetupTIMER0_A0 (); //Настройка таймера А0

__enable_interrupt(); //Разрешаем прерывания глобально
for (;;)
{

}
}

void Init_IO (void) //Настройка портов вход – “0”, выход – “1”
{
P1DIR = 0b00000000; // настраиваем вывод P1.х на вход
P2OUT =0b000000; // Ставим сигнал на выходе 0 у порта P2
P2DIR =0b111000; // настраиваем вывод P2.0..P2.2 на вход / P2.3..P2.5 на выход (подключены светодиоды)
}

void SetupTIMER0_A0 (void)
{
TACCR0 = 6000; // регистр TACCRx содержит число, которое срав нивается с содержимым регистра счётчика TAR.
TACCTL0 = CCIE;

TACTL = TASSEL_2 + // TASSEL_2 – источник тактов SMCLK (SubMainCLocK), по умолчанию настроенных на работу от DCO
ID_3 + // ID_3 – делитель частоты 8
MC_1 + // Прямой счёт: таймер считает от 0000h до TACCR0.
TACLR; //обнуляем счетчик
}

void ADC_init_1(void) //Настройка АЦП циклический одноканальный режим
{
ADC10CTL0 = SREF_0 + // Используем Vcc/Vss(аналоговая земля) для верхнего_нижнего ИОН /
MSC+ //первому нарастающему фронту сигнала SHI, а остальные выборки/преобразования запускаются автоматически

ADC10CTL1 = INCH_0 + //Выбор входного канала А0
SHS_0 + //Источник сигнала для пуска бит ADC10SC
ADC10SSEL_0 + //Источник тактов АЦП ACLK
CONSEQ_3; //Циклический одноканальный

ADC10AE0 = BIT0; //Аналоговый вход включён

ADC10SA = (unsigned int)ADC_int; //Начальный адрес блока. Эти биты определяют начальный адрес для контроллера DTC

}

#pragma vector = TIMER0_A0_VECTOR
__interrupt void CCR0_ISR (void)
{
P2OUT ^= BIT5; //Желтый светодиод

if (ADC_int == 0) //3 //Красный светодиод
P2OUT |= BIT4;
else
P2OUT &= ~BIT4;

if (ADC_int != 0) //Зеленый светодиод (не хочет загораться)
P2OUT |= BIT3;
else
P2OUT &= ~BIT3;

}

1. Hi,

At the top of your code is a #include but there is no header file for this to include. You don’t specify which microcontroller you are using, but you need to include the header files for that model i.e. #include msp430.h

There are also generic header files for each individual MSP430, look at an example program for your microcontroller and you can copy paste the #include ??????.h from there.

You also appear to have a continous for loop i.e. for(;;){} but if you are running the code inside interrupts this should not be necessary, just put the processor to sleep and the interrupts will wake it up run the code and then go back to sleep again.

This will hopefully solve the issue, but I am afraid I don’t have time to look through your code and find errors. Best thing to do it get small parts working then bring it altogether.

1) Get the timer and interrupt working separately and flip a GPIO HIGH and LOW so you can view the output on an oscilloscope every time it generates and interrupt
2) Get the ADC working on it’s own and have a simple delay between reads
3) Split the ADC code into a setup and the part that starts and stops the read
4) Bring the timer code and ADC together, have the ADC setup code initialise the ADC at startup, and the ADC start and stop code in the timer interrupt.

Something like the steps above should work, but breaking the code up into small tasks is a far better way I have found, especially with microcontrollers you are not necessarily familiar with.

Cheers,
Ant

1. chaitanya says:

can I get c code for configuring msp430f6632 to mcp3202(external adc),in spi mode

1. Hi,

I have never worked with the MCP3202 so have no SPI example written, maybe someone on the 43oh forum has something or you can adapt an SPI example to meet your needs.

Cheers,
Ant

9. Hey Ant,
Nice info regarding the ADCs. I have a doubt. I use MSP430FR5969.
I want to input a sine wave (1Hz, 1Vpk-pk, 1V DC offset) and also check the digital values in the concerned register. I’m a beginner and I’m finding it hard to code. Please help.

Thanks,
Yashaswy

1. Hi Yashaswy,

I cannot help you with the full code as simply don’t have time, I would start with some of the basic code examples supplied with Code Composer Studio or the IAR IDE.

If you measuring a sinewave of 1Hz you can easily over sample this to capture enough information. Capturing this information from the register is going to be your main issue, as presume you want to view the values and reproduce a graph?

This post will probably prove useful MSP430 Oscilloscope

Cheers,
Ant

10. Ashwin says:

Thanks for all the tutorials over here.

I have a few doubts in the last example (3)

While setting up ADC only INCH 2 is mentioned, so what about P1.3 and P1.1?
So should the initialisation look something like INCH 1 + INCH 2 + INCH 3??

ADC10AE0 should also be 0x0e instead of 0x03 right??(Since P1.1 P1.2 and P1.3 are being used)

1. Hi Ashwin,

Sorry been on holiday so not had a chance to catch-up with comments and mail until now.

The INCH_n value determines the ADC port to be sampled, but which port/s are sampled is determined by the CONSEQ_n. See the below defines copied and pasted from the msp430 header file.

#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 */

If you used the parameter ADC10CTL1 = INCH_2 + CONSEQ_1; for example, then this would allow a sequence of channel reads starting with A0, A1 upto A2.

For enabling P1.1, P1.2 and P1.3 it’s best to understand the defines used for these ports, then it’s just simply adding hexadecimal values. Some more code pasted directly from the code composer msp430 header file.

#define BIT0 (0x0001)
#define BIT1 (0x0002)
#define BIT2 (0x0004)
#define BIT3 (0x0008)
#define BIT4 (0x0010)
#define BIT5 (0x0020)
#define BIT6 (0x0040)
#define BIT7 (0x0080)

So 0x0002 + 0x0004 + 0x0008 = 0x000E

Cheers,
Ant

11. Karn says:

Hey guys, need some help with coding. I am designing a variable voltage controlled PWM using the MSP430F5529. The 10k ohm potentiometer is connected to Port 6 Pin 0 which is getting its Vcc from the board 3.3 V. When the potentiometer wiper voltage is 0 V the duty cycle should be 0%, when the wiper voltage is 2.5 V the duty cycle should be 100% (Another resistor is connected in series with the potentiometer to give the wiper voltage a 0-2.5 V range). Here is the code i have so far.

#include

int main(void)
{
volatile unsigned int i;
WDTCTL = WDTPW+WDTHOLD; // Stop watchdog timer
P6SEL |= 0x01; // Enable A/D channel A0

// By default, REFMSTR=1 => REFCTL is used to configure the internal reference
while(REFCTL0 & REFGENBUSY); // If ref generator busy, WAIT
REFCTL0 |= REFVSEL_2+REFON; // Select internal ref = 2.5V
// Internal Reference ON
// Turn on ADC12, Sampling time

for ( i=0; i<0x30; i++); // Delay for reference start-up

while (1)
{
__no_operation(); // SET BREAKPOINT HERE

}
}

12. uv says:

Hey thanks for sharing this tutorial.
2)In ADC10SHT_2 what the number “2” stands for? and how to calculate sample and hold time from this?
and what are the control register in ADC12?

1. Hi,

1) ADC10SHT_2 sets the ADC10 sample and hold time, is this case the last digit 2 indicates 16 ADC10 clock cycles. See page of the family guide PDF

2) The control registers for the ADC12 can be accessed like this ADC12CTL1 = ????????

Cheers,
Ant

13. Hello
I am doing a project on MSP430F5510, on Dual Axis Solar tracker using 4 LDRs
I have written the following code, but it is showing an error stating: Expected a declaration
& This kind of pragma may not be used here.

#include

#define SENS_LEFT BIT0
#define SENS_RIGHT BIT1
#define SENS_UP BIT2
#define SENS_DOWN BIT3

#define MCU_CLOCK 1000000
#define PWM_FREQUENCY 46 //ideally 50Hz.
#define SERVO_STEPS 30 // max amount of steps in degrees (180 is common)

unsigned int PWM_Period = (MCU_CLOCK / PWM_FREQUENCY); // PWM Period
unsigned int PWM_Duty = 0; // %
int i;
int count;
int count2;

{

ADC10AE0 = SENS_LEFT + SENS_RIGHT + SENS_UP + SENS_DOWN ;

}

void main(void) {

WDTCTL = WDTPW | WDTHOLD;

TA1CCTL1 = OUTMOD_7 ;
TA0CCTL1 = OUTMOD_7 ;

TA1CTL = TASSEL_2 + MC_1 ;
TA0CTL = TASSEL_2 + MC_1 ;

TA1CCR0 = PWM_Period-1; // PWM Period
TA0CCR0 = PWM_Period-1 ;

TA1CCR1 = 0 ; // TACCR1 PWM Duty Cycle
TA0CCR1 = 0 ;

P1DIR = 0; /* set as inputs */
P1SEL = 0; /* set as digital I/Os */
P1OUT = 0; /* set resistors as pull-downs */

P1REN |= (SENS_LEFT|SENS_RIGHT|SENS_DOWN|SENS_UP); /* enable pull-up on SENSOR */

P1DIR |= BIT6 ; // P1.2 = output
P2DIR |= BIT2 ;

P1SEL |= BIT6 ; // P1.2 = TA1 output
P2SEL |= BIT2 ;

__enable_interrupt();
while (1) {

__delay_cycles(1000);
__bis_SR_register(CPUOFF + GIE);

for (i = 0; i < SERVO_STEPS; i++) {
TA1CCR1 = count2 ;
TA0CCR1 = count ;
__delay_cycles(20000);
TA1CCR1 = count2 ;
TA0CCR1 = count ;
__delay_cycles(20000);
}
}

{

if (samples[0] 550)
{
count = 850;
}
else {
count = 1350;
}
else if ((samples[0] + samples[1])/2 550)
{
count = 1850;
}
else
{
count = 1350;
}}
if (samples[2] 550)
{
count2 =850;
}else
{
count2 = 1150;
}
}
else if ((samples[2] + samples[3])/2 550)
{
count2 = 1850;
}else
{
count2 = 1150;
}
}
__bic_SR_register_on_exit(CPUOFF);
}
}

1. Hi,

I glanced briefly over your code and can see you enable the ADC10 interrupt and the initial code for the interrupt handler appears ok. However at the moment I really don’t have time to review the full code, as 2 jobs, studying and running this site doesn’t leave me a lot of time, additionally I don’t have a MSP430F5510 to hand to run any tests.

I would do the following, reduce the code down to a working example that reads the 4 ADC ports and calls an interrupt. Then after you have this working add the additional features you require. It is worth checking that when you call the interrupt, ensure you execute all the code inside the interrupt before the interrupt is called again.

Another root is to upload this code to the Texas Instruments MSP430 forums, as they will offer advice and help.

Best regards,
Ant

14. imat says:

Hi Eveybody !
First thank you Ant for this site.
I have a lm35 temperature sensor and nokia5110 lcd. I have a project. I am new in embedded systems.
I wrote the program. I used ADC with MSP430G2553 but, i dont know how to write the temperature value to LCD. I write all characters from the keyboard but i dont know how to write the temperature value to the LCD.
Is anyone can help me ?
Thank you very much.
Imat

1. Hi Imat,

I have never used the nokia5110 LCD, I am pretty sure there are some tutorials for communicating with that LCD on the 430h.com forums. I know that the ADC values will be an integer and to send to the LCD you will need to convert these to a Char, I used an itoa function for this and more can be found in a 2 part post I made here

Regards,
Ant

15. Lisa B. says:

Thank you so much for this! It has been super useful in helping me understand what each line of code does. Would you mind checking out a code I wrote that models your multiple input adc code for the accelerometer? I don’t know much about clocks or about coding in general. I don’t have a way to debug this at home and won’t get to try the code out before it’s due next week so I would really appreciate any advice you could give me.

#include

unsigned int ADC[2] = {0}; // INITIALIES ARRAY TO HOLD ADC VALUES
unsigned int HP = 0; // VALUE OF THE HIGH PASS FILTER
unsigned int BP = 0; // VALUE OF THE BAND PASS FILTER

int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // STOP WDT
ADC10CTL1 = INCH_1 + CONSEQ1; // SET CHANNEL TO READ A1 AND A0 SEQUENTIALLY
// SET VREFS, SET SAMPLE HOLD TIME FOR 64 CLOCK TICKS, SET MULTIPLE SAMPLE CONVERSION, TURN ON ADC, ENABLE INTERUPT
ADC10DTC1 = 0X02; // SET 2 CONVERSTIONS
ADC10AE0 |= 0X02; // DISABLE DIGITAL I/O FROM P1.0 TO P1.1
BCSCTL1 = CALBC1_1MHZ; // SET RANGE(UC TO RUN AT 1MHZ)
DCOCTL = CALDCO_1MHZ; // SET RANGE(UC TO RUN AT 1MHZ)
P1DIR |= BIT5 + BIT6; // SET P1.5 AND P1.6 AS OUTPUTS
while (1)
{
HP = ADC[0]; // VALUE IN ADC[0] WILL BE STORED IN VARIABLE HP
BP = ADC[1]; // VALUE IN ADC[1] WILL BE STORED IN VARIABLE BP
if (HP >= 560)
{
P1OUT |= BIT5; // TURN ON BIT 5
}
elseif (BP >= 560)
{
P1OUT |= BIT6; // TURN ON BIT 6
}
else
{
P1OUT &= ~(BIT5 | BIT6); // TURN OFF BIT 5 AND 6
}
}
}

1. Hi Lisa,

I cannot look through all your code for testing, but can make a few observations working from the top of the code down.

1) You have an include with nothing after it, probably need to have something like this `#include msp430.h` Need to have a less than arrow before the msp430.h and a greater than arrow after the msp430.h, the comment would not allow me to type that.
2) You set-up the watchdog timer, then the ADC and then main clock. I would set-up the main clocks after the watchdog timer, and then the ADC. Just keeps the code in a better order and you could potentially run into issues, with other code examples.
3) Your last condition statements in the while loop, I would probably write like this as not sure if yours will compile and not had a chance to check.

```if (HP >= 560) {P1OUT |= BIT5;} // TURN ON BIT 5 if (BP >= 560) {P1OUT |= BIT6;} // TURN ON BIT 6 else {P1OUT &= ~(BIT5 | BIT6);} // TURN OFF BIT 5 AND 6 } // for the while loop } // for main```

Hope this helps,
Ant

16. John Q says:

Hey man,
I’m still a little confused on how to read two different signals. If I have the following code:

P6SEL |= 0x01; // Enable A/D channel A0
// set multiple sample conversion
unsigned int pot=0;
while(1){ // Infinite loop
while (!(ADC12IFG & BIT0)); //wait until conversion is completed
printf(“Pot=%d\n”, pot);

This code is giving me the digital value coming from the voltage to pin P6.0,
How exactly would I go about reading a second signal to say pin P6.1?
Should I just rewrite all of this code but make it pertain to the 10bit ADC?
This is another option I am thinking of doing but I do not have the instrumentation on me to see if it works:

P6SEL |= 0x01;

—> //P6SEL |= 0x02;

unsigned int pot=0;
while(1){ // Infinite loop
while (!(ADC12IFG & BIT0)); //wait until conversion is completed

printf(“Pot=%d\n”, pot);

This code is given to me in class, I am trying to figure out where the INCH register is being set to dedicate a channel. Is it the statement where he says ADC12IE. Basically, what I’m wondering I guess, is there a way to read two signals into channel 0 and 1 of the ADC at the same time, or would I have to reset all the registers to read the second signal?

Any help is much appreciated.

Thanks!

1. Hi John,

I have not used the ADC12 but the code shouldn’t differ greatly from the ADC10 apart from some extra functionality as described in the family guide. I don’t know which MSP430 device you are using, but if you study the datasheet for that device it will help to understand which ports are used for the ADC.

From your first block of code I cannot see the control register command ADC12MCTLx, bits 3-0 of that register appear to control the input channel select (see page 578 of the family guide my version slau144j.pdf). The command would look something like this ADC12MCTL0 |= INCH_1; // this should then start at analog port A1, thus reading from A1 to A0.

So looking at your second example you appear to have the same issue, the ADC12IE is the interrupt enable register. If your unsure on some of these the family guide is a must, all these register names are listed with brief descriptions, admittedly some are less clear than others what their function is.

Cheers,
Ant

17. Lucas says:

Hey Ant. I am trying to apply this code to read a guitar signal as well as values from 2 different 10k potentiometers. The guitar signal is a sine wave and therefore has negative values. Does the MSP430 have the ability to read negative voltage? Secondly, the max frequency of the guitar signal will be about 1.3kHz. I want to optimize the 3rd code for effectively handling a signal with this frequency. Will using the 16MHz clock help? I know that there are better microcontrollers for this application, but the MSP430G2553 is what I’ve got. The result of the ADC conversion will be truncated to 8 bits and sent to port 2 for output to a DAC. Any further wisdom or insight on this sort of signal processing will be greatly appreciated.

1. Hi Lucas,
The MSP430 and probably the vast majority of microcontrollers will not allow negative voltages (I say vast as there may be some specialist product developed for a particular task). The MSP430G series ADC accepts 0 to 3V, so without knowing the full details of your input voltages etc we can use a basic example.
Let’s say your input sine wave has a peak to peak voltage of 2V, so its positive peak voltage will be 1V and negative peak voltage will be -1V. You will then need to use a signal conditioning circuit (I would probably use an opamp with a negative and positive supply) to condition this signal so that the zero crossing point of the sine wave is located at 1.5V. The 1.5V is then directly in the centre of the ADC sample voltage on the MSP430, if your peak to peak voltage is larger or smaller than your signal conditioning circuit, you will also need to attenuate or amplify the incoming signal, thus ensuring the best use of the input ADC range on the MSP430. Look-up using opamps with offset on Google as this will be a good starting point.
Your sample frequency needs to be at least double your input frequency (Nyquist Theorem), but your best sample rate depends on the application and how much resolution you need. Let’s say you sample at 2.6kHz, that is a sample every 385uS, if the clock was set to run at 16MHz that would give you plenty of time for your calculations as 1 clock pulse at that speed is 62.5nS. You can increase the sample rate further, just ensure any calculations you need to do fall into the time you have between samples, it may even be possible to reduce the clock frequency to 8MHz.
You can store the sampled ADC values in a file using Code Composer Studio, then you can copy these to Excel for example to visualise them to see how accurate your sampled data is compared to the original sine wave.
I would focus on the ADC sampling side first so you can obtain accurate data, then focus on the output side of the task.
Hope this helps,
Ant

1. Lucas says:

Thanks Ant. I will make a positive clamper with an op amp to adjust the signal before feeding it to the MSP430. One last question. The output of the MSP430 will be unipolar just like the input. Do you know if audio amplifiers, such as a guitar amp, will accept unipolar as opposed to the expected bipolar signal? If not I know the circuitry to convert the signal back to bipolar it’s just more parts and hassle. Thanks again for your help.

1. No worries Lucas,

I am afraid I don’t know much about guitar amplifiers so cannot help with this question, I would turn to Google and see what forums or sources turn up.

Good luck with the project.
Regards,
Ant

1. Lucas says:

Hey Ant,

I’ve made major progress with using your 3rd code for my guitar effects processor project. I have played with the code and MSP430 a bit and found that the values of the adc array max out, max of 1024, at about 3.3 volts. I’ve read in the code comments that the ADC operation is set up for a reference voltage of 1.5 volts. Shouldn’t the values be maxing out at 1.5 volts?

1. Hey Lucas,

Glad to hear your project is progressing well. On that 3rd example there is no reference voltage being used, not sure what the comments are referring to as was a while ago when I did that project, but might be referencing my circuit as I used an offset voltage in the signal conditioning stage. This is why you are seeing the readings max out at 1023.

If you want to set the reference voltage up, you need to set various parameters in the ADC10CTL0 register.

Cheers,
Ant

18. Jon C says:

These tutorials have been very helpful so far! 😀

I’m still quite new to microcontrollers, but I was wondering how you would use this adc concept to get analog values (i.e. from voltages modulated by a potentiometer) and then use these values to control the duty cycle of a PWM signal?

Any detailed outline or a conceptual help would be helpful! I’ve searched other sites, but you have given me the best help so far and wondering if you had any thoughts.

Thank you!

1. Hi Jon,

We all have to start somewhere and glad to hear the site has been useful.

Well first of all divide the problem up into smaller chucks and bring it together, so get the ADC code working and a working example of the PWM as well both as separate examples. This way you know both aspects work and it should be a simple to task to bring them together. Then the ADC value you are reading and storing as a variable, use this to update the timer counter value or TACCR value of the timer. There needs to be some structure to your program so eveything runs correctly but a brief outline of what you need to do is below.

From the tutorials here for PWM http://coder-tronics.com/msp430-timer-pwm-tutorial/ use the second example, and you want to adjust the line 13, which reads TA0CCR1 |= 100;. In this case the 100 is equal to 50% duty cycle and a raw value from the ADC10, will vary from 0-1023, so a little maths is required depending on your particular example. In this case I am just going to say divide the raw ADC value by 10, this will give you a value from 0-100 (therefore the duty cycle will vary from 0-50%). You then need to have something like TA0CCR1 = Calculated_ADC_Value;.

Hope this gives you a rough idea how to proceed.

Cheer,
Ant

19. Alfonso says:

Unfornately example 3 doesn’t work for MSP430FR5739 🙁

1. Hi,

These examples are written for MSP430G devices, but with some basic tweaks should run on the other MSP430 family of devices. I don’t have a launchpad with any of the newer MSP430 series, so am unable to test or check the code. Presume you have changed the obvious like the included header file for the series and the checked the pin configuration.

Regards,
Ant

20. aravindh says:

Hi,

as a beginner i have some basic doubts in the logic and some manner in which registers are initialized,.. and ur tutorials are great, thanks for explaining in detailed about how to get on with the Basic ADC registers. It would be nice and greatly helpful if you explained the code too(couldnt figure some lines and portion of the code. Not always, only some portion)
Here goes my actual questions,…?

1. in the adc initialization part you have mentioned channels and pins(port.pin),
are “channels” and “pins” the same ?

2. In code1’s main section,….
“P1SEL |= BIT3; // ADC input pin P1.3”
as the comment says it is to set the pin to accept analog signals as input to the controller
and in the same code, in the “ConfigureAdc” function
INCH_3 is specified, as channel 3,…

where as in the renaming code 2 & 3 there is no any specification about assigning input pin for adc input. ie there is no line as P1SEL statement.?

3. im using msp430g2231 ic. If im right, only changing the header should work. coze you haven’t used port 2 in the first code. and if thats the case will connecting an LDR btw vcc and pin3 is the right way for the hardware part ?

4. now i have a logical doubt.
You have mentioned as “Single read from P1.3” in code 1.
but the sample and hold statement is place inside the “While(1)” loop.
which means it should keep reading the pin again and again right.?

if possible,… can you explain/example in real time embedded terms?

Thanks a lot for your kind and great help.

1. Hi,

1) Not sure where I mentioned channels, but assume you mean PORTS and PORT PINS. For example P1IN and P2IN are selecting the register for PORT 1 input and Port 2 input. The individual pins are then simply BIT0, BIT1 etc

2) This isn’t always necessary for each port and depends on it’s default mode after a reset, the best way to check this is to look at the datasheet for the particular MSP430G device as it lists the default port configuration after reset. I did mean to test this code again and omit P1SEL |= BIT3; // ADC input pin P1.3 to see the affect, just not had the time.

3) I would use your LDR in a potential divider circuit, but even then you will only register a small voltage change i.e. lets say we have a 3V supply, your (R1) LDR is a 10K type and then you have another (R2) 10K resistor is series. So you would expect 1.5V at the junction of the LDR and the resistor when the LDR is at 10K, if the LDR changes to 1K then you register 2.7V. This will give 1.2V’s of change, which is enough for a basic test, using an opamp with offset would be a better option to extend the range to the full ADC input. Also bear in mind LDR’s are not linear devices!

4) Yes it is a single read and then it updates multiple times, you can put it inside a timer for more accurate reading times. It’s just an example, but does do a single read never the less.

5) Actually not fully sure, however I believe it is just a standard C code practice, certain standards and best practice methods to use and follow.

1. aravindh says:

21. Ronak says:

Hi, I’m new to the embedded programming. I have few basic question.
1. There are different modules in MSP430 like ADC, Timer, SPI/I2C etc. There is procedure also to configure, setting the register bits/values, setting the port pin direction etc. Is there any particular order in which we should do this procedure?

When you write this code, we dont actually write the value. for ex ADC10SHT_2 (10 bit) means 16 × ADC10CLKs. So when we write ADC10SHT_2 does it automatically sets the 10 bits?

3. I noticed there are few expressions like |= and +. How many others are there and what do they mean? is there a list of such expressions?

4. I also noticed that there are 16 bits and 8 bits registers. sometime we dont set all the bits but we use only few of them? Dont we need to set others to its default value?

-Ronak

1. Hi Ronak,

1) Not in all cases but generally you set the peripheral up before enabling it, I have encountered this issue more on the TI Stellaris and C2000 microcontrollers. This is not so much the case with the MSP430 ADC examples show, as the ADC10ON command is with other registry commands and there are also commands after it.

2) The ADC10SHTx are Bits 11-12 of the the ADC10 control register 0 (ADC10CTL0), so your actually just setting those bits with 4 combinations 00,01,10 and 11. ADC10SHT_2 sets those bits to 10.

3) These are C language operators, pretty essential to know what these means when dealing with microcontrollers. I do intend to write a tutorial on these at some point, but not had the time. The ones you have listed specifically are Bitwise operators | is a Bitwise OR when combined with the = or assignment operator it becomes |= Bitwise OR assignment, the + is simply an addition. If you are familiar with logic gates the logical operators are the same, there are AND, OR, XOR and NOT. Do a search online for C operators and you should find a list.

4) There will be default values set as each bit of a register can only be a zero or a 1, I cannot remember what the default settings will be, but if the particular setting in a register is not used in your project and has no performance implication on your project, then no need to change it.

I would have a read of the MSP430 family guide, yes it’s well over 600 pages but just use the search function to get the relevant bits of information you need. There is a link in the tutorial to the guide. I will get around to writing tutorials about the other peripherals for the MSP430 sometime soon as well.

Regards,
Ant

1. Ronak says:

Hi,

ADC10CTL0 is 16 bit register. on the right hand side of equation there is addition of 1 or 2 bits. So How does it make up the 16 bit value?

1. Hi,

Ok I can see where the confusion lies, have a look at the defines for registry commands like ADC10SHT_2. I will explain it so it should become clearer, but have a look at the MSP430G2231.h header file as these are all listed here.

ADC10SHT_0 is defined as having a hexadecimal value of (0*0x800u) in binary this is equal to 0000 0000 0000 0000

ADC10SHT_1 is defined as having a hexadecimal value of (1*0x800u) in binary this is equal to 0000 1000 0000 0000

ADC10SHT_2 is defined as having a hexadecimal value of (2*0x800u) in binary this is equal to 0001 0000 0000 0000

ADC10SHT_3 is defined as having a hexadecimal value of (3*0x800u) in binary this is equal to 0001 1000 0000 0000

Now note that the only bits that are changing value are the 11th and 12th bits, as per my previous reply. For other registry bit commands for that particular register, the same principle is in operation, so adding them simple sets certain bits in the register and adds the values together.

Simple really!

Ant

22. David says:

I just posted part of my code because it is very long.
But I have a question about the configuration of the ADC and is as follows.
but not if I have to configure this pin as analog input l like you doing in the first example in which you say P1SEL | = BIT3;
The program is by running the steps but I want to know if you really need to set this as this is my problem.
thanks

1. Hi David,

This is setting parameters for the ADC10 Control Register 0, you firstly turn the ADC10 on, then the second parameter controls the sample and hold time, setting this to 16 clock cycles.

The second code you pasted looks invalid to me, I have to admit I have not used the MSP430 for over a year as focused on the Stellaris and C2000 at the moment. But looking at the C language operators used and also the format, usually the register you are addressing is followed by an ‘=’ sign and then you set each individual bit in the register with commands like ADC10ON, then if you want to set a further parameter you have a ‘+’ sign between each parameter.

Now you mention about configuring the pin as an analog input, this is not used in all examples. I am pretty sure if you run my first example and remove the line P1SEL |= BIT3; it will still work.

The ADC10 Control Register 1 is used to configure which channels will be used by the ADC10, the channels are connected directly to the external pins of the device. The parameter for this is the INCHx, from the first example code the command looks like this ADC10CTL1 = INCH_3 + ADC10DIV_3 ; (line 13)
This selects channel 3 as an ADC input with the command INCH_3 and then ADC10DIV_3 is the ADC clock divider with 3 being the division value.

If you have the MSP430 family guide, I am using slau144j.pdf and the control register 1 parameters can be found on page 555.

Ant

23. David says:

Hi I am making a program that I need to take 20 samples per channel 1 and 10 samples per channel 2 I have the following program

_BIS_SR(GIE);

while(muestra< 20)
{ //Deshabilito el BIT0 para el ADC
ADC10CTL1 = INCH_1; //Habilito el canal 1
while((ADC10CTL1 & ADC10BUSY)==0x01);//Espero a que termine la conversion //Espero a que se termine la conversion
sample = sample + ADC10MEM; //Tomo muestra de voltaje
muestra++;
}
if(muestra == 20)
{
sample = sample/1023; //Tomo 20 valores de conversion
TA0CCR1 =PWM_VARIABLE[sample]; //Cargo el valor del timer1 la posicion del valor leido en la conversion
muestra++;
}
if(muestra ==21)
{ //Deshabilito la conversion por el canal 1
ADC10CTL1 = INCH_0; //Habilito la conversion por el canal0
There is something that is not doing well but not if it is in your support lña agradesco settings

1. Hi David,

From the code I see the variable muestra is used as a counter to ensure 20 samples are taken, controlled by the while loop. You are reading channel 1 only (this is channel 1 not 2), and then each time the loop runs it adds the value read on the ADC to the total value assigned to the variable sample.

Then the following if statement will be true as muestra will be 20. This divides sample by 1023. Which I am a little unsure why, as dividing by 20 would give you the average of your 20 samples, but 1023 is the step resolution of the 10bit ADC? Then this value is used to update a timer used for PWM, muestra is incremented so will now be equal to 21.

The final code block which starts with an if statement should again be true, as muestra is equal to 21. Your now reading channel 0 only (this is channel 0 not 1), but you only appear to read it once, and assign the value to the variable sample1.

Ok, now I am not entirely sure what your end goal is, but sounds like you want to over sample the ADC channels to get some hardware or software averaging?

Do you have any critical timing considerations?

Possible options:

(1) You could do repeated single reads for channel 0 and use a for loop or while loop and repeat this again for channel 1. Simply alter the loop count parameter from 10 to 20.

(2) Or you could read channel 0 and 1 simultaneously, saving the data to an array. This should be faster as you are running the ADC reads sequentially. You will have 20 samples of each this way, then can simply discard the last 10 samples of one of the read sequences, or only use every second sample of that sequence. My second example code goes some way to showing this for a single channel, but I had a quick search and someone has some code for 5 channels which could be modified for you needs (see the link below). This is actually very similar to my third example. I think you will need to sort the odd and even arrays out and add them together separately, otherwise you will be mixing samples.