Implementación de UART por Interrupciones con Buffers de Software Circulares

Tabla de Contenidos

Implementación de UART por Interrupciones con Buffers de Software Circulares

<u>Introducción</u>

<u>Estructura del Módulo</u>

<u>Codificación del Módulo</u>

<u>Transmisión:</u>

- Productor

- Consumidor

<u>Recepción:</u>

- Productor

- Consumidor

<u>Introducción</u>

Con el fin de una programación estructurada, y como paso previo a una programación orientada a objetos, se ha incursionado en la implementación de módulos para el manejo de la transmisión y recepción de datos en un microcontrolador LPC2114.

El módulo contiene funciones que manipulan los datos elementales transformándolos en paquetes, o viceversa, para ambas operaciones de transmisión y recepción. Esto es, un protocolo y una capa intermedia que permite la comunicación con otros dispositivos utilizando el puerto serie RS232. También cabe destacar aquí, la programación directa realizada sobre registros de configuración del UART, que siguiendo con el criterio de programación adoptado se deberán componer en otro módulo.

<u>Estructura del Módulo</u>

Antes de empezar a describir las funciones del módulo comenzaremos describiendo los datos estructurados del mismo, que se presentan a continuación:

struct packet_t{ unsigned int index; unsigned int length; unsigned int position;};struct communication_t{ unsigned char uc_tx_ring_buf[TX_BUF_LEN]; unsigned char uc_rx_ring_buf[RX_BUF_LEN]; unsigned char uc_tx_packet_count; unsigned char uc_rx_packet_count; struct packet_t tx_packet[TX_PACKET_BUF_LEN]; struct packet_t rx_packet[RX_PACKET_BUF_LEN]; int i_rx_next_packet; int i_rx_new_packet; int i_tx_new_packet; int i_tx_next_packet; unsigned char e_error_status; unsigned char uc_status; unsigned char THR_empty;};

Como se puede observar, el tipo de variable *"communication_t"* está constituida principalmente por:

unsigned char uc_tx_ring_buf[TX_BUF_LEN]:

Buffer circular de caracteres en donde se colocarán los paquetes armados y listos para transmitir. Las rutinas del módulo se encargarán de recibir lo datos elementales (datos originales) y formar los paquetes que se almacenarán en este buffer, para luego ser transmitido todo el conjunto.

unsigned char uc_rx_ring_buf[RX_BUF_LEN]:

Buffer circular de caracteres en donde se alojarán los datos elementales recibidos. Si bien se reciben datos en forma de paquetes, las rutinas del módulo obtienen el dato elemental y lo almacenan en este buffer.

struct packet_t tx_packet[TX_PACKET_BUF_LEN]:

Buffer circular de paquetes que contiene información acerca de los últimos "TX_PACKET_BUF_LEN" cantidad de paquetes a transmitir.

struct packet_t rx_packet[RX_PACKET_BUF_LEN]:

Buffer circular de paquetes que contiene información acerca de los últimos "RX_PACKET_BUF_LEN" cantidad de paquetes recibidos. <ul>La información que brindan estos dos últimos buffers del tipo *'packet_t'* se refleja en sus variables miembros indicadas a continuación:

index: posición inicial del paquete en el buffer circular de caracteres.

length: cantidad de caracteres que ocupa el paquete en el buffer de caracteres.

position: variable auxiliar utilizada para recorrer el paquete.</ul>

unsigned char uc_tx_packet_count:

Semáforo que almacena la cantidad de paquetes armados y listos para transmitir.

unsigned char uc_rx_packet_count:

Semáforo que almacena la cantidad de paquetes recibidos y listos para ser procesados en el programa principal.

<ul>Como todos sabemos un semáforo es una variable protegida o tipo de dato abstracto que constituye el método clásico para restringir el acceso a los recursos compartidos, como la memoria compartida en un entorno de multiprogramación</ul>

int i_rx_next_packet:

Índice del buffer circular de paquetes del receptor. Hace referencia al siguiente paquete apto para leer o procesar (consumir).

int i_rx_new_packet:

