Jump to content
Sign in to follow this  
rockets4kids

Problem combining ADC10 and USCI on G2553

Recommended Posts

I am encountering an odd problem using the ADC to sample a value and the USCI to send it out over the UART.  In the code below, defining SERIAL works as expected, and defining ADC works as expected, but when both are defined, the ADC does not seem to generate an interrupt when the conversion has completed.

 

The serial portion of the code does nothing more than send the string "Starting up"

 

The ADC portion of the code does nothing more than initiate a single sample, and then turn on P1.0 when the sample has completed.  This interrupt is generated and the LED lit when the UART is not enabled.  However, when the UART is enabled, the interrupt is not triggered and the LED is not lit.

 

As you can see in the commented-out line in serial_nextchar(), the end goal is to initiate a new sample when the serial buffer has emptied.

 

Any suggestions as to what is going on here?


#include <msp430.h>
#include "stdlib.h"

#define SERIAL
#define ADC

volatile char *serial_bp = 0;
char buf[20];

void serial_nextchar (void);
void serial_out (char *p);
void str_crlf (char *p);

//
// Main entry point
//

int main (void)
{
    // disable the watchdog timer (re-enable it later as interval timer)
    WDTCTL = WDTPW | WDTHOLD;

    // use internal VLO for ACLK
    BCSCTL3 = (BCSCTL3 & ~(BIT4+BIT5)) | LFXT1S_2;
    
    // Set the DCO to the 1 MHz calibration frequency
    BCSCTL1 = CALBC1_1MHZ;			// set range 
    DCOCTL  = CALDCO_1MHZ;			// set step + modulation

    // configure/flash status LEDs
    P1DIR |=  (BIT0 | BIT6);			// configure LED pins
    P1OUT &= ~(BIT0 | BIT6);			// LEDs off

    // Configure USCI_A0
#ifdef SERIAL
    UCA0CTL1  |= UCSWRST;			// insure USCI is disabled during config
    P1SEL      = (BIT1 | BIT2);			// select secondary peripheral function (USCI) for P1.1 (RXD) and P1.2 (TXD)
    P1SEL2     = (BIT1 | BIT2);			// (see table 16 on page 46 of msp430g2553.pdf)
    UCA0CTL1  |= UCSSEL_2;			// select clock source (source 2 == SMCLK)
    UCA0BR0    = 104;				// 1MHz/9600 = ~104.2
    UCA0BR1    = 0;				// MSB 0
    UCA0MCTL   = UCBRS0;			// Second stage modulation (fine tuning)
    UCA0CTL1  &= ~UCSWRST;			// Enable USCI
    IE2	      |= (UCA0RXIE | UCA0TXIE);		// Enable RX and TX interrupts

    serial_out ("Starting up\r\n");
    _BIS_SR (GIE);
    __delay_cycles (1000000);
#endif // SERIAL
    
    // Configure ADC
#ifdef ADC
    ADC10CTL0  = ADC10SHT_2 | ADC10ON | ADC10IE; // ADC10 on, interrupt enabled
    ADC10CTL1  = INCH_4;			// input A4
    ADC10AE0  |= BIT4;				// PA.0 ADC option select
    ADC10CTL0 |= (ENC | ADC10SC);		// Sampling and conversion start
#endif // ADC
    
    // note: should never come out of sleep
    for (; {
	_BIS_SR (LPM0_bits + GIE);		// Enter LPM w/interrupt
    }
    // NOTREACHED
}


//
// Interrupt: ADC10
//

#pragma vector=ADC10_VECTOR
__interrupt void adc10_isr (void)
{
    P1OUT |= BIT0;				//  LED on - sample finished
#ifdef SERIAL
    itoa (ADC10MEM, buf, 10);
    str_crlf (buf);
    serial_out (buf);
#endif // SERIAL
}

//
// Interrupt: RXBUF just got a character
//

#pragma vector=USCIAB0RX_VECTOR
__interrupt void usciab0rx_isr (void)
{
    // char in UCA0RXBUF
}

//
// Interrupt: TXBUF has just gone empty
// note: often (always?) generates an interrupt on startup!
//

#pragma vector=USCIAB0TX_VECTOR
__interrupt void usciab0tx_isr (void)
{
    serial_nextchar ();
}


//
// add cr+lf to string after null
// no safety checking!
//

void str_crlf (char *p)
{
    while (*p) {
	p++;
    }
    *p++ = '\r';
    *p++ = '\n';
    *p++ = 0;
}

//
//  serial_out
//

void serial_out (char *p)
{
    serial_bp = p;				// set global pointer
    serial_nextchar ();				// start things going!
}

void serial_nextchar (void)
{
    if (! serial_bp) {				// safety check!
	return;
    }
    
    if (! *serial_bp) {				// more data?
	serial_bp = 0;				// safety!
	//ADC10CTL0 |= (ENC | ADC10SC);		// Sampling and conversion start
	return;
    }
    
    if (IFG2 & UCA0TXIFG) {			// TX buffer ready?
	UCA0TXBUF = *serial_bp++;		// transmit the received character
    }
}

--end

 

 

Share this post


Link to post
Share on other sites

I don't think that is the problem.   Here is a further stripped example:
 

#include <msp430.h>
#include "stdlib.h"

#define SERIAL
#define ADC

int main (void)
{
    // disable the watchdog timer (re-enable it later as interval timer)
    WDTCTL = WDTPW | WDTHOLD;

    // use internal VLO for ACLK
    BCSCTL3 = (BCSCTL3 & ~(BIT4+BIT5)) | LFXT1S_2;
    
    // Set the DCO to the 1 MHz calibration frequency
    BCSCTL1 = CALBC1_1MHZ;			// set range 
    DCOCTL  = CALDCO_1MHZ;			// set step + modulation

    // configure/flash status LEDs
    P1DIR |=  (BIT0 | BIT6);			// configure LED pins
    P1OUT &= ~(BIT0 | BIT6);			// LEDs off

    // Configure USCI_A0
#ifdef SERIAL
    UCA0CTL1  |= UCSWRST;			// insure USCI is disabled during config
    P1SEL      = (BIT1 | BIT2);			// select secondary peripheral function (USCI) for P1.1 (RXD) and P1.2 (TXD)
    P1SEL2     = (BIT1 | BIT2);			// (see table 16 on page 46 of msp430g2553.pdf)
    UCA0CTL1  |= UCSSEL_2;			// select clock source (source 2 == SMCLK)
    UCA0BR0    = 104;				// 1MHz/9600 = ~104.2
    UCA0BR1    = 0;				// MSB 0
    UCA0MCTL   = UCBRS0;			// Second stage modulation (fine tuning)
    UCA0CTL1  &= ~UCSWRST;			// Enable USCI
    IE2	      |= (UCA0RXIE | UCA0TXIE);		// Enable RX and TX interrupts
#endif // SERIAL
    
    // Configure ADC
#ifdef ADC
    ADC10CTL0  = ADC10SHT_2 | ADC10ON | ADC10IE; // ADC10 on, interrupt enabled
    ADC10CTL1  = INCH_4;			// input A4
    ADC10AE0  |= BIT4;				// PA.0 ADC option select
    ADC10CTL0 |= (ENC | ADC10SC);		// Sampling and conversion start
#endif // ADC
    
    // note: should never come out of sleep
    for (; {
	_BIS_SR (LPM0_bits + GIE);		// Enter LPM w/interrupt
    }
    // NOTREACHED
}


#pragma vector=ADC10_VECTOR
__interrupt void adc10_isr (void)
{
    P1OUT |= BIT0;				//  LED on - sample finished
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void usciab0rx_isr (void)
{
}

#pragma vector=USCIAB0TX_VECTOR
__interrupt void usciab0tx_isr (void)
{
}

 
The LED on P1.0 lights as expected when only ADC is defined.  It does not light when SERIAL and ADC are defined.

Share this post


Link to post
Share on other sites

The UART tx interrupt flag will be set whenever the tx buffer can accept a char. So if you are not feeding chars to the UART, then it may get stuck in the ISR.

 

Turn on the tx interrupt enable when you have char(s) to send, turn it back off when there are no more to send.

Share this post


Link to post
Share on other sites


in main() remove this line: IE2 |= (UCA0RXIE | UCA0TXIE);  // Enable RX and TX interrupts

 

 

#pragma vector=USCIAB0TX_VECTOR

__interrupt void usciab0tx_isr (void)

{

    if(serial_bp && *serial_bp) {

        UCA0TXBUF = *serial_bp++;

    } else {

        IE2 &= ~UCA0TXIE;

    }

}

 

void serial_out (char *p)

{

while(IE2 & UCA0TXIE);

    serial_bp = p;              // set global pointer

    IE2 |= UCA0TXIE;            // start things going!

}

 

Share this post


Link to post
Share on other sites

This was indeed the problem.   Working code below:

//******************************************************************************
//
//  sample ADC
//  send value over serial
//  fully interrupt driven
//
//******************************************************************************

#include <msp430.h>
#include <stdlib.h>

#include "itoa.h"

volatile char *serial_bp = 0;
char buf[20];

void serial_out (char *p);
void str_crlf (char *p);

//
// Main entry point
//

int main (void)
{
    // disable the watchdog timer (re-enable it later as interval timer)
    WDTCTL = WDTPW | WDTHOLD;

    // use internal VLO for ACLK
    BCSCTL3 = (BCSCTL3 & ~(BIT4+BIT5)) | LFXT1S_2;
    
    // Set the DCO to the 1 MHz calibration frequency
    BCSCTL1 = CALBC1_1MHZ;			// set range 
    DCOCTL  = CALDCO_1MHZ;			// set step + modulation

    // configure/flash status LEDs
    P1DIR |=  (BIT0 | BIT6);			// configure LED pins
    P1OUT &= ~(BIT0 | BIT6);			// LEDs off

    // Configure USCI_A0
    UCA0CTL1  |= UCSWRST;			// insure USCI is disabled during config
    P1SEL      = (BIT1 | BIT2);			// select secondary peripheral function (USCI) for P1.1 (RXD) and P1.2 (TXD)
    P1SEL2     = (BIT1 | BIT2);			// (see table 16 on page 46 of msp430g2553.pdf)
    UCA0CTL1  |= UCSSEL_2;			// select clock source (source 2 == SMCLK)
    UCA0BR0    = 104;				// 1MHz/9600 = ~104.2
    UCA0BR1    = 0;				// MSB 0
    UCA0MCTL   = UCBRS0;			// Second stage modulation (fine tuning)
    UCA0CTL1  &= ~UCSWRST;			// Enable USCI
    IE2	      |= UCA0RXIE;			// Enable RX interrupts.  do not enable TX interrupts here!

    // Configure ADC
    ADC10CTL0  = ADC10SHT_2 | ADC10ON | ADC10IE; // ADC10 on, interrupt enabled
    ADC10CTL1  = INCH_4;			// input A4
    ADC10AE0  |= BIT4;				// PA.0 ADC option select

    // Display startup message, prime run loop
    serial_out ("Starting up\r\n");
    
    // note: should never come out of sleep
    for (; {
	_BIS_SR (LPM0_bits + GIE);		// Enter LPM w/interrupt
    }
    // NOTREACHED
}


//
// Interrupt: ADC10
//

#pragma vector=ADC10_VECTOR
__interrupt void adc10_isr (void)
{
    itoa (ADC10MEM, buf, 10);
    str_crlf (buf);
    serial_out (buf);
}

//
// Interrupt: RXBUF just got a character
//

#pragma vector=USCIAB0RX_VECTOR
__interrupt void usciab0rx_isr (void)
{
    // char in UCA0RXBUF
}

//
// Interrupt: TXBUF has just gone empty
// note: often (always?) generates an interrupt on startup!
//

#pragma vector=USCIAB0TX_VECTOR
__interrupt void usciab0tx_isr (void)
{
    IE2 &= ~UCA0TXIE;				// Disable TX interrupt

    if (! serial_bp) {				// safety check!
	return;
    }
    
    if (! *serial_bp) {				// more data?
	serial_bp = 0;				// safety!
	ADC10CTL0 |= (ENC | ADC10SC);		// Sampling and conversion start
	return;
    }
    
    UCA0TXBUF = *serial_bp++;		// transmit the next character
    IE2 |= UCA0TXIE;			// Enable TX interrupt
}


//
// add cr+lf to string after null
// no safety checking!
//

void str_crlf (char *p)
{
    while (*p) {
	p++;
    }
    *p++ = '\r';
    *p++ = '\n';
    *p++ = 0;
}

//
//  serial_out
//

void serial_out (char *p)
{
    serial_bp = p;				// set global pointer
    IE2 |= UCA0TXIE;				// Enable TX interrupt
}

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...