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.

MSP-EXP430FR2355: MSP-EXP430FR2355 Meaningless UART Output

Part Number: MSP-EXP430FR2355
Other Parts Discussed in Thread: MSP430FR2355

Hi All

 

I am doing a very simple project (so I thought) where I read the value in on P1.4 ADC from a 10k pot connected to 3.3V.

 

The ADC reads the value with no issues what so every. However, when I try to transmit the HEX value out on the UART I get meaningless characters which says to me it is a baud rate configuration issue. But I followed all the user guides and the formula and check it against several online AI tools and tey all come up with the same answer. 

 

I suspect it is something to do with how I have set up my UART interrupts

 

I am using SMCLK = 1MHz ad the desired baud rate is 9600

 

The code is below and ay help would be much appreciated.

#include <msp430.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

//Variables

volatile unsigned int ADC_value = 0;

void UART_init(void)
{

/* Configure UART pins (P4.2 = RXD, P4.3 = TXD) */
	
	P4SEL0 |= BIT2 | BIT3;                    			// Set P4.2 and P4.3 to UART mode
	P4SEL1 &= ~(BIT2 | BIT3);

	UCA1CTLW0	|= UCSWRST;					// Put eUSCI_A1 in reset
	UCA1CTLW0	|= UCSSEL__SMCLK;				// Choose SMCLK clock source

/*	Baud Rate Calculation for 9600 baud with 1,000,000 Hz SMCLK:
  	1000000 / 9600 = 104.166 -> UCBRx = 104, fractional portion = 0.166
  	Using Table 22-4 from User's Guide: UCBRSx = 0x11*/ 

	UCA1BR0		= 0x68;						// 1MHz 9600 baud
	UCA1BR1		= 0;
	UCA0MCTLW	= 0x1100;
	
	UCA1CTLW0	&= ~UCSWRST;					// Initialize eUSCI state machine
	UCA1IE		|= UCTXIE;				 	// Enable USCI_A1 TX interrupt
	
	__bis_SR_register(GIE);
	__no_operation();
}

int main(void)
{
	WDTCTL = WDTPW | WDTHOLD;	            			// stop watchdog timer
	
/* Disable the GPIO power-on default high-impedance mode to activate
 * previously configured port settings*/

	PM5CTL0 &= ~LOCKLPM5;

	UART_init();

/* Configure ADC Channel Pins*/

	P1SEL1 |= BIT4;                         			// Configure P1.4 for ADC
	P1SEL0 |= BIT4;

/* Configure ADC12 */

	ADCCTL0     =   ADCSHT_2 | ADCON;       			// ADCON, S&H=16 ADC clks
	ADCCTL1     =   ADCSHP;                 			// ADCCLK = MODOSC; sampling timer; ADC conversion start source is ADCSC bit
	ADCCTL2     =   ADCRES_2;               			// Set ADCRES_2 to binary 10 for 12 bit resolution
	ADCIE       |=  ADCIE0;                 			// Enable ADC conv complete interrupt
	ADCMCTL0    |=  ADCINCH_4 | ADCSREF_0;  			// ADC channel A4 selected as input and internal reference selected 000b = {VR+ = AVCC and VR- = AVSS}

/* Configure voltage reference for ADC conversion */
	PMMCTL0_H = PMMPW_H;                                      	// Unlock the PMM registers
	PMMCTL2 |= INTREFEN;                                      	// Enable internal reference 1.5V
	__delay_cycles(400);                                     	 // Delay for reference settling

	while(1)
	     {
	        ADCCTL0 |= ADCENC | ADCSC;                            	// Sampling and conversion start
	        __bis_SR_register(LPM0_bits | GIE);                  	// LPM0, ADC_ISR will force exit
	        __no_operation();
	     }
}

#pragma vector = ADC_VECTOR
__interrupt void ADC_ISR(void)

{
    switch (__even_in_range(ADCIV, ADCIV_ADCIFG))
    {
        case    ADCIV_ADCIFG: 						// ADC12MEM1 Interrupt
                ADC_value = ADCMEM0; 					// Save MEM0
                __bic_SR_register_on_exit(LPM0_bits | GIE);		// Exit CPU, clear interrupts
                break;
               default:
                break;
    }
}