Índice del buffer circular de paquetes del receptor. Hace referencia al nuevo paquete que se va a agregar en la recepción (producir).

int i_tx_new_packet:

Índice del buffer circular de paquetes del transmisor. Hace referencia al nuevo paquete que se va a agregar para la transmisión (producir).

int i_tx_next_packet:

Índice del buffer circular de paquetes del transmisor. Hace referencia al siguiente paquete que se va a transmitir (consumir).

<ul>Cabe destacar que en una futura actualización, y con el criterio de programación estructurada, estas variables miembros de la estructura communication_t deberán ser trasladadas a la estructura packet_t .</ul>

unsigned char e_error_status:

Indica los errores producidos en todo el proceso de transmisión/recepción.

unsigned char uc_status:

Indica el estado en el que se encuentra el proceso de transmisión/recepción.

unsigned char THR_empty:

Flag que indica el estado del buffer transmisor del hardware de la UART. Si tiene asignado TRUE significa que el buffer está vacío, y si es FALSE el buffer tiene al menos un byte.

<ul>Debemos aclarar que las dos primeras variables se encuentran sin uso actualmente, pero fueron pensadas para un futura implementación.</ul>

Concluiremos con esta sección destacando el paralelismo entre el proceso de transmisión y recepción. Ambos cuentan con buffers circulares y variables miembros totalmente independientes entre sí pero a la vez semejantes en estructura y funcionalidad.

<u>Codificación del Módulo</u>

Se puede dividir la codificación del módulo en dos partes, Productor y Consumidor , según la tarea que realizan. Básicamente el productor se encarga de procesar el dato (recibido o a transmitir) para que luego lo adquiera el Consumidor. Este concepto se presenta tanto en la transmisión como el la recepción.

La rutina del productor, a grandes rasgos, se encarga de cargar los caracteres secuencialmente en el buffer de caracteres, armar los paquetes (index y length), chequear desbordes, controlar la circularidad e incrementar la cuenta de paquetes *(semáforo)*.

La rutina del consumidor extrae los caracteres secuencialmente, liberando el espacio en el buffer de caracteres, controla la circularidad y decrementa la cuenta de paquetes *(semáforo)*.

<u>Transmisión:</u>

Gráfico de los buffers circulares y sus índices:

- Productor

El productor, para la transmisión, es una función llamada *"send_packet"* y recibe como parámetros un puntero a un vector de caracteres y el tamaño del mismo. A esta información le agrega los caracteres de inicio ('*') y final ('/r') de trama, y en el caso de que estuviese vacío el buffer de hardware (THR_empty == TRUE) carga un byte a TX_FIFO para que inicie el proceso del consumidor. A este byte o caracter se lo denomina "prime the pump".Por último se destaca que el valor de retorno de esta función hace referencia a la cantidad de bytes escritos. A continuación se muestra el prototipo de esta función: <b>int send_packet(char* buf, int count)</b>

- Consumidor

Es una rutina de código dentro de la interrupción de la UART, y se activa cuando se vacía el buffer de hardware de transmisión (TX_FIFO) de la UART0. A partir de allí carga hasta 16 caracteres desde el buffer circular de transmisión en el TX_FIFO. En caso de que el paquete sea más grande, espera hasta la próxima interrupción para terminar de transmitirlo y decrementar la cuenta de paquetes cuando esto sucede (semáforo del transmisor).

<u>Recepción:</u>

Gráfico de los buffers circulares y sus índices:

- Productor

Esta rutina de código se encuentra dentro de la interrupción de la UART y se activa cuando la RX_FIFO (buffer de hardware) llega a 14 bytes, o cuando no, pero transcurrido un determinado tiempo (timeout). A los datos recibidos les quita los caracteres de inicio y fin de trama, y los carga en el buffer circular de RX.

- Consumidor

Es una función llamada *"receive_packet"*. La misma recibe como parámetro un puntero a un vector, en el que cargará el último paquete procesado, y devolverá la cantidad de caracteres del mismo. A continuación se muestra el prototipo de esta función: <b>int receive_packet(char *buf)</b>

Fuente_Ejemplo.tar: Codigo fuente de ejemplo