Electrónica y programación para Microcontroladores.

Libros técnicos para electrónica programable.

Email
Contactanos en:

consultas@firtec.com.ar

Arduino

Todo programador de Arduino alguna vez ha usado la clásica función Serial.available() para saber cuando hay datos en el puerto serial, esta función junto con Serial.begin() y Serial.print() nos dan las herramientas necesarias para establecer comunicación por ejemplo con un computador y ver los datos en el propio terminal de Arduino.
Un ejemplo sería enviar un carácter y pedirle a Arduino que lo devuelva como un eco.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*******************************************************************
 * Descripción: Recibe datos por la UART y los envía como un eco.               
 * Placa Arduino: UNO
 * Arduino IDE: 1.8.10
 * www.firtec.com.ar
*******************************************************************/
void setup() {
    Serial.begin(9600); 	 // Configura la UART
 }
void loop() { 
  if (Serial.available()){		    // Hay datos disponibles?
     char RX_Byte = Serial.read();	// Leer el carácter recibido
     Serial.print(RX_Byte); 		// Enviar nuevamente el caracter
   }
  }

El programa anterior es un programa que sigue los lineamientos clásicos de un programa Arduino y funciona correctamente.
Está claro que la filosofía de Arduino es simplificar las cosas, no enredar al programador con la compleja electrónica del microcontrolador que tiene la propia placa, para poder hacer esto se necesita generar una capa de abstracción de hardware (HAL). Esta capa resuelve los enredos electrónicos como accesos a memoria, registros, banderas, etc, solo basta con escribir Serial.begin(9600) para realizar la configuración de la UART, esto implica actuar sobre varias posiciones de memoria interna (registros) para establecer los parámetros de funcionamiento de la UART de acuerdo a pautas definidas en la hoja de datos del controlador.
Sin embargo la capa HAL condiciona varios aspectos operativos del microcontrolador, es el precio que se paga por la simpleza en la programación. Algo sí como tener un automóvil con seis marchas pero usar siempre solamente cuatro.
Pongamos como ejemplo el programa anterior, este programa desde el punto de vista funcional cumple perfectamente, hace lo que se espera que haga pero desde la eficiencia tiene algunos puntos objetables, el primero es que para recibir nuevos caracteres el programa debe ejecutar siempre el bucle loop() de lo contrario Serial.available() no es llamado y por lo tanto no sabremos si hay nuevos datos.
Imagine el lector que podríamos estar recibiendo datos una vez al día lo que significa que Arduino estaría trabajando todo el tiempo consumiendo energía sin hacer nada!!!
Podríamos incluso usar la función SerialEvent() pero siempre necesitaremos la ejecución del loop() para mantener los datos actualizados.

Para mejorar esto podemos prescindir de HAL y “conversar” directamente con el microcontrolador, el siguiente programa hace exactamente lo mismo que el anterior pero mientras no se reciben nuevos datos el microcontrolador es puesto en un estado de bajo consumo (Sleep) y se activa en el momento en que hay que hacer algo.
Arduino por si mismo no tiene ninguna función para poner a dormir (Sleep) el procesador si las bibliotecas de AVR para poder usar estas funciones solo debemos incluir el archivo de cabecera correspondiente avr/sleep.h.
El programa también hace uso de la interrupción de recepción de la UART, de hecho Serial.available() también usa esta interrupción pero HAL obliga a que esta función se ejecutada repetidamente dentro del bucle loop(). En este ejemplo la interrupción funciona como lo hace de manera nativa en el hardware del ATMEGA328P.

Este poderoso microcontrolador tiene interrupciones vectorizadas es decir que cuando la interrupción se active el Contador de Programa (PC) saltará a una dirección de programa definida por el rótulo USART_RX_vect para el ATMEGA328 según lo informa su hoja de datos.
El siguiente es el programa completo sin la capa HAL de Arduino.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*******************************************************************
 * Descripción: Prueba del puerto serial de la placa Arduino.
 *              Recibe dato mediante interrupciones, cuando no
 *              recibe datos pasa a bajo consumo.
 *                           
 * Placa Arduino: UNO
 * Arduino IDE: 1.8.10
 *
 * www.firtec.com.ar
*******************************************************************/
#include <avr/sleep.h>
#define F_CPU 16000000
#define USART_BAUDRATE 9600
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
 
void setup(){
   UCSR0A = 0x00; 	// Limpia las banderas de la UART
   UBRR0H = (uint8_t)(UBRR_VALUE >> 8); // Configura baudios
   UBRR0L = (uint8_t)UBRR_VALUE;
   UCSR0C |= (1 << UCSZ00) | (1 << USBS0); // Datos en 8-bits con 1 bit de stop
   UCSR0B |= (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);   // Activa TX,RX y INT     
   interrupts();		// Las interrupciones están activadas
}
void loop() { 
       sleep_enable();
       set_sleep_mode(SLEEP_MODE_IDLE);  
       sleep_cpu();	// Pasa a bajo consumo
}
 
ISR(USART_RX_vect){  
  char RX_Byte = UDR0; 	// Guarda el carácter recibido
  UDR0 = RX_Byte;		// Envía el carácter nuevamente
 }

 Observe que la función ISR(USART_RX_vect) se llama de forma automática cuando se recibe un nuevo carácter, otro punto no menor es el uso de recursos que cada uno de los programas toma del microcontrolador, observe las siguientes capturas de pantalla.

En el segundo programa el microcontrolador es puesto en modo Sleep mientras no hay tráfico de datos, es verdad que hay varias bibliotecas para que al procesador pase a modo Sleep sin embargo ninguna de ellas tiene un funcionamiento tan eficiente como la diseñada por el propio fabricante del chip.
En rigor de verdad hay varios "Modos" de colocar el controlador en modo Sleep según lo indica la biblioteca de AVR siendo el modo mas drástico SLEEP_MODE_PWR_DOWN en donde el consume baja mas de un 75% respecto del consumo normal, sin embargo es modo no se puede usar con la UART puesto que todos los relojes internos son detenidos y el puerto de comunicaciones deja funcionar.

Conclusión.

Esta claro que programar Arduino sin la capa HAL es  una tarea bastante engorrosa y mucho mas "electrónica", sin embargo es evidente la clara ventaja que tiene en cuanto a romper las condiciones que impone la propia estructura de Arduino.
De repente puede ser interesante mezclar tanto la "programación" mas electrónica para los bloques de programa que el propio programador escribe para resolver un problema determinado y la capa HAL para el uso de las tantas bibliotecas que existen para distintos sensores y periféricos vinculados a Arduino.
En definitiva el objetivo de Arduino fue siempre acercar la electrónica a todo el mundo, hacer lo complejo sencillo y si bien su idea original se aparta un poco del desarrollo profesional, nada impide que logremos hacer con Arduino un verdadero proyecto de ingeniería puesto que en definitiva a bordo de cada placa hay un poderoso microcontrolador.