domingo, 7 de septiembre de 2014

Tutorial MPLABX XC8 parte 2: configuración del módulo UART asíncrono sin interrupción


Saludos:

En esta entrada vamos a configurar el módulo USART del microcontrolador PIC16F628A con el objetivo de implementar la comunicación serial entre el micro y el computador.  Si bien este tema es un poco más avanzado, es muy importante utilizarlo pues con esto lograremos una interfaz de depuración para los demás tutoriales, por ejemplo cuando se utilize el conversor analógico-digital, podemos ver el resultado de la conversión en el puerto serial sin la necesidad de utilizar un LCD.

Específicamente realizaremos la configuración de módulo USART como un transmisor asíncrono, es decir, enviará datos al computador pero no podrá recibirlos.


¿Qué es USART?

USART (Universal Synchronous/Asynchronous Receiver/Transmitter) es un protocolo de hardware que nos permite transmitir datos de manera serial.  La USART puede ser síncrona o asíncrona, es decir, se puede utilizar una señal de reloj para leer los bits transmitidos o no.  En este tutorial configuraremos la USART como asíncrona, es decir, no se necesita la señal de reloj. 

Comúnmente la USART es un protocolo que utiliza 5V y se utiliza para comunicar dispositivos alejados un par de metros, pero si la distancia es mayor, debido a la caída de voltaje, se requiere utilizar niveles de tensión mayores, estos protocolos se conocen como RS-232 y RS-485.
Parámetros importantes de la USART

1.- Hardware de conexión

Tenemos varias alternativas para conectar un módulo USART de un microcontrolador a un computador.  El primero y más conocido es el que utiliza un circuito integrado MAX232 (fig.1).  Este circuito nos permite cambiar el nivel de voltaje del micro al requerido para el protocolo RS-232.  Tenemos que tener en cuenta, que en todo hardware de conexión serial, los pines de Rx y Tx van cruzados, es decir, el Rx del micro va al Tx del MAX232, y el Tx del micro va al Rx del MAX232.
Figura 1.  Circuito de conversión TTL a RS-232 con MAX232

El problema de esta alternativa de conexión es que necesitamos que el computador tenga un conector DB9 para el puerto serial  lo cual es un problema en computadores nuevos.  Entonces lo que podemos hacer es utilizar un cable conversor RS-232 a USB como el que se muestra en la figura 2.
Figura 2.  cable conversor RS232-USB

Esta solución es la más aplicable en mi país pues es relativamente barata (unos $3 por el circuito con el MAX232 y unos $10 por el cable), pero esta alternativa es “poco elegante” y un poco redundante, es decir, primero pasamos de 5V a RS232, y luego de RS232 a 5V para el USB, por lo que podemos simplificar esto utilizando un circuito integrado como el FT232RL.  Este circuito integrado nos permite conectar directamente al microcontrolador con el USB del computador creando un puerto serie virtual (fig.3)
ft232
Figura 3.  Conexión básica de un FT232 a un microcontrolador

Como podemos ver en la figura 3, únicamente se necesitan los pines de Rx y Tx del microcontrolador para realizar la comunicación por USB.  Éstos módulos se los puede adquirir (en Cuenca-Ecuador) en Deltrony, Smelektronik, y si pueden hacer la compra a China en DX.

2.- Trama de datos

El protocolo USART transmite una trama de datos con los bits configurados de la siguiente manera:
  • 1 bit de inicio
  • 0-9 bits de datos
  • 1 bit de paridad (opcional)
  • 1 o 2 bits de parada
Figura 4.  Trama de datos para USART

Como se puede observar en la figura 4, la USART tiene un estado IDLE o de espera, el cual mantiene el medio de transmisión en estado lógico 1 (5V para nuestro caso).  La figura muestra 8 bits de datos, pero el módulo USART de microcontrolador puede manejar 9 bits.

Si por ejemplo se requiere enviar 8 bits de datos, sin bits de paridad y con 1 bit de parada, esta configuración se conoce como 8-N-1, si requerimos 7 bits de datos, paridad impar y con dos bits de parada sería 7-O-2 y así según el requerimiento.

