Tutorial MSP430 – USCI_A0 – Comunicacion Serial UART

slidemsp430lengaujeccon930x300

En esta oportunidad corresponde hacer el tutorial que trata sobre el UART en el microcontrolador MSP430F2132. Es una comunicación asíncrona muy utilizada en el mundo de los micros ya que és por lo general el medio de interface con una PC o con dispositivos externos de comunicación (como un serial-ethernet bridge). Realizaremos un proyecto en el Proteus donde enlazaremos nuestra aplicacion con un programa de PC que permite ver como si fuese un osciloscopio el voltaje análogo medido en un pin del micro.

uartproyecto

Este post está basado en el trabajo hecho por Nicholas J. Conn cuya autoría del proyecto le pertenece (tan sólo le hice una adaptación del código para el micro MSP430F2132 compilado con el IAR), pueden ver el post original en su propio blog: njcs-launchscope-launchpad-oscilloscope.

Incluso le hice un video demostrativo tiempo atrás:

Comunicacion Serial UART

Este post será muy breve pues el manejo de una comunicación serial punto a punto con el UART es super sencillo, lo único que debemos saber es cómo configurar el baud rate, lo demás es pan comido.

Iniciaremos mencionando las características que tiene el UART del MSP430F2132:

  • Se ubica en el módulo USCI_A0 del MSP430F2132 (1 UART).
  • 7-o-8 bit de datos con paridad none, par o impar.
  • Registros de desplazamiento independientes para la transmisión y recepción.
  • Buffers separados para la transmisión y recepción de datos.
  • LSB-fisrt o MSB-first configurable para la transmisión y recepción de datos.
  • Soporta modo asíncrono para comunicaciones tipo RS232 (tema a desarrollar).
  • Soporta detección automática del baud rate para comunicaciones tipo LIN.
  • Soporta Built-in idle-line y Address-bit para comunicaciones multipunto tipo RS485.
  • Auto-wake up para salir de modos de bajo consumo.
  • Baud rate programable con modulación para rangos fraccionales.
  • Interrupciones para la transmisión y recepción de datos.

Pines del UART

Como ya comentamos, el UART de encuentra dentro del módulo USCI_A0, y mediante unos bits de configuración se setea dicho modulo para que trabaje con el protocolo UART. Antes de mencionar las configuraciones internas, primero hay que ubicar los pines del UART en el MSP430F2132.

uscia0bloques

Indagando en el datasheet encontramos que los pines del UART están a simple vista en el pin P3.5 que corresponde con el pin de recepción RXD, y el pin P3.4 que corresponde con el pin de transmisión TXD.

uartpines

Y cómo los configuramos como pines del UART? Pues por defecto todos los pines inicializan como digitales. En la tabla de abajo (extraída también del datasheet) se ubican los bits de configuración para los pines RXD y TXD. Fíjense que basta con poner 1 en el bit correspondiente del registro P3SEL.

uartdetalle

Hasta ahí, aún los pines no están totalmente definidos como UART pues tan sólo los hemos conectado al módulo USCI_A0 y dependerá finalmente de la configuración del USCI_A0 si termiarán siendo pines del UART u otra comunicación soportada por el USCI_A0.

Configuración del USCI_A0 para UART

Para que el módulo USCI_A0 se configure como UART necesitamos poner el bit UCSYNC=0 con lo cual le indicamos al módulo que vamos utilizar una comunicación asíncrona (no depende de un clock), luego especificamos el protocolo asíncrono UART seteando los bits UCMODEx=00 para elegir UART mode.

ucaxctl1a

ucaxctl1bits

Los parámetros de la comunicación UART como el bit de paridad (UCPEN y UCPAR), longitud de datos 7 u 8 bits (UC7BIT), bits de stop (UCSPB), y dirección de la data (UCMSB) también están presentes:

ucaxctl1b

ucaxctl1bits2

Baud Rate del UART en el USCI_A0

Sin la necesidad de entrar en mucho detalle (la parte fea está explicada en el pdf Family Users Guide MSP430Fxx2xx más conocido como slau144i.pdf) existen tablas que indican cómo configurar los registros que determinan el baud rate deseado para nuestra aplicación (siempre y cuando figure en la tabla, sino tendrán que aprender la parte fea).

Pero antes de buscar el baud rate deseado debemos especificar con que clock generar los baudios para el UART, esto se hace con los bits UCSSELx y su valor de entrada servirá para hacer el match junto con el baud rate deseado y ubicar los valores de configuración. dicha señal de clock dentro del USCI_A0 se llama BRCLK.

