USART. Arduino Mega.

Es común encontrar en la mayoría de los microcontroladores una o varias usart (el Arduino Mega lleva 4), sirven para transmitir datos en serie entre varios dispositivos, un micro con otro micro, micro con el pc (RS232 puerto serie), etc.. La principal diferencia con protocolos como el spi o el i2c, es que la usart (uart en este caso) no necesita un pin de señal de reloj entre ambos dispositivos para realizar la comunicación. Cada dispositivo se pone de acuerdo en la velocidad a la que se va a realizar la transmisión (se fija la velocidad), y después de una condición de inicio cada dispositivo muestrea los bits en el tiempo acordado, por lo que sólo son necesarios dos pines para que el micro pueda enviar y recibir datos.

En el siguiente paso del proyecto MiniZ se requiere transmitir datos al pc para muestrear la respuesta del servomotor de dirección y poder calcular su regulador, para ello se va a utilizar la usart. La mayoría de los pcs ya no tienen puerto serie, que es el usado para comunicarnos con la usart del micro mediante el protocolo RS232, el Arduino Mega para soluccionar este inconveniente lleva un integrado FTDI232, que se encarga de convertir RS232 a USB. Una vez conectada la placa del Arduino Mega e instalados los drivers del FTDI232 podemos ver nuestro puerto usb convertido en uno serie (com) en el pc, y por tanto seleccionarlo para una comunicación micro-pc mediante programas como Hyperterminal.

Para poder realizar la comunicación, los dispositivos que se vayan a comunicar deben conocer varios aspectos de ésta. El primero es la velocidad a la que se va a realizar, es decir a qué baudios se va a realizar la transmisión. La comunicación comienza con una señal de Start, seguida de los bits a enviar, y se pueden seleccionar entre 5 y 9 bits a mandar, después tenemos que seleccionar si va a haber un bit de paridad para comprobar errores y por último si tenemos uno o dos bits de Stop. Estos parámetros han de estar configurados de igual manera en los dos dispositivos que se van a comunicar.



Se va a realizar una comunicación asíncrona para comunicarnos con el pc a través del integrado FTDI, por lo que sólo necesitaremos dos pines del micro (RX y TX). Lo primero es
determinar la velocidad de transmisión, que utiliza el reloj del microcontrolador, en este caso tenemos un reloj de 16 MHz.

Cada X tiempo una vez recibida la señal de Start el micro lee o escribe los unos y los ceros, así con cada bit hasta recibir el Stop. Este tiempo está determinado por el valor que almacenemos en el registro del microcontrolador UBRRn, es un registro de 16 bits dividido en dos de 8 bits, UBRRL y UBRRH, y lo primero es calcular este valor. Para ello encontramos la siguiente fórmula en el datasheet.



BAUD son los bits por segundo que queremos mandar y fosc es la frecuencia del cristal externo que colocamos para generar la señal de reloj del microcontrolador, el resultado de esta operación será almacenado en el registro UBRRn, y determinará la velocidad de las transmisión.

El problema es que el reloj del sistema no puede ser dividido por un número (16BAUD) que de un resultado exacto si este reloj no es múltiplo de una frecuencia determinada (1843200 Hz), por lo que siempre vamos a obtener un pequeño error. Si tenemos un reloj de 16 MHz y queremos un BAUD de 9600 bps (tenemos que elegir entre unas velocidades ya establecidas para comunicarnos con el pc), el valor a almacenar en UBRRn obtenido en la fórmula es 103.166, en UBRRn sólo podemos almacenar un número entero, por lo que si almacenamos 103 cometemos un error del 0.16%, errores menores de +- 2% son aceptables según Atmel para una comunicación de 8 bits, cuanto mayor sea el error menos fiable es la comunicación, en cada bit de Start se sincroniza el reloj.

Error máximo recomendando en función del número de bits a enviar + bit de paridad.

No podemos utilizar un cristal que nos produzca un error mayor que los recomendados en la tabla, ya que puede dar lugar a comunicaciones erróneas.

Error para un cristal de 16 MHz en función de la velocidad seleccionada.


Copiando el datasheet encontramos el siguiente código en C para establecer el valor del registro UBRRn:

#define FOSC 1843200// Clock Speed
#define BAUD 9600
#define (MYUBRR FOSC/16/BAUD-1)

void main( void )
{...
USART_Init ( MYUBRR );
...} // main

