domingo, 6 de noviembre de 2022

El display III. La instrucción "millis()" (IDE Arduino)

En la función que creamos en la entrada anterior para controlar el display ("display7Seg") sus dígitos se van encendiendo y apagando sucesivamente, como eso sucede a gran velocidad (se coloca dentro del bucle loop que se repite permanentemente) nuestros ojos ven encendidos los cuatro a la vez. Si el programa se detiene en algún momento, por ejemplo, porque usamos la instrucción "delay", sólo se quedará encendido uno de los dígitos. Eso es lo que va a suceder en el programa que vamos a hacer a continuación, se trata de que el display muestre los segundos que van transcurriendo desde que se inicia.

/*
 * displayCuentaSegundosDelay.ino
 * Queremos que el display vaya contando los segundos que van
 * transcurriendo desde que se inicia el programa.
 * Esta versión no funcionará bien, ya que al usar la
 * instrucción "delay(1000)" el programa se detiene un segundo
 * y durante ese tiempo sólo permanece encendido el último
 * digito de la función "display7Seg".
 * En una versión posterior veremos como resolverlo.
 */

byte codigo7seg[] = {B00000011, B10011111, B00100101, B00001101, B10011001, 
                     B01001001, B01000001, B00011111, B00000001, B00001001};
int segundos = 0;

void setup() {
  pinMode(4,OUTPUT);                     //pines display
  pinMode(7,OUTPUT);
  pinMode(8,OUTPUT);  
}

void loop() {
  delay(1000);            //el programa se detiene 1 seg.
  segundos++;
  display7Seg(segundos);  //el último digito permanece encendido 1 seg.
}

void display7Seg(int numero){
  int millar = numero/1000;                                   
  int centena = (numero - millar*1000)/100;              
  int decena = (numero - millar*1000 - centena*100)/10;  
  int unidad = numero - millar*1000 - centena*100 - decena*10;
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[unidad]);
  shiftOut(8, 7, LSBFIRST, B00010000);
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[decena]);
  shiftOut(8, 7, LSBFIRST, B00100000);
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[centena]);
  shiftOut(8, 7, LSBFIRST, B01000000);
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[millar]);
  shiftOut(8, 7, LSBFIRST, B10000000);
  digitalWrite(4,HIGH);
}

Para evitar que esto se produzca  vamos a usar una nueva instrucción del IDE Arduino, "millis()". Se trata de un contador que se va incrementando cada milisegundo desde que se pone en marcha el programa. Para hacer una temporización tenemos que almacenar en una variable los milisegundos correspondientes al inicio de la misma, después vamos preguntando si los milisegundos transcurridos son superiores a los que hemos guardado mas el tiempo que queramos que dure la temporización.

/*
 * displayCuentaSegundosMillis.ino
 * Queremos que el display vaya contando los segundos que van
 * transcurriendo desde que se inicia el programa.
 * En este caso, hemos usado la instrucción "millis", que nos
 * da los milisegundos que van pasando desde que se inicia el
 * programa. Con esta instrucción podemos hacer temporizaciones
 * sin que el programa se detenga.
 */

byte codigo7seg[] = {B00000011, B10011111, B00100101, B00001101, B10011001, 
                     B01001001, B01000001, B00011111, B00000001, B00001001};

int segundos = 0;
unsigned long tiempoInicial;       //aquí guardaremos los miliseg que lleva
                                   //contados la instrución millis cuando
                                   //vayamos a iniciar una temporización.

void setup() {
  pinMode(4,OUTPUT);               //pines display
  pinMode(7,OUTPUT);
  pinMode(8,OUTPUT);
  tiempoInicial = millis();        //guardamos los miliseg actuales,
                                   //millis seguirá incrementándose 
}