3.- Velocidad de transmisión

El concepto de velocidad de transmisión es un poco complicado en USART, pues la verdadera forma de expresar la velocidad de transmisión es en Baudios por segundo (tasa de baudios o baud rate) la cual indica el número de símbolos que se pueden transmitir´, pero se utiliza la expresión en bits por segundo.  Una explicación sobre la diferencia de ambos la puedes encontrar aquí.  Entonces, aunque escuches que se habla de “baudios”, lo que se configura en realidad es “bits por segundo” (sólo en el caso de USART).

Las tasas estándar de baud rate son: 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000 and 256000 bits por segundo.

Módulo USART del PIC16F628A

Una vez conocidos los parámetros básicos que tenemos que configurar en la comunicación USART, debemos estudiar el módulo para el efecto que tiene el microcontrolador.  Al utilizar un módulo integrado estamos utilizando hardware y nos limitamos a los canales de transmisión que el microcontrolador tiene.  En el caso de requerir más canales de Rx y Tx, podemos hacer bitbang (utilizar las librerías de SoftwareUART que manejan algunos compiladores).

En el caso del PIC16F628A se tienen los siguientes registros para la configuración de la USART:

bits1
Figura 5. Registro TXSTA

1.- El registro TXSTA (fig. 5) que se encarga del control de transmisión.  Para el transmisor asíncrono del ejemplo se utilizará TXEN=1, SYNC=0 y BRGH=0.
bits2
Figura 6.  Registro RCSTA

2.- El registro RCSTA (fig. 6) que se encarga del control de recepción de datos. Para el transmisor asíncrono del ejemplo se utilizará SPEN=1.
bits3
Figura 7. Registro PIE1

3.- El registro PIE1 (fig. 7) se encarga de la habilitación de interrupciones de periféricos. Para el transmisor asíncrono del ejemplo no se utilizará este registro, pero el bit TXIE es el encargado de habilitar la interrupción por transmisión.
bits4
Figura 8. Registro PIR1

4.- El registro PIR1 (fig. 8) se encarga de la habilitación de interrupciones de periféricos. Para el transmisor asíncrono del ejemplo no se utilizará este registro, pero el bit TXIF es el encargado de avisarnos si se genera una interrupción por transmisión, es decir, si se está realizando una transmisión.

Programación en MPLABX

Bueno, basta de bla bla, y empecemos a programar.  Los pasos para configurar el módulo USART del PIC16F628A como transmisor asíncrono o UART son los siguientes:
  1. Declarar los puertos B1 y B2 como ingresos: TRISB<1> y TRISB<2> =1
  2. Inicializar el bit BRGH y el registro SBPRG con el baud rate requerido
  3. Activar el puerto asíncrono con SYNC=0 y SPEN=1
  4. Si se requiere que la transmisión sea por interrupción TXIE=1
  5. Si se requiere que la transmisión sea de 9 bits TX9=1
  6. Habilitar la transmisión con TXEN=1
  7. Si se seleccionó la transmisión de 9 bits, cargar el 9no bit en TX9D
  8. Cargar los bits de datos en el registro TXREG
Con esto vamos a programar una transmisión contínua asíncrona 9600/8N1 de la siguiente manera:

Primero creamos el proyecto como se hizo en el tutorial anterior, y cargamos los bits de configuración del microcontrolador con la diferencia que ahora utilizaremos un cristal externo de 20MHz.  La utilización del cristal externo nos permite una mayor precisión en el tiempo de reloj, se pudiera utilizar el oscilador interno del micro pero esto implicaría errores de transmisión.  Entonces los bits de configuración serían los siguientes:

#pragma config FOSC = HS 
#pragma config WDTE = OFF  // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON //(RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = ON // Brown-out Detect Enable bit (BOD enabled)
#pragma config LVP = OFF //RB4/PGM pin has digital I/O function
#pragma config CPD = OFF //(Data memory code protection off)
#pragma config CP = OFF //(Code protection off)


Luego creamos la rutina de inicialización del puerto B con B1 y B2 como ingresos.

