13/12/2022
Los microcontroladores AVR son la columna vertebral de innumerables proyectos de electrónica, desde sistemas embebidos simples hasta dispositivos complejos. Una de las interfaces de comunicación más fundamentales y ampliamente utilizadas en estos chips es la UART (Universal Asynchronous Receiver/Transmitter), esencial para interactuar con módulos externos como GPS, Bluetooth, o simplemente para depuración a través de un puerto serie. Sin embargo, la evolución constante de las herramientas de desarrollo y los propios microcontroladores exige que las librerías que utilizamos se mantengan al día. Este artículo explora una librería UART actualizada para AVR, que resuelve problemas de compatibilidad y añade funcionalidades muy solicitadas, como la gestión de múltiples puertos serie y la implementación de buffers circulares.

La comunicación serial, aunque aparentemente sencilla, puede volverse compleja cuando se trata de manejar grandes volúmenes de datos o de interactuar con múltiples dispositivos simultáneamente. Las librerías eficientes son clave para simplificar esta tarea, permitiendo a los desarrolladores centrarse en la lógica de su aplicación en lugar de los intrincados detalles de bajo nivel del hardware. La librería que presentamos aquí busca precisamente eso: proporcionar una base sólida y flexible para todas tus necesidades de comunicación UART en el ecosistema AVR.
- ¿Por Qué una Actualización Era Necesaria?
- Las Modificaciones Clave Realizadas
- Uso de la Librería Actualizada
- Consideraciones de Licencia
- Modelos AVR Compatibles
- Comprendiendo el Buffer Circular y el Soporte Multi-UART
- Funciones y Macros Esenciales de la Librería
- Manejo de Errores en la Comunicación UART
- Preguntas Frecuentes (FAQs)
- ¿Cómo puedo configurar el tamaño de los buffers de recepción y transmisión?
- ¿Es compatible esta librería con todas las placas Arduino que usan microcontroladores AVR?
- ¿Cómo puedo usar múltiples puertos UART (USART) en mi microcontrolador?
- ¿Qué significa un error de "Overrun" o "Buffer Overflow"?
- ¿Qué licencias rigen el uso de esta librería?
¿Por Qué una Actualización Era Necesaria?
La necesidad de una librería UART renovada surgió de varias limitaciones y desafíos presentes en versiones anteriores, como la popular librería de Mr. Fleury. A medida que los microcontroladores AVR y sus herramientas de desarrollo, como AVR-Libc, evolucionaban, ciertos aspectos de las librerías más antiguas se volvían obsoletos o incompatibles. Aquí detallamos las razones principales que impulsaron esta actualización:
La Evolución de AVR-Libc y los Vectores de Interrupción
Una de las razones fundamentales para la actualización fue el cambio en el formato de los nombres de los vectores de interrupción introducido en AVR-Libc versión 1.4.0 (siendo 1.6.6 la versión actual en el momento de la escritura inicial de la librería). Las librerías antiguas, como la de Mr. Fleury, utilizaban el formato antiguo SIG_, lo que generaba varios problemas. En primer lugar, muchos modelos nuevos de AVR, lanzados después de esta versión de AVR-Libc, solo estaban definidos con el nuevo formato _vect. Esto significaba que la librería original no podía ser utilizada directamente con estos microcontroladores modernos sin modificaciones manuales. En segundo lugar, la página Interrupt.h de AVR-Libc ya insinuaba que el formato antiguo podría ser deprecado en el futuro, lo que hacía imperativo el cambio para garantizar la sostenibilidad y compatibilidad a largo plazo de la librería.
Integración con Librerías C++ y Funciones al Estilo Arduino
Otro desafío importante surgió al intentar "conectar" la librería UART de C de Mr. Fleury con librerías C++ como la Xbee-Arduino de Andrew Rapp, que permite comunicaciones en modo API con módulos XBee. La naturaleza de C++ y los problemas de ámbito al intentar crear una clase "wrapper" para la librería original sin modificarla directamente, llevaron a la conclusión de que era necesario integrar funciones específicas de Arduino. Las funciones available() y flush(), omnipresentes en el entorno Arduino HardwareSerial, son increíblemente útiles para gestionar el flujo de datos y el estado del buffer de recepción. La falta de interés de Mr. Fleury en añadir nueva funcionalidad a su librería original reforzó la decisión de crear una versión actualizada que incluyera estas características esenciales para una integración más fluida con el ecosistema Arduino y proyectos en C++.
Las Modificaciones Clave Realizadas
La librería actualizada incorporó una serie de modificaciones significativas para abordar las limitaciones mencionadas y mejorar la experiencia del desarrollador. Estas mejoras no solo resuelven problemas de compatibilidad, sino que también añaden funcionalidades que hacen que la librería sea más robusta y fácil de usar.
Actualización de Nombres de Vectores de Interrupción y Rutinas de Servicio (ISR)
Se actualizó el formato de los nombres de los vectores de interrupción UART/USART para todos los dispositivos AVR contenidos en la librería original. Se pasó del antiguo formato SIG_ al nuevo formato _vect, siguiendo como guía los archivos de cabecera de AVR-Libc versión 1.6.6. Este cambio requirió algunas adaptaciones adicionales debido a inconsistencias en los nuevos nombres en esa versión específica de AVR-Libc. Además, las etiquetas de las Rutinas de Servicio de Interrupción (ISR) se actualizaron del antiguo formato SIGNAL al nuevo formato ISR, estandarizando el código con las prácticas modernas de AVR-Libc.
Soporte para Nuevos Modelos ATmega 'P'
Una adición crucial fue el soporte para los modelos más recientes de microcontroladores, como los ATMEGA48P, ATMEGA88P, ATMEGA168P y ATMEGA328P. Aunque sus requisitos de hardware son idénticos a los de sus predecesores sin la 'P' (ATMEGA48/88/168), sus archivos de cabecera solo utilizan el nuevo formato de vectores de interrupción. La inclusión de estos modelos garantiza que la librería sea compatible con una gama más amplia de microcontroladores modernos, incluyendo los populares chips utilizados en las placas Arduino.
La Conveniencia de available() y flush()
Se implementaron las funciones available() y flush(), modeladas a partir de la librería Arduino HardwareSerial. Estas funciones son de gran utilidad para la gestión de la comunicación serie. La función available() permite verificar cuántos bytes hay disponibles en el buffer de recepción sin bloquear el programa, lo cual es fundamental para una comunicación no bloqueante. Por su parte, flush() permite limpiar el buffer de recepción, descartando cualquier dato pendiente. Estas funciones se añadieron con soporte tanto para modelos AVR con una UART como para aquellos con dos UART, manteniendo la coherencia con el resto de la librería.
Uso de la Librería Actualizada
La utilización de la librería actualizada es bastante intuitiva, especialmente si estás familiarizado con el entorno Arduino. Para información detallada sobre el uso de las funciones available() y flush() al estilo Arduino, se sugiere consultar la Referencia de Arduino. La sección "Serial" de la documentación de Arduino explica completamente el uso de estas funciones, incluyendo ejemplos prácticos. Se ha procurado mantener la sintaxis y el comportamiento de ambas funciones idénticos a los de Arduino para facilitar la transición y el desarrollo.
La librería está diseñada para ser flexible y configurable. Los tamaños de los buffers de recepción y transmisión (UART_RXn_BUFFER_SIZE y UART_TXn_BUFFER_SIZE) deben ser potencias de 2 y pueden ser definidos como símbolos en la configuración de tu compilador o directamente en el archivo uart.h. También es posible habilitar o deshabilitar USARTs específicos (por ejemplo, USART0_ENABLED) para optimizar el uso de recursos.
Consideraciones de Licencia
La librería original de Mr. Fleury estaba licenciada bajo la GPL, versión 2 o posterior. La librería Arduino HardwareSerial, de la cual se adaptaron las funciones available() y flush(), está licenciada bajo la LGPL, versión 2.1 o posterior. Dado que la GPL es más restrictiva que la LGPL, y sin ser experto legal, se optó por licenciar la librería actualizada bajo la GPL. Esta elección se hizo para asegurar que no se violaran los derechos de Mr. Fleury como licenciante original, optando por la licencia más restrictiva para garantizar la compatibilidad legal.