void loop() {
  if(millis() > tiempoInicial + 1000) {    //Si los millis actuales superan
                                           //a los guardados + 1000, es que
                                           //ha pasado 1 segundo.
    tiempoInicial = millis();              //Guardamos de nuevo el valor
                                           //actual de millis.
    segundos++;                            //Incrementamos segundos.
  }
  display7Seg(segundos);
}


void display7Seg(int numero){
  int millar = numero/1000;                               
  int centena = (numero - millar*1000)/100;     
  int decena = (numero - millar*1000 - centena*100)/10;
  int unidad = numero - millar*1000 - centena*100 - decena*10;
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[unidad]);
  shiftOut(8, 7, LSBFIRST, B00010000);
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[decena]);
  shiftOut(8, 7, LSBFIRST, B00100000);
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[centena]);
  shiftOut(8, 7, LSBFIRST, B01000000);
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[millar]);
  shiftOut(8, 7, LSBFIRST, B10000000);
  digitalWrite(4,HIGH);
}

Otra opción es crear una nueva función para hacer temporizaciones, pero que no detenga totalmente el programa, sino que permita atender aquellos procesos que lo necesiten. Esta función va a utilizar una nueva instrucción condicional que ejecuta en bucle las instrucciones que le indiquemos mientras que la condición se cumpla, se trata de while (mientras que..). A continuación se muestra el programa con la nueva función, que hemos llamado delayMillis, ya que también usamos en ella la instrucción millis().

/*

 * displayCuentaSegundosDelayMillis.ino
 * Queremos que el display vaya contando los segundos que van
 * transcurriendo desde que se inicia el programa.
 * En este caso, hemos creado la función delayMillis, que hará
 * el retardo de 1 segundo pero manteniendo el refresco del 
 * display. Con esta función podemos hacer temporizaciones sin
 * desatender aquellos procesos que lo necesiten.
 * En la función utilizamos la instrucción while (mientras que):
 *     while (condición) {    //mientras que se cumpla la condición se
 *       instrucciones        //permanece ejecutando estas instrucciones, 
 *     }                      //cuando deje de cumplirse se sale del bucle
 */

byte codigo7seg[] = {B00000011, B10011111, B00100101, B00001101, B10011001, 
                     B01001001, B01000001, B00011111, B00000001, B00001001};

int segundos = 0;

void setup() {
  pinMode(4,OUTPUT);               //pines display
  pinMode(7,OUTPUT);
  pinMode(8,OUTPUT);
}

void loop() {         
  delayMillis(1000);                 //la nueva función
  segundos++;
  display7Seg(segundos);
}

void delayMillis(unsigned long tiempo){
  unsigned long tiempoFinal = millis() + tiempo;
  while (millis() < tiempoFinal){     //El programa se queda en este bucle 
    display7Seg(segundos);            //hasta que pase el tiempo fijado,
  }                                   //pero atiende al display.
}


void display7Seg(int numero){
  int millar = numero/1000;                               
  int centena = (numero - millar*1000)/100;     
  int decena = (numero - millar*1000 - centena*100)/10;
  int unidad = numero - millar*1000 - centena*100 - decena*10;
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[unidad]);
  shiftOut(8, 7, LSBFIRST, B00010000);
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[decena]);
  shiftOut(8, 7, LSBFIRST, B00100000);
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[centena]);
  shiftOut(8, 7, LSBFIRST, B01000000);
  digitalWrite(4,HIGH);
  digitalWrite(4,LOW);
  shiftOut(8, 7, LSBFIRST, codigo7seg[millar]);
  shiftOut(8, 7, LSBFIRST, B10000000);
  digitalWrite(4,HIGH);
}

Ejercicios propuestos.

1.- Hacer un programa que haga aparecer en el display el número 1234 durante 2 segundos y el número 5678 durante otros dos, el ciclo se repite permanentemente.

2.- Ahora queremos programar un reloj básico de horas y minutos. Crear dos variables, horas y minutos, y utilizar su valor inicial para la puesta en hora. Para visualizarlas juntas podemos usar la expresión 100*horas+minutos.