void initPorts(void)
{
 TRISB = 0b00000110;
}


Creamos la rutina de inicialización del módulo USART con los bits de configuración descritos anteriormente

void initUART(void)
{
    TXSTAbits.BRGH=0;
    SPBRG=32;
    TXSTAbits.SYNC=0;
    RCSTAbits.SPEN=1;
    TXSTAbits.TXEN=1;
}


El valor SPBRG=32 se obtiene de la tabla de baud rates de la hoja de datos para BRGH=0 y un oscilador externo de 20MHz(fig. 9).  Así, si por ejemplo quisiéramos otro valor de baud rate como 19200, entonces SPBRG=15.

bits5

Figura 9.  Selección de SPBRG para BRGH=0 y baud rate de 9600

Por último en el bucle principal se llaman a las rutinas de inicialización y se carga el buffer de transmisión TXREG con los datos que queremos enviar.

int main(void)
{
 // Inicializar puertos y periféricos
 initPorts();
        initUART();
 // Repetir indefinidamente
 while(1)
 {
  TXREG = 0b01001000; // ascii H
                _delay(1000000);
                TXREG = 0b01001111; // ascii O
                _delay(1000000);
                TXREG = 0b01001100; // ascii L
                _delay(1000000);
                TXREG = 0b01000001; // ascii A
                _delay(1000000);
                TXREG = 0b00001101; // ascii \r
                _delay(1000000); 
                TXREG = 0b00001010; // ascii \n 
                _delay(1000000); 
 }
}


Como se puede observar, se carga el registro TXREG con los valores ASCII de la letra H, luego de la O y así hasta formar la palabra “HOLA”, además se agrega un retardo entre cada envío con el objetivo de poder observar con facilidad el envío de datos y sobre todo para permitir que el módulo pueda enviar todos los bits.  El retardo es de casi un segundo, pero se puede utilizar 50ms para que todos los datos puedan enviarse.  La simulación en Proteus se puede observar en la figura 10.

sim1

Figura 10.  Simulación UART con retardo de 1 segundo

Entonces, para demostrar este efecto del retardo introducido, podemos eliminar las líneas de código “_delay(1000000);” compilamos, y corremos la simulación.  Se observará que no se puede transmitir la palabra por completo (fig. 11).

sim2

Figura 11.  Simulación UART sin retardo

Para solucionar esto podemos “tantear” el retardo requerido (unos 50ms) o hacer uso de la bandera TXIF del registro PIR1, la cual nos indica si el buffer de transmisión está ocupado y así empezar a enviar datos una vez que el módulo termine el proceso de transmisión de todos los bits anteriores.  Para esto creamos una rutina para testear si el buffer está ocupado:

void busyUART(void)
{
    ocupado:
    if (PIR1bits.TXIF==0){
        goto ocupado;
    } else return;
}

En esta rutina creamos una subrutina llamada “ocupado” la cual pregunta si TXIF=0, es decir, si el buffer está ocupado, si este es el caso, el procesador se mantiene en esta rutina hasta que el buffer se desocupe y retorne al bucle principal.  Entonces, llamamos a la rutina busyUART antes de cada envío:

while(1)
{
   busyUART();
   TXREG = 0b01001000; // ascii H
   busyUART();
   TXREG = 0b01001111; // ascii O
   busyUART();
   TXREG = 0b01001100; // ascii L
   busyUART();
   TXREG = 0b01000001; // ascii A
   busyUART();
   TXREG = 0b00001101; // ascii \r
   busyUART();
   TXREG = 0b00001010; // ascii \n
}


Si realizamos la simulación, deberíamos observar la palabra “HOLA” aparecer en el terminal virtual (fig.12).

sim3

Figura 12.  Simulación UART testeando el bit TXIF

Este funcionamiento podemos integrarlo en una librería para obviar la programación de todo el código cada vez que hacemos un proyecto.  Esto lo puedes observar en la parte 3 de este tutorial.

El programa completo, la simulación y el archivo en MPLABX lo tienen a continuación.  Es todo por esta entrada, espero que la información les sea de utilidad.  Hasta una próxima entrega, adiós.