#pragma vector = USCI_A1_VECTOR 					// Vector depends on specific MSP430 family variant
__interrupt void USCI_A1_ISR(void)
{
    switch(__even_in_range(UCA1IV, USCI_UART_UCTXCPTIFG))
    {
        case USCI_NONE: 
            break;
            
        case USCI_UART_UCRXIFG:
            break;
            
        case USCI_UART_UCTXIFG:
        	
            UCA1TXBUF = (ADC_value);
            break;
            
        case USCI_UART_UCSTTIFG:
            break;
            
        case USCI_UART_UCTXCPTIFG:
            break;
        default:
        	break;
    }
}



UART Issue.txt 

  • >  UCA1TXBUF = (ADC_value);

    This appears to be sending the raw ADCMEM value, which is unlikely to appear as (readable) ASCII.

    Some terminal emulators have a "hex" mode which will (in effect) do the ASCII formatting for you. In the absence of that, you probably need to use snprintf() or similar to format it. (The formatted result will be multiple bytes.)

  • Thank you,

    I have a terminal with a HEX mode and it is starting to make a bit more sense but the values are a bit all over the place

    I have noticed that I am using UCA0MCTLW for my modulation of the baud rate, should I really be using UCA1MCTLW?

  • Use a scope or Logic analyzer to verify the baud rate.

    Do you really want to continually send a stream of bytes even if there is no "fresh" ADC data?

    Your ADC is 12 bits, what do you expect assigning an int with 12 bits of data to a byte to do?

    UCA1TXBUF = (ADC_value);

  • 1) You're writing (only) the low-order byte of the sample, which is the part that changes most. Try 8-bit sampling (ADCRES_0).

    2) WIth a 10K pot, your sampling time may be too short. Try setting the ADCSHT higher. At the moment you're not in a hurry so try setting it to the maximum (ADCSHT_12).

  • Or, he can just shift the int right 4 bits.

    UCA1TXBUF = (ADC_value >> 4);

  • Hi everyone thank you for your help.

    I have implemented what you suggested, and I can now new the HEX values on Docklight using the HEX conversion facility.

    Sometimes I get a few FEs when I should be getting FFs but I think that is down to the adc sending tuff to the UART when it isn't ready. I plan to implement some code for the ADC to only do a conversion when the UART is ready. I know this would be slow, but I don't need speed at the moment.

    My main goal is to output the ADC result on the UART in a decimal number from 0 - 4095, Does anyone know of a tutorial out there that goes through this?

    The MSP academy doesn't really cover this, and I just need something to cover the basics of formatting the data in a readable manner to be output on the UART

    Thank you again for your help! 

  • Hi All



    I managed to get a working program that takes the ADC value from a pot that is connected to P1.4 and 3.3V and output the ADC value to a terminal at 115200 baud via the UART.



    I slowed everything down dramatically such as the ADC clock cycles and put a lot more waits in there to allow the ADC to finish what it was doing and for the UART to finish sending.



    The ADC value can still jump around a little bit but at least it is centered around a value, I'm not sure if anyone knows the reason for this, I'm guessing the MSP430FR2355 Launchpad board doesn't have very good filtering on it a such as an RC filter on the ADC input. If anyone knows or as any ideas, I would be open to suggestions. But on the whole, it works!

    Edit: For some reason the TI forum won't let me paste the code in, I will put this in at a later date



  • It doesn't really have any filtering.

    The traditional way to do it is to average about 5 samples.

    You can also use a "running average".

    If you want an 8 sample running average, when you first get going just do an average of 8 values. After that, take your running average and multiply by 7/8 and add 1/8 of the current ADC reading. It will take some time to respond to step changes, so you sometimes have to put in step detection to reset things if you detect a step.

  • After that, take your running average and multiply by 7/8 and add 1/8 of the current ADC reading.

    Not actually a moving average filter but a recursive (IIR) filter. It has the great advantage of requiring less storage. 

    Assuming that the noise is zero mean, and it almost always is, it can be used to extend the precision of the ADC. Add 8 samples but do not shift. You now have an extra 3 bits. Just remember where the binary point is.

  • I was also thinking, which is along the same lines as you that you should sum the result variable 7 times, add the new sample, and then divide by 8, that way you do not lose precision from the new sample.

  • The recursive version is simple but a bit tricky since you have to keep track of the binary point. Simplest is keeping the filtered value in Q3 (3 fractional bits) while the spiffy new ADC reading isn't.

    Shift the current filtered value right 3 bits and subtract from the latest ADC reading, then add to the current filtered value to get the new version. 

    (You don't have to store the filtered value in Q3. Use whatever you prefer but keep track of the binary point!)

  • I would just do it all in counts and convert at the end, not as much precision, but easier to keep track of!

**Attention** This is a public forum