ucaxctl1

ucaxctl1bits3

Dependiendo del clock seleccionado a continuación debemos setear el bit UCOS16 (del registro UCA0MCTL) de la siguiente forma:

UCOS16 = 0 : Low-Frequency Baud Rate Generation. Este modo permite generar el baud rate desde clocks de baja frecuencia lo cual se traduce en bajo consumo de energía. En este modo no se recomienda utlizar osciladores de alta frecuencia (arriba de 8MHz) pues para ellos existe el siguiente modo. Están involucrados los registros UCBRx (compuesto por 2 registros UCA0BR0 y UCA0BR1), los bits UCBRSx (del registro UCA0MCTL). Su tabla es la siguiente:

baudlentop1

baudlentop2

UCOS16 = 1 : Oversampling Baud Rate Generation. Este modo permite utilizar frecuencias altas con bajo porcentaje de error de los baudios. Están involucrados los registros UCBRx (compuesto por 2 registros UCA0BR0 y UCA0BR1), los bits UCBRSx y UCBRFx (del registro UCA0MCTL). Su tabla es la siguiente:

baudfast

Interrupciones del UART en el MSP430F2132

Las interrupciones del UART son lo más práctico que hay, por ejemplo si se habilita la interrupción de recepción haciendo UCA0RXIE = 1, el flag UCA0RXIFG se activará cada vez que exista información para leer en el buffer de recepción UCA0RXBUF.

uartbufrx

bitsrx

Por otro lado, si se habilita la interrupción de transmisión haciendo UCA0TXIE = 1, el flag UCA0TXIFG se activará cada vez que el buffer de transmisión UCA0RXBUF esté disponible para enviar nueva información.

uartbuftx

bitstx

Programa de Aplicación

En el ejemplo de esta oportunidad participa el ADC10 junto con el UART pues vamos a simular un osciloscopio digital de baja frecuencia que lee una entrada analógica conectada a un potenciómetro cuya conversión en el ADC10 es enviada por el UART vía un puerto COM hacia un software alojado en la PC que permite visualizar en tiempo real las variaciones del potenciómetro. En el link de descarga de los archivos del ejemplo está también el ejecutable del software para visualizar los datos (el código fuente de dicho programa lo pueden encontrar en la web del autor: njcs-launchscope-launchpad-oscilloscope).

#include "io430.h"
#include "intrinsics.h"

// ASCII values for the commands
#define TEST_SPEED	0x31
#define M_A3			  0x32
#define STREAM			0x33
#define STOP			  0x34
#define M_TEMP			0x35
#define M_VCC			  0x36

unsigned char TXByte;		// Value sent over UART when Transmit() is called
unsigned char RXByte;		// Value recieved once hasRecieved is set
unsigned int i;			    // for loop variable
char hasReceived;			  // Lets the program know when a byte is received
char ADCDone;				    // ADC Done flag
unsigned int ADCValue;	// Measured ADC Value

// Function Definitions
void Transmit(void);
void Receive(void);
void Single_Measure(unsigned int);
void Single_Measure_REF(unsigned int, unsigned int);
void Start_Stream(unsigned int);
void Stop_Stream(void);

int main( void )
{
  // Stop watchdog timer to prevent time out reset
  WDTCTL = WDTPW + WDTHOLD;

	BCSCTL1 = CALBC1_1MHZ;			              // Set range
	DCOCTL = CALDCO_1MHZ;			                // SMCLK = DCO = 1MHz  

  P3SEL = 0x30;                             // P3.4,5 = USCI_A0 TXD/RXD
  UCA0CTL1 |= UCSSEL_2;                     // SMCLK
  UCA0BR0 = 104;                            // 1MHz 9600
  UCA0BR1 = 0;                              // 1MHz 9600
  UCA0MCTL = UCBRS0;                        // Modulation UCBRSx = 1
  UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
  IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt  

	P1DIR |= BIT0;					
	P1OUT &= ~BIT0;					                  // Turn off LED at P1.0

	hasReceived = 0;
	ADCDone = 0;

  __enable_interrupt();

  while(1) {

    if(hasReceived) {			                  // If the device has recieved a value
			Receive();
		}

		if(ADCDone) {					                  // If the ADC is done with a measurement		
			ADCDone = 0;				                  // Clear flag
			TXByte = ADCValue;		                // Set TXByte
			Transmit();						                // Send
			TXByte = (ADCValue >> 8);		          // Set TXByte to the upper 8 bits
			Transmit();
		}    

		if(~(hasReceived && ADCDone)) {         // Loop again if either flag is set
      __low_power_mode_0();                 // LPM0, the ADC interrupt will wake the processor up.
    }

	}

}

