Multiproceso en Arduino

ATMega328.  Micro usado en Arduino UNO. Imagen: www.openhacks.com

ATMega328. Micro usado en Arduino UNO.
Imagen: www.openhacks.com

Arduino es un ordenador.  Tiene un hardware, parte física, que le permite interactuar con el entorno.  Detectar pulsaciones de botones, encender luces, medir temperatura, etc.
Dispone de un microprocesador con instrucciones definidas, software, capaz de cambiar el comportamiento del hardware en función de las órdenes que tenga programadas.  Y el comportamiento cambiará modificando las órdenes, sin necesidad de alterar la parte física.
Arduino pertenece a ese montón de ordenadores que pasan desapercibidos a nuestros ojos.  Arduino, sin embargo, dista mucho de tener la capacidad de proceso que tienen los portátiles o los de sobremesa que estamos acostumbrados a manejar; con pantalla, teclado y ratón.
Aunque se pueden programar en C/C++, como los grandes, el código de instrucciones es muy reducido con respecto a los micros que tienen lo que todos reconocemos como ordenador.  También lo es la capacidad de almacenamiento, la cantidad de memoria que puede manejar y las características de multiproceso.
El multiproceso es la capacidad que tiene un microprocesador de atender diferentes tareas a la vez; ejecutando una y apartando la que tiene menos prioridad, compensando los tiempos que dedica a cada proceso, para que el usuario tenga la sensación de que el equipo es capaz de hacer varias cosas a la vez.
Un ejemplo no muy técnico podemos observarlo cuando estamos trabajando con una hoja de cálculo y subiendo fotos a Internet.  La sensación que tenemos es que el ordenador está atendiendo nuestras órdenes en la hoja de cálculo en una de las ventanas, mientras que las imágenes no paran de procesarse.  Lo que ocurre realmente es que nosotros somos más lentos que el ordenador, que aprovecha para copiar un trocito de foto mientras nosotros estamos buscando el siguiente número que teclear en la hoja de cálculo.  Esta virtud la tienen los microprocesadores potentes.
El ciclo del programa en Arduino consta de una función, “setup()”,  que se ejecuta cuando Arduino comienza a trabajar, y otra función que se repite infinitamente, “loop()”.  Al contrario que en los PC, este es el único programa que se ejecuta.

Folioscopio, inventado en 1895 por Herman Castler.  Simula el movimiento al pasar rápidamente las hojas con imágenes consecutivas impresas. Imagen: http://las1000nochesyuna.wordpress.com

Folioscopio, inventado en 1895 por Herman Castler. Simula el movimiento al pasar rápidamente las hojas con imágenes consecutivas impresas.
Imagen: http://las1000nochesyuna.wordpress.com

Pero aunque solo trabaje con un programa deseamos que esté atendiendo diferentes circunstancias.  Por ejemplo, si ponemos a Arduino a controlar la temperatura de una habitación es necesario  que esté pendiente de si hay alguien dentro, la temperatura actual, los cambios de temperatura que el usuario quiera hacer mientras la ocupe y la temperatura a la que deba quedar cuando la habitación esté vacía.
Entonces en nuestro bucle “loop()” primero miraremos si hay alguien en la habitación, solicitando la información a un control de presencia.  Guardaremos esa información y lo dejaremos desatendido.  Luego consultaremos la temperatura, guardaremos la información y también dejaremos desatendido el termómetro.  Y así con todos los sensores involucrados.  Luego con la información que hemos recogido y guardado en memoria el programa tomará la decisión de subir, bajar o mantener la temperatura.
Si mientras consulta la temperatura el sensor de presencia detecta a alguien, Arduino no lo sabrá.  Su forma de engañarnos es ser lo suficientemente rápido en sus consultas a los dispositivos como para que a esa persona no le dé tiempo a salir antes de que el ciclo del programa haya vuelto a pasar por el punto en el que consulta la presencia.