void USART_Init( unsigned int ubrr){
/* Set baud rate */
UBRRH = (unsigned char)(ubrr>>8);
UBRRL = (unsigned char)ubrr;
/* Enable receiver and transmitter */
UCSRB = (1<<RXEN)|(1<<TXEN);
/* Set frame format: 8data, 2stop bit */
UCSRC = (1<<USBS)|(3<<UCSZ0);
} // USART_Init


Por lo que sólo tendremos que establecer la frecuencia de nuestro cristal en el primer #define, a las dos últimas instrucciones aún no se ha llegado. Poniendo los defines podemos cambiar facilmente el BAUD sin necesidad de hacer cálculos ni poner números mágicos, el resultado de la fórmula lo pasamos en MYUBRR a la función y ésta lo almacena en los dos registros de 8 bits que forman el registro de 16 UBRR.

UCSRB = (1<<RXEN)|(1<<TXEN);
/* Set frame format: 8data, 2stop bit */
UCSRC = (1<<USBS)|(3<<UCSZ0);

Con las dos últimas instrucciones acabamos de configurar la USART para una comunicación no basada en interrupciones, la primera habilitaría los módulos de transmisión y recepción de la USART, pasando los pines TXn y RXn del microcontrolador a realizar esta actividad. La segunda configura el frame que vamos a usar, es decir el número de bits, bit de paridad, stop, se verán las distintas opciones más adelante.

Una vez que se ha inicializado la USART enviar y recibir datos es muy sencillo, sólo hay que leer y escribir en un registro para que comience la transmisión, y comprobar los flags que indican que ésta se ha realizado.

Para transmitir un dato podemos ver el siguiente código de ejemplo en el datasheet:

void USART_Transmit( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSRnA & (1<<UDREn)) )
;
/* Put data into buffer, sends the data */
UDRn = data;
}

Tenemos que escribir un dato en el registro UDRn, comprobando antes que el registro esté libre, es decir que si estaba transmitiendo un dato, comprobar que la transmisión ya haya finalizado.

Para recibir un dato lo hacemos de igual forma, sólo tenemos que leer este registro cuando nos indiquen que ha llegado un dato.

unsigned char USART_Receive( void )
{
/* Wait for data to be received */
while ( !(UCSRnA & (1<<RXCn)) )
;
/* Get and return received data from buffer */
return UDRn;
}

El registro UDRn tiene un doble buffer y se comporta de manera diferente cuando escribimos en el (mandamos el dato cargado por el pin TX) que cuando leemos (leemos el dato recibido por el pin RX).

En el microcontrolador encontramos los siguientes registros relacionados con la USART que debemos configurar para realizar una transmisión. Se van a comentar para una comuniación asíncrona sin interrupciones.



El registro anteriormente comentado, donde escribimos el dato a mandar y leemos el dato recibido. Para transmisiones de 5, 6 y 7 bits, los bits superiores son ignorados por TX cuando los enviamos, y son leídos como cero en el dato recibido.



Bit 7 - RXCn: USART Receive Complete: se pone a uno cuando hay datos sin leer en el buffer de entrada (UDRn) y se pone a cero cuando está vacío.
Bit 6 – TXCn: USART Transmit Complete: se pone a uno cuando todos los datos cargados en el buffer de salida (UDRn) han sido enviados y no hay nuevos datos a enviar.
Bit 5 – UDREn: USART Data Register Empty: indica poniendose a uno si UDRn está listo para recibir nuevos datos a enviar.
Bit 4 – FEn: Frame Error: nos indica un error en la recepción con un 1.
Bit 3 – DORn: Data OverRun: se pone a uno cuando se produce una situación de desbordamiento, el buffer y el registro de entrada están llenos y se detecta una nueva condición de Start, permanece a uno hasta que se lee el registro de entrada.
Bit 2 – UPEn: USART Parity Error: a uno cuando tenemos un error en el bit de paridad.
Bit 1 – U2Xn: Double the USART Transmission Speed: poniendo este bit a uno entramos en un modo de comunicación asíncrona al doble de velocidad, usaremos el normal.
Bit 0 – MPCMn: Multi-processor Communication Mode: habilita este modo.