/**
 * Handles the received byte and calls the needed functions.\
 **/
void Receive()
{
	hasReceived = 0;	                        // Clear the flag
	switch(RXByte)			                      // Switch depending on command value received
	{
		case TEST_SPEED:
			P1OUT |= BIT0;					              // Turn on LED while testing
			for (i = 0; i != 0x100; i++)	        // Loop 256 times
			{
				TXByte = i;					                // Sends the counter as if it were a 16 bit value
				Transmit();
				TXByte = 0;
				Transmit();
			}
			P1OUT &= ~BIT0;					              // Turn off the LED
			break;

		case STREAM:
			Start_Stream(INCH_0);			            // Starts continuously reading A3
			break;

		case STOP:
			Stop_Stream();					              // Stops the ADC
			break;

		case M_A3:
			Single_Measure(INCH_0);					      // Reads A3 only once
			break;

		case M_TEMP:
			Single_Measure_REF(INCH_10, 0);			  // Reads the temperature sensor once
			break;

		case M_VCC:
			Single_Measure_REF(INCH_11, REF2_5V);	// Reads VCC once (VCC/2 internally)
			break;

		default:;
    }
}

/**
 * Starts to continuously read the 'chan'. The reference used is AVCC and not
 * 	an internal reference.
 **/
void Start_Stream(unsigned int chan)
{
  // Disable the ADC	   
	ADC10CTL0 &= ~ENC;						            
  // Turn on ADC10, 64 clock ticks for sample and hold, multiple mode, enable ADC interrupt
	ADC10CTL0 = SREF_2 + ADC10ON + ADC10SHT_3 + MSC+ ADC10IE;	
  // Set 'chan', SMCLK/8, repeat-single-channel	
	ADC10CTL1 = ADC10SSEL_3 + chan + ADC10DIV_7 + CONSEQ_2;		
  ADC10AE0 |= BIT0;
  // Enable and start conversion
	ADC10CTL0 |= ENC + ADC10SC;             			
}

/**
 * Stops the ADC.
 **/
void Stop_Stream()
{
	ADC10CTL0 &= ~ENC;		          // Disable ADC
	ADC10CTL0 &= ~ADC10SC;          // Stop conversion
	ADC10CTL0 &= ~ADC10ON;		      // Turn ADC off
}	

/**
 * Reads ADC 'chan' once using AVCC as the reference.
 **/
void Single_Measure(unsigned int chan)
{
  // Disable ADC	   
	ADC10CTL0 &= ~ENC;						
  // 64 clock ticks, ADC On, enable ADC interrupt
	ADC10CTL0 = SREF_2 + ADC10SHT_3 + ADC10ON + ADC10IE;		
  // Set 'chan', SMCLK
	ADC10CTL1 = ADC10SSEL_3 + chan;					
  ADC10AE0 |= BIT0;
  // Enable and start conversion
	ADC10CTL0 |= ENC + ADC10SC;             		
}

/**
 * Reads ADC 'chan' once using an internal reference, 'ref' determines if the
 *   2.5V or 1.5V reference is used.
 **/
void Single_Measure_REF(unsigned int chan, unsigned int ref)
{
  // Disable ADC   
	ADC10CTL0 &= ~ENC;	
  // Use reference, 64 clock ticks, internal reference on ADC On, enable ADC interrupt, Internal  = 'ref'  
	ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ref + ADC10IE;	
  // Set 'chan', SMCLK	  
	ADC10CTL1 = ADC10SSEL_3 + chan;		
  // Delay to allow Ref to settle	  
	__delay_cycles (128);							
  // Enable and start conversion  
	ADC10CTL0 |= ENC + ADC10SC;             		
}

/**
 * Transmits the value currently in TXByte. The function waits till it is
 *   finished transmiting before it returns.
 **/ 
void Transmit()
{ 
  // USCI_A0 TX buffer ready?
  while (!(IFG2 & UCA0TXIFG));              
  __delay_cycles (50); //TRUCO PARA EL PROTEUS, NO ES NECESARIO.
  UCA0TXBUF = TXByte;
}

/**
 * ADC interrupt routine. Pulls CPU out of sleep mode for the main loop.
 **/
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
	ADCValue = ADC10MEM;				            // Saves measured value.
	ADCDone = 1;  					                // Sets flag for main loop.
  __low_power_mode_off_on_exit();         // Enable CPU so the main while loop continues
}

/**
 * UART RX interrupt routine.
 **/
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
  RXByte = UCA0RXBUF;				
  hasReceived = 1;
  __low_power_mode_off_on_exit();  
}

Finalmente para enlazar el COMPIM del Proteus con el COM Port del programa LaunchPad Scope.exe, necesitan crear en su PC dos puertos virtuales y conectarlos de tal manera que en el COMPIM del Proteus eligen un COM Virtual y en el LaunchPad Scope.exe seleccionan el otro COM Virtual . Yo he utilizado el Virtual Serial Port Driver, les dejo un video de youtube para aprendan a hacerlo. Naturalmente en el video del ejemplo del tutorial también lo explicaré.

Videos

Archivos

4Shared

Boxnet

Los enlaces anteriores contenían el programa Launchscope.exe incompleto pues genera un error. Estos nuevos enlaces contienen dicho programa completo. Sorry XD!

4Shared

Boxnet

Estos nuevos enlaces están compilados con la versión del software IAR 5.40 :

4Shared

Boxnet

Henry Laredo Q.

bannermsp430

8 Responses to Tutorial MSP430 – USCI_A0 – Comunicacion Serial UART

  1. David dice:

    Hola hace rato que estado trabajando en los modos de bajo consumo como para comprobar que el micro es realmente de bajo consumo pero no lo he logrado y es porque el pin del reset cuando apago la fuente queda en cero y resetea el micro el problema es que si lo coloca a la pila se descarga la pila muy rapido y si lo coloco en el modo NMI el pin del reset tambien se descarga muy rapidamente no se si ese pin se pueda colocar en algun modo para que el bajo consumo sea realmente prolongado gracias!

    • Henry Laredo dice:

      Hola David, un gusto ver de que estás progresando en este campo, te puedo proponer una idea para que pruebes tu bajo consumo, en realidad lo iba a poner como tutorial pero recién lo pienso hacer en la saga “msp430 en vivo”, como bien sabrás el bajo consumo del msp430 se refleja en la manipulación de los clocks internos y sus respectivas oscilaciones, puedes realizar lo siguiente: a través de 3 pines del micro asignale a cada uno las respectivas señales del MCLK, SMCLK y ACLK, luego conectalos a 1 osciloscopio que soporte 3 canales, finalmente dentro de tu programa del micro explora los distintos modos de lowpowermode hasta conseguir la configuración deseada. Que te parece la idea? Con que micro estas trabajando? Saludos!

  2. Mark dice:

    Hola amigos!!! Estoy empezando a usar el MSP430 y necesito si me pueden ayudar con un ejercicio.
    1- Mediante el programa Terminal de la PC se deben enviar datos en formato ASCII por puerto serial al microcontrolador para que este realice operaciones de suma y resta, y devuelva los resultados por esta misma interfaz para ser visualizados en la Terminal.
    2- Los datos enviados desde la PC deben respetar el siguiente formato:

    [N0][N1][N2][OP][M0][M1][M2][CR]
    Donde: N0, N1 y N2 son 3 números en formato ASCII que representan un número de 3 dígitos (1° sumando). De la misma forma M0, M1 y M2 representan el 2° sumando. OP es el caracter que determina la operación a realizar: ‘+’ para suma y ‘-‘ para la resta. Y CR es el código ASCII de la tecla “Enter” (0x0D).
    Ejemplo: si se desea realizar la operación 23+400 se debe enviar la siguiente secuencia: [‘0’][‘2’][‘3’][‘+’][‘4’][‘0’][‘0’][CR]
    3- El resultado debe ser enviado desde el microcontrolador a la PC en el siguiente formato:

    [R0][R1][R2][R3][CR]
    4- Los datos deben ser enviados por la interfaz serial configurada sin control de flujo de datos, a 9600bps, 8 bit de datos, 1 bit de stop y paridad par.

  3. David dice:

    Oye gracias ya me funciono es que en el bajo consumo yo tenia mas circuitos conectados a Vcc y se llevaban el voltaje del supercap adicionalmente para el bajo consumo lo hice con el WDT en modo temporizador y primero en mi caso tuve que colocar la subrutuna y luego el modo de bajo consumo ya que de otra forma no funciona en mi caso.
    Gracias

  4. Oscar dice:

    Hola Henry,

    Me gustaria implementar este code en un msp340g2355, ya que tiene la posibilidad de comunicacion UART USCI_A0, entiendo que quizas lo unico que tendria que cambiar la configuracion de los pines, ejemplo el P3SEL, asi creo…, quizas me puedas ayudar a cambiar la configuracion del code.

    Gracias y saludos

    Oscar

Deja un comentario