Es como los fotogramas de una película.  Nos engaña haciéndonos creer que hay alguien en movimiento, pero son fotografías estáticas pasadas lo suficientemente rápidas como confundirnos.
Por eso el tiempo que a Arduino le lleva ejecutar una vuelta de “loop()” es importante.

//  *************************************************************************************
// *                                                                                     *
// * V E L O C I D A D                                                                   *
// *                                                                                     *
// *     Prueba de velocidad con Arduino                                                 *
// *                                                (C) Vicente Simón Rando; 11.10.2013  *
// *                                                                                     *
//  *************************************************************************************

// Variables globales
int  miliINI;
int  miliFIN;
unsigned long  contador;
bool final;
#define MILISEGUNDOS  10000

// Setup
void setup( )
{
  contador = 0;
  final = false;
  Serial.begin(115200);
  miliINI = millis();
  miliFIN = miliINI+MILISEGUNDOS;  
}

// Loop
void loop( )
{
  if( millis() )
    contador++;
  else
  {
    if( !final )
    {
      Serial.print( "Inicio: " ); Serial.println( miliINI );
      Serial.print( "Fin: " ); Serial.println( miliFIN );
      Serial.print( "Contador: " ); Serial.println( contador );

      final = true;
    }
  }
}

Yo he usado el programa anterior.
Toma el valor de milisegundos al principio.  Cada vuelta la anota en una variable.  Y cuando han pasado un número determinado de milisegundos muestra la información.
Como soy un maniático de la precisión he repetido el mismo programa con diferentes valores de MILISEGUNDOS, y estos son los resultados.

Milisegundos Vueltas Vueltas en un milisegundo
20000 3313494 165,6747
10000 1656743 165,6743
7000 1159683 165,669
5000 828367 165,6734
3000 497051 165,683667
1000 165734 165,734

Los valores de milisegundos=20000 son más precisos que el resto.  En un milisegundo, la función loop() de este programa de testeo se ha ejecutado casi 166 veces.  Es muy difícil que una persona sea capaz de cruzar una habiación en 0,006 milisegundos.  Por lo que Arduino nos dará la sensación de que está pendiente a todo lo que pasa a su alrededor.
Con este sketch de test  Arduino UNO arroja como resultados una frecuencia de poco menos de 166 veces y un periodo de aproximadamente 0,006 milisegundos.
Esta velocidad solo la consigue si no tiene nada más que hacer.  Cuando loop() tiene más órdenes que cumplir el proceso se comenzará a alargar, y en un milisegundo realizará menos tareas.  Si alguna de estas tareas es bloqueante, incluso puede estar varios segundos sin dar ninguna vuelta.
Es importante tener claro que los resultados los consigue con este programa.  Si hacemos una pequeña modificación en la que en cada vuelta comprueba si debe o no mostrar el resultado los valores son:

Milisegundos Vueltas Vueltas en un milisegundo
20000 2840138 142,0069

Frecuencia de 142 veces y periodo de 0,007 milisegundos.

//  *************************************************************************************
// *                                                                                     *
// * V E L O C I D A D                                                                   *
// *                                                                                     *
// *     Prueba de velocidad con Arduino                                                 *
// *                                                (C) Vicente Simón Rando; 11.10.2013  *
// *                                                                                     *
//  *************************************************************************************

// Variables globales
int  miliINI;
int  miliFIN;
unsigned long  contador;
int final;
#define MILISEGUNDOS  20000

// Setup
void setup( )
{
  contador = 0;
  final = 0;
  Serial.begin(115200);
  miliINI = millis();
  miliFIN = miliINI+MILISEGUNDOS;  
}

// Loop
void loop( )
{
  if( final==0 )
  {
    if( millis()<miliFIN)
      contador++;
    else
      final=1;
  };

  if( final==1 )
  {
    Serial.print( "Inicio: " ); Serial.println( miliINI );
    Serial.print( "Fin: " ); Serial.println( miliFIN );
    Serial.print( "Contador: " ); Serial.println( contador );

    final = 2;
  }
}
El código es libre , aunque deberá indicar la procedencia. No me responsabilizo ni del fin buscado ni de los datos obtenidos.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *