This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hi,
I've recently started using MSP430 and have done some basic programming such as blinking the LED, and learned a little bit about ADC and UART by displaying the CPU temperature via UART over Realterm Terminal(An application that runs on windows based system) etc. So far so good, but none of these required interfacing of external circuit with the MSP430 Microcontroller. Now, I've been asked by my instructor (I'm a student) to interface some simple external circuits(probably just drawn on the breadboard) and interface those with MSP430. To be precise, these external circuits should be used to measure the voltage, current and resistance. So, to keep things simple, I'll have three different circuits such as
That seems fine to me, but I'm not able to visualize, how I'm going to interface these three different circuits with the ADC ports, (I've Launchpad MSP430G2553 to be precise and In past, when I had used the ADC10 module of this Microcontroller for sensing microcontroller's CPU temperature. I had basically used the in-built temperature sensor on the MSP430G2553 by selecting Input channel 0x0A and little bit of coding) and how those wires from external circuit should be connected to this MSP430G2553 Microcontroller.
Thank you for your help.
Kumar Mehta said:
- Voltage divider for measuring voltage
- A shunt resistor for measuring current
You shall take in account that ADC10 of g2553 measures GND-referenced voltages. So your voltage divider and shunt shall be tied to GND (low side current shunt).
Kumar Mehta said:A wheatstone bridge circuit for measuring resistence
This one needs differential ADC like SD16 or SD24. Those are not present in g2553.
[edit] Well... there's solution to use galvanically isolated (from microcontroller) battery to power bridge - then you can use ADC10 of g2553.
Kumar Mehta said:I'm not able to visualize, how I'm going to interface these three different circuits with the ADC ports
About interfacing you shall start with application reports:
http://www.ti.com/general/docs/lit/getliterature.tsp?literatureNumber=slaa024&fileType=pdf
I finally figured out how to interface an external circuit with MSP43G2553. For starters, I've drawn a voltage divider circuit on the breadboard, connected the VCC pin on MSP430G2553 to VCC on breadboard, and similarly connected the GND pins. Then I connected Vout from the Voltage divider circuit to P2.3 pin of the MSP430G2553. Now comes the coding part. After referring the user guide and some online reference [1], the current code is as follows:
#include <msp430.h> void setup_uart(void); void caliberate_clock(void); void setup_adc(void); //Counter for 1 second delay unsigned int counter = 15; unsigned int timer = 0; unsigned int i; unsigned int var; int main(void) { // Stop WDT WDTCTL = WDTPW + WDTHOLD; caliberate_clock(); // Select lowest DCOx and MODx settings DCOCTL = 0; // Set DCO BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; // Configure Timer // CCR0 interrupt enabled CCTL0 = CCIE; CCR0 = 30000; // CCR0 = 65535; // SMCLK, continuous mode TACTL = TASSEL_2 + MC_1; setup_uart(); P2SEL |= BIT3; //ADC Input pin P2.3 setup_adc(); // Enter LPM0 w/ interrupt _BIS_SR(LPM0_bits + GIE); } void setup_uart(void) { // UART setup section // Refer data sheet, pg 43 P1SEL = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD P1SEL2 = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD UCA0CTL1 |= UCSSEL_2; // SMCLK // 1MHz 9600 (N=BRCLK/baudrate) UCA0BR0 = 0x6D; //refer user guide UCA0BR1 = 0; // 1MHz 9600 UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** } void caliberate_clock(void) { // If calibration constant erased if (CALBC1_1MHZ==0xFF) { while(1); // do not load, trap CPU } } void setup_adc(void) { // ADC10 control register 1 - Select the in-built temperature sensor // ADC10 control register 0 - Select sample and hold time, turn on ADC10 // Select reference (Vr+ and Vr- ADC10CTL1 = INCH_5 + ADC10DIV_3 ; ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON + ADC10IE; ADC10AE0 |= BIT3; } // Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A (void) { // Add Offset to CCR0 CCR0 += 30000; // Increase timer1 value timer++; if (timer >= counter) { // start the conversion using ADC10SC as source for SHI ADC10CTL0 |= ENC ; var = ADC10MEM; // Send data over UART UCA0TXBUF = var; for (i = 0; i < 50000; i++); timer = 0; } }
I'm using the Realterm (windows software) to show the readings on the terminal, but the readings are not correct(all i can see is zeros, please refer the screenshot).
Please take a look.
[1] http://www.embeddedrelated.com/showarticle/199.php
Thank you for your help.
Please ignore some of the comments in the code. Most of the code is taken from my previous exercise, where I had done the readings for CPU temperature of the MSP430 and showed those values over UART on realterm terminal.
Kumar Mehta said:Please ignore some of the comments in the code. Most of the code is taken from my previous exercise, where I had done the readings for CPU temperature of the MSP430 and showed those values over UART on realterm terminal.
When you are done writing correct comments, you are welcome back. Generic rule of getting attention in any forum post: show that you care in EVERY detail.
You configure the ADC for single channel, single conversion, but you never start a conversion.Kumar Mehta said:ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON + ADC10IE;
Since you never start a conversion, ADC10MEM contains the (undefined) power-up value all the time.
Also, you enable the ADC10 interrupt, but you don't have an ISR for handling it. So if you would start a conversion, the CPU would jump into the void and crash when the conversion is done.
When you just want to read the latest conversion result when the timer interrupt comes, then you should configure the ADC for repeated single channel conversion, then start converting. And do not set ADC10IE, as you don't need an interrupt when a conversion result is ready.
Thank you for the reply and guidance. I rechecked the MSP430 user guide and came up with a slightly different version of the code, however, I don't see anything being shown on the realterm. The modified code is shown below:
//****************************************************************************** // Filename: lab-final.c // Read analog voltage and display that value over UART // //****************************************************************************** #include <msp430.h> void setup_uart(void); void caliberate_clock(void); void setup_adc(void); //Counter for 1 second delay unsigned int counter = 15; unsigned int timer = 0; unsigned int i; unsigned int var; int main(void) { // Stop WDT WDTCTL = WDTPW + WDTHOLD; caliberate_clock(); // Select lowest DCOx and MODx settings DCOCTL = 0; // Set DCO BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; // Configure Timer // CCR0 interrupt enabled CCTL0 = CCIE; CCR0 = 30000; // SMCLK, continuous mode TACTL = TASSEL_2 + MC_1; setup_uart(); //P2SEL |= BIT3; //ADC Input pin P2.3 setup_adc(); // Enter LPM0 w/ interrupt _BIS_SR(LPM0_bits + GIE); } void setup_uart(void) { // UART setup section // Refer data sheet, pg 43 P1SEL = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD P1SEL2 = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD UCA0CTL1 |= UCSSEL_2; // SMCLK // 1MHz 9600 (N=BRCLK/baudrate) UCA0BR0 = 0x6D; //refer user guide UCA0BR1 = 0; // 1MHz 9600 UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** } void caliberate_clock(void) { // If calibration constant erased if (CALBC1_1MHZ==0xFF) { while(1); // do not load, trap CPU } } void setup_adc(void) { // ADC10 control register 1 // ADC10 clock source select; ADC10SSELx // Input Channel // ADC10 clock divider ADC10CTL1 = INCH_5 + ADC10DIV_3 | ADC10SSEL0|ADC10SSEL1; // ADC10 control register 0 // Select Internal reference voltage(Vr+ = VCC and Vr- = VSS) using REFON // and SREF_0 // Sample and hold time set to 64 × ADC10CLKs using ADC10SHT_3 // Enable the ADC10 core using ADC10ON bit ADC10CTL0 = SREF_0 + ADC10SHT_3 + REFON + ADC10ON; //BIS.B #08h,&ADC10AE0 ; P2.3 ADC10 function and enable // Input channel 5 INCH_5 was selected above // These bits enable the corresponding pin for analog input. BIT0 // corresponds to A0, BIT1 corresponds to A1, etc. ADC10AE0 |= BIT5; } // Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A (void) { // Add Offset to CCR0 CCR0 += 30000; // Increase timer1 value timer++; if (timer >= counter) { // start the conversion using ADC10OSC as source of conversion clock ADC10CTL0 |= ENC + ADC10SC; var = ADC10MEM; // Send data over UART UCA0TXBUF = var; for (i = 0; i < 50000; i++); timer = 0; } }
I Just realized that I've not selected the port on which my external voltage divider circuit is connected to. My external voltage divider circuit's Vout is connected to P2.3 on the MSP430G2553 board. Hence I made the following changes in the code:
P2SEL |= BIT3; ADC10CTL1 = INCH_3 + ADC10DIV_3 | ADC10SSEL0|ADC10SSEL1; ADC10AE0 |= BIT3;
However, still no luck :(
Kumar Mehta said:My external voltage divider circuit's Vout is connected to P2.3 on the MSP430G2553 board. Hence I made the following changes in the code:
P2SEL |= BIT3; ADC10CTL1 = INCH_3 + ADC10DIV_3 | ADC10SSEL0|ADC10SSEL1; ADC10AE0 |= BIT3;However, still no luck :(
First, INCH_3 is P1.3, pin 5 of g2553.
Unfortunately you did not follow to what JMG said. You can/shall start ADC conversion in timer ISR, but don't read ADC10MEM here! For god's sake not immediately after starting conversion :)) You read ADC10MEM __only__ when conversion is finished. Best way of doing that - in ADC ISR. Because when ADC ISR is called you know - it's done, you have result.
And finally - you don't have to invent anything here. You can read source code examples code and see how it shall be done.
Ohh. Thank you for the reply. I've updated the code now and also connected the Vout from my external voltage divider circuit to P1.5 on the MSP430G2553 board. For the purpose of avoiding any confusion, the complete code is show below. Still No luck but as suggested I've found some sample codes here [1] and will refer them for proceeding further.
//****************************************************************************** // Filename: lab-final.c // Read analog voltage and display that value over UART // //****************************************************************************** #include <msp430.h> void setup_uart(void); void caliberate_clock(void); void setup_adc(void); //Counter for 1 second delay unsigned int counter = 15; unsigned int timer = 0; unsigned int i; unsigned int var; int main(void) { // Stop WDT WDTCTL = WDTPW + WDTHOLD; caliberate_clock(); // Select lowest DCOx and MODx settings DCOCTL = 0; // Set DCO BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; // Configure Timer // CCR0 interrupt enabled CCTL0 = CCIE; CCR0 = 30000; // SMCLK, continuous mode TACTL = TASSEL_2 + MC_1; setup_uart(); //ADC Input pin P1.5 //Refer data sheet, page 6 setup_adc(); // Enter LPM0 w/ interrupt _BIS_SR(LPM0_bits + GIE); } void setup_uart(void) { // UART setup section // Refer data sheet, pg 43 P1SEL = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD P1SEL2 = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD UCA0CTL1 |= UCSSEL_2; // SMCLK // 1MHz 9600 (N=BRCLK/baudrate) UCA0BR0 = 0x6D; //refer user guide UCA0BR1 = 0; // 1MHz 9600 UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** } void caliberate_clock(void) { // If calibration constant erased if (CALBC1_1MHZ==0xFF) { while(1); // do not load, trap CPU } } void setup_adc(void) { P1SEL |= BIT5; // ADC10 control register 1 // ADC10 clock source select; ADC10SSELx // Input Channel (P1.5) // ADC10 clock divider ADC10CTL1 = INCH_5 + ADC10DIV_3 | ADC10SSEL0|ADC10SSEL1; // ADC10 control register 0 // Select Internal reference voltage(Vr+ = VCC and Vr- = VSS) using REFON // and SREF_0 // Sample and hold time set to 64 × ADC10CLKs using ADC10SHT_3 // Enable the ADC10 core using ADC10ON bit // Enable interrupt ADC10CTL0 = SREF_0 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE; // Input channel 5(INCH_5) was selected above // These bits enable the corresponding pin for analog input. BIT0 // corresponds to A0, BIT1 corresponds to A1, etc. ADC10AE0 |= BIT5; } // Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A (void) { // Add Offset to CCR0 CCR0 += 30000; // Increase timer1 value timer++; if (timer >= counter) { // start the conversion using ADC10OSC as source of conversion clock ADC10CTL0 |= ENC + ADC10SC; for (i = 0; i < 50000; i++); timer = 0; } } #pragma vector=ADC10_VECTOR __interrupt void ADC10_ISR (void) { // Send data over UART UCA0TXBUF = ADC10MEM; }
[1] http://www.ti.com/lsds/ti/microcontroller/16-bit_msp430/msp430_software_landing.page
Kumar Mehta said:UCA0TXBUF = ADC10MEM;
Are you sure UART is working? You shall develop one component at a time.
Kumar Mehta said:ADC10CTL0 |= ENC + ADC10SC; for (i = 0; i < 50000; i++);
Delay is not doing any good here.
Next time you look for source code examples in product page, "Software" download section.
http://www.ti.com/lit/zip/slac485
msp430g2x33_adc10_12 - timered ADC start, result read in ADC isr. Very close to what you are building. Compile example as is (well, ok, change just input), check how it works using debugger.
1) Rather than using unpredictable delay using "for loop" you can check the ADC10BUSY bit in ADC10CTL1 register. As per datasheet, this bit is set when a sampling and conversion is in progress, and reset when it is done.
2) You don't need to use ADC10 interrupt. Send the resulting conversion via UART after the conversion is done.
3) Keep in mind that ADC10MEM is 10-BIT long, and a single UART transmission only able to send maximum of 8-BIT data. So you should transmit ADC10MEM in two consecutive sets. The terminal will read it as two characters of course, but some little recalculation on PC should do the job before you present the actual data.
You can modify your timer interrupt to something like this :
// Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A (void) { // Add Offset to CCR0 CCR0 += 30000; // Increase timer1 value timer++; if (timer >= counter) { ADC10CTL0 |= ENC + ADC10SC; while ((ADC10CTL1 & ADC10BUSY) != 0); //Wait for conversion to finish UCA0TXBUF = ADC10MEM & 0xFF; //Send eight lower bits while ((UCA0STAT & UCBUSY) != 0); //Wait for transmit completion UCA0TXBUF = ADC10MEM >> 8; //Send the remaining two upper bits while ((UCA0STAT & UCBUSY) != 0); //Wait for transmit completion timer = 0; } }
[Edit]
Double posted... but anyway... Happy New Year! (for anyone who might notice it... :) )
Thank you everyone, Your suggestions helped and It worked. I didn't replied for sometime as I wanted to verify the readings taken by the ADC10 module of MSP430G2553, using a digital voltmeter.
**Attention** This is a public forum