Los 3 primeros bits están relacionados con las interrupciones, sirven para habilitarlas y se corrresponden con los 3 primeros bits del registro anterior.
Bit 4 – RXENn: Receiver Enable n: habilita el módulo de recepción de la USART pasando a utilizar el pin RxDn para tal fin.
Bit 3 – TXENn: Transmitter Enable n: igual que el anterior pero para la parte de transmisión.
Bit 2 – UCSZn2: Character Size n: junto con otros bits establece el número de bits a enviar.
Bit 1 – RXB8n: Receive Data Bit 8 n: en una frame de 9 bits de datos éste es el noveno y el de mayor peso, se debe leer antes que los del registro UDRn.
Bit 0 – TXB8n: Transmit Data Bit 8 n: el noveno bit a enviar en el frame de 9 bits de datos, se debe escribir antes que los del registro UDRn.



Con estos dos bits seleccionamos el modo de funcionamiento de la USART, en este caso 00.

• Bits 5:4 – UPMn1:0: Parity Mode: establecen la paridad, 00 en este caso.



Bit 3 – USBSn: Stop Bit Select: establece el número de bits de stop que generará el transmisor, 0 para 1 bit, un uno para dos.


Establecen el número de bits de datos que se van a enviar en cada frame.

Bit 0 – UCPOLn: Clock Polarity: selecciona entre el flanco de subida y de bajada del reloj para el modo síncrono.


Para probar todo lo anterior se va a desarrollar el programa más simple, y que será necesario para el proyecto MiniZ, que consiste en mandar bytes desde el micro y recibirlos en el pc a través de Hyperterminal para su posterior análisis.

Para la prueba se va a mandar al pc desde el microcontrolador la dirección "www.jmnlab.com" una letra cada segundo. El código de prueba según lo anterior es el siguiente:

#define F_CPU 16000000UL
#define BAUD 9600
#define MYUBRR ((F_CPU/(BAUD*16UL))-1)

#include <avr/io.h>
#include <util/delay.h>

void USART_Transmit( unsigned char data );

int main( void )
{
    UBRR0H = (MYUBRR>>8);
    UBRR0L = MYUBRR;
    /* Enable receiver and transmitter */
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    /* Set frame format: 8data, 2stop bit */
    UCSR0C = (1<<USBS0)|(3<<UCSZ00)|(1<<UCSZ01);

    while(1)
    {
        USART_Transmit('w');
        _delay_ms(1000);
        USART_Transmit('w');
        _delay_ms(1000);
        USART_Transmit('w');
        _delay_ms(1000);
        USART_Transmit('.');
        _delay_ms(1000);
        USART_Transmit('j');
        _delay_ms(1000);
        USART_Transmit('m');
        _delay_ms(1000);
        USART_Transmit('n');
        _delay_ms(1000);
        USART_Transmit('l');
        _delay_ms(1000);
        USART_Transmit('a');
        _delay_ms(1000);
        USART_Transmit('b');
        _delay_ms(1000);
        USART_Transmit('.');
        _delay_ms(1000);
        USART_Transmit('c');
        _delay_ms(1000);
        USART_Transmit('o');
        _delay_ms(1000);
        USART_Transmit('m');
        _delay_ms(1000);
        USART_Transmit(' ');
        _delay_ms(1000);
    }
}

void USART_Transmit( unsigned char data )
{
    /* Wait for empty transmit buffer */
    while ( !( UCSR0A & (1<<UDRE0)) );
    /* Put data into buffer, sends the data */
    UDR0 = data;
}


Para recibir los datos en el pc podemos usar Hyperterminal, antes se debe configurar una conexión.

Abrimos el programa y damos nombre a una nueva conexión y aceptar.


Seleccionamos el puerto serie donde nos aparece nuestro dispositivo FTDI, si no se sabe cuál es se puede mirar en el panel de control o se desconecta el Arduino y nos fijamos en que puerto estaba antes y ahora no.


Configuramos los parámetros de la conexión para que sean iguales que los establecidos en el microcontrolador.


Le damos a aceptar y ya tenemos a nuestro microcontrolador mandando el mensaje al pc, y podemos ver como éste va apareciendo en la pantalla.


Si conectamos un analizador lógico a la línea TX entre el micro y el integrado FTDI, podemos ver lo siguiente:

Como se mandan 8 bits cada segundo.


Y si nos acercamos podemos ver el dato mandado, en este caso el valor j.


Utilizar la USART para pasarle datos al pc desde el microcontrolador es algo muy sencillo y que resulta muy útil si queremos analizar datos recogidos por el micro en el pc. También nos puede servir para controlar el micro desde el pc mandando datos también mediante Hyperterminal.

Para cualquier comentario: Foro.