Modelos AVR Compatibles
La librería actualizada soporta una amplia gama de microcontroladores AVR, incluyendo los modelos clásicos y los más recientes con el sufijo 'P'. A continuación, se presenta una tabla con los modelos AVR soportados, en orden alfabético, resaltando las nuevas adiciones:
| Familia | Modelo | Notas |
|---|---|---|
| AT90S | 2313, 2333, 4414, 4433, 4434, 8515, 8535 | Modelos clásicos |
| ATmega | 8, 16, 32, 48 | |
| ATmega | 48P | ¡Nuevo! |
| ATmega | 64, 88 | |
| ATmega | 88P | ¡Nuevo! |
| ATmega | 103, 128, 162, 163, 164P, 168 | |
| ATmega | 168P | ¡Nuevo! |
| ATmega | 169, 323, 324P, 325 | |
| ATmega | 328P | ¡Nuevo! |
| ATmega | 329, 640, 644, 644P, 645, 649, 1280, 2560 | |
| ATmega | 3250, 3290, 6450, 6490, 8515, 8535 | |
| ATtiny | 2313 |
Comprendiendo el Buffer Circular y el Soporte Multi-UART
Uno de los aspectos más potentes de esta librería es su implementación de buffers circulares para la transmisión y recepción de datos UART, junto con el soporte para múltiples interfaces USART, lo que permite una comunicación serial flexible y eficiente.
¿Qué es un Buffer Circular y Por Qué es Crucial para UART?
Un buffer circular, también conocido como buffer de anillo o cola circular, es una estructura de datos que utiliza un solo bloque de memoria contigua como si fuera circular. Es ideal para la comunicación serial porque permite almacenar datos entrantes (recepción) o salientes (transmisión) de manera eficiente, evitando la pérdida de datos y permitiendo operaciones no bloqueantes. En el contexto de UART, cuando un byte llega o está listo para ser enviado, la interrupción correspondiente lo coloca o lo toma del buffer. Esto es crucial porque:
- Previene la pérdida de datos: Si los datos llegan más rápido de lo que el programa principal puede procesarlos, el buffer los almacena temporalmente hasta que el microcontrolador esté listo.
- Operaciones no bloqueantes: El programa principal no tiene que esperar a que un byte se reciba o se transmita. Simplemente escribe en el buffer de transmisión o lee del buffer de recepción, y las rutinas de interrupción se encargan del trabajo de bajo nivel en segundo plano. Esto permite que el microcontrolador realice otras tareas mientras la comunicación serial se lleva a cabo.
- Eficiencia de memoria: Reutiliza el mismo espacio de memoria, optimizando su uso.
La librería permite configurar el tamaño de estos buffers (UART_RXn_BUFFER_SIZE y UART_TXn_BUFFER_SIZE) para cada UART, los cuales deben ser una potencia de 2 (por ejemplo, 128, 256 bytes) para optimizar las operaciones de puntero.
Soporte Multi-UART: USART0, USART1, USART2, USART3
Muchos microcontroladores AVR avanzados, como el ATmega2560, poseen múltiples interfaces USART (UARTs). La librería actualizada reconoce esta capacidad y proporciona funciones específicas para cada una de ellas, permitiendo la comunicación simultánea a través de diferentes puertos serie. Esto se logra mediante:
- Macros de Habilitación: Definiciones como
USART0_ENABLED,USART1_ENABLED, etc., que permiten al compilador incluir el código relevante solo para las UARTs que se van a utilizar, ahorrando espacio en memoria. - Funciones Dedicadas: Cada USART tiene su propio conjunto de funciones para inicialización, envío, recepción y gestión de buffers. Por ejemplo,
uart0_init(),uart1_init(),uart2_init(),uart3_init(), y sus correspondientesgetc(),putc(),available(),flush(), etc. Esto garantiza que cada puerto se gestione de forma independiente y eficiente.
Esta capacidad multi-UART es invaluable para proyectos que requieren comunicación con varios módulos seriales a la vez, como un módulo GPS en un puerto y un módulo Bluetooth en otro, sin que interfieran entre sí.
Funciones y Macros Esenciales de la Librería
La librería proporciona un conjunto completo de funciones y macros para facilitar la interacción con los puertos UART. A continuación, se detallan las más importantes:
Inicialización y Configuración
uartX_init(uint16_t baudrate): Inicializa el puerto UART especificado (X puede ser 0, 1, 2 o 3) y establece la velocidad de baudios. El parámetrobaudratese calcula usando las macrosUART_BAUD_SELECToUART_BAUD_SELECT_DOUBLE_SPEED.UART_BAUD_SELECT(baudRate, xtalCpu): Macro para calcular el valor del registro UBRR para una velocidad de baudios dada y una frecuencia de cristal, en modo normal.UART_BAUD_SELECT_DOUBLE_SPEED(baudRate, xtalCpu): Macro para calcular el valor del registro UBRR en modo de doble velocidad (U2Xn).
Envío y Recepción de Datos
uartX_getc(void): Obtiene un byte recibido del buffer circular de la UART especificada. Retorna el carácter recibido en el byte bajo y el estado del último error de recepción en el byte alto. RetornaUART_NO_DATAsi no hay datos.uartX_peek(void): Permite "echar un vistazo" al siguiente byte en el buffer de recepción sin eliminarlo. Útil para verificar el siguiente carácter sin consumirlo.uartX_putc(uint8_t data): Coloca un byte en el buffer circular de transmisión de la UART especificada para su envío.uartX_puts(const char *s): Envía una cadena de caracteres (desde RAM) a través de la UART. Bloquea si no puede escribir la cadena completa en el buffer.uartX_puts_p(const char *s): Envía una cadena de caracteres desde la memoria de programa (Flash) a través de la UART. Utiliza la macroPSTR().uartX_puts_P(__s): Macro conveniente para enviar cadenas constantes de programa (por ejemplo,uart0_puts_P("Hola Mundo");).
Gestión del Buffer
uartX_available(void): Retorna el número de bytes esperando en el buffer de recepción de la UART especificada.uartX_flush(void): Limpia (vacía) los bytes esperando en el buffer de recepción de la UART especificada.
Manejo de Errores en la Comunicación UART
La librería también proporciona mecanismos para identificar y manejar errores comunes en la comunicación UART, lo cual es vital para la robustez de cualquier sistema. Los códigos de error se retornan en el byte alto de las funciones uartX_getc() y uartX_peek():
UART_FRAME_ERROR (0x0800): Indica un error de trama, lo que significa que el carácter recibido no tenía los bits de inicio/parada correctos o hubo un problema de sincronización.UART_OVERRUN_ERROR (0x0400): Señala una condición de sobreescritura (overrun). Esto ocurre cuando un carácter ya presente en el registro UDR de la UART no fue leído por el manejador de interrupciones antes de que llegara el siguiente carácter, resultando en la pérdida de uno o más caracteres.UART_BUFFER_OVERFLOW (0x0200): Indica que el buffer circular de recepción se ha desbordado. Esto sucede si no se leen los datos del buffer de recepción lo suficientemente rápido, lo que también resulta en la pérdida de caracteres.UART_NO_DATA (0x0100): No es un error per se, sino una indicación de que no hay datos disponibles en el buffer de recepción en el momento de la llamada a la función.
Es fundamental que el código de la aplicación verifique estos códigos de error para implementar una lógica de reintento o de manejo de excepciones adecuada, garantizando así la fiabilidad de la comunicación serial.
Preguntas Frecuentes (FAQs)
A continuación, respondemos algunas preguntas comunes sobre el uso y las características de esta librería UART actualizada:
¿Cómo puedo configurar el tamaño de los buffers de recepción y transmisión?
El tamaño de los buffers se define mediante las macros UART_RXn_BUFFER_SIZE y UART_TXn_BUFFER_SIZE (donde 'n' es el número de la UART, por ejemplo, UART_RX0_BUFFER_SIZE). Estos valores deben ser potencias de 2 (ej. 128, 256). Puedes definirlos en la configuración de tu compilador (por ejemplo, en las opciones de preprocesador) o directamente en el archivo uart.h.
¿Es compatible esta librería con todas las placas Arduino que usan microcontroladores AVR?
Sí, la librería es compatible con microcontroladores AVR utilizados en muchas placas Arduino, especialmente los modelos ATmega48P/88P/168P/328P. Las funciones available() y flush() se modelaron al estilo Arduino para facilitar su uso en ese entorno, aunque la librería es de bajo nivel y se puede usar en cualquier proyecto AVR C/C++.
¿Cómo puedo usar múltiples puertos UART (USART) en mi microcontrolador?
Si tu microcontrolador AVR tiene múltiples puertos UART (como el ATmega2560 con hasta 4 USARTs), la librería te permite usar cada uno de ellos de forma independiente. Simplemente asegúrate de que las macros USARTn_ENABLED para los puertos que deseas usar estén definidas, y luego utiliza las funciones específicas para cada puerto (ej. uart0_init(), uart1_init(), uart2_init(), uart3_init() y sus respectivas funciones getc, putc, etc.).
¿Qué significa un error de "Overrun" o "Buffer Overflow"?
Un error de Overrun ocurre a nivel de hardware cuando el registro de datos de la UART no se lee a tiempo antes de que llegue el siguiente byte. Un Buffer Overflow ocurre a nivel de software cuando el buffer circular de recepción se llena y nuevos datos no pueden ser almacenados, lo que significa que el programa no está procesando los datos lo suficientemente rápido. Ambos resultan en la pérdida de datos y sugieren que necesitas optimizar tu código o aumentar el tamaño del buffer.
¿Qué licencias rigen el uso de esta librería?
Esta librería está licenciada bajo la GPL (General Public License) versión 2 o posterior. Esto significa que eres libre de usarla, modificarla y distribuirla, pero cualquier trabajo derivado también debe licenciarse bajo la GPL.
Esta librería actualizada representa un paso adelante significativo para los desarrolladores de AVR, ofreciendo una solución de comunicación UART más robusta, compatible y fácil de usar. Al adoptar los estándares modernos de AVR-Libc y al integrar funcionalidades inspiradas en Arduino, esta herramienta facilita el desarrollo de proyectos complejos y optimiza el rendimiento de la comunicación serial. ¡Feliz programación con tus microcontroladores!
Si quieres conocer otros artículos parecidos a Librería UART Actualizada para Microcontroladores AVR puedes visitar la categoría Librerías.
