lunes, 23 de diciembre de 2019

Temporizaciones sin delay

Vamos a considerar un programa que haga parpadear el último LED de la segunda fila y, a la vez, nos permita encender y apagar el primer LED de dicha fila con el pulsador A. En principio podríamos pensar en el siguiente código:
/* 
 * pulsadorAParpadeoLedConDelay.ino
 * Encendido-Apagado del primer LED de la segunda fila
 * con el pulsador A. Simultáneamente, parpadeo del
 * último LED de la segunda fila.
*/

boolean pulsadorA;
/*
 * Vamos a realizar el parpadeo de manera distinta,
 * utilizaremos una variable "estadoLed" para
 * guardar la situación del LED.
 */
boolean estadoLed; 

void setup() {
  pinMode(28,OUTPUT);    //Programamos los pines
  pinMode(23,OUTPUT);    //como salidas.
  pinMode(7,OUTPUT);
  pinMode(5, INPUT);     //pin pulsador como entrada
  digitalWrite(28,HIGH); //Activamos la fila.
  digitalWrite(23,HIGH); //apagamos LED 
  digitalWrite(7,HIGH);  //apagamos LED
}

void loop() {
  /* Parpadeo LED
   * estadoLed=!estadoLed
   * estadoLed toma el valor contrario al que tenía
   * (0 o 1, LOW o HIGH) en cada ciclo del bucle loop().
   */
  estadoLed=!estadoLed;
  digitalWrite(7, estadoLed);
  delay(2000);               //el programa se detiene 2 seg.
  /* 
   * Ahora gestionamos el pulsadorA y el otro LED.
   * La parada anterior provocará que no se detecten
   * correctamente las pulsaciones.
   */
  pulsadorA=digitalRead(5);
  if (pulsadorA == 0) {
    digitalWrite(23,LOW);
  }
  if (pulsadorA == 1) {
    digitalWrite(23,HIGH);
  }
}

Para evitar este problema vamos a recurrir a la instrucción "millis()", la cual nos proporciona el número de milisegundos transcurridos desde que se inicia la ejecución del programa. 
/* 
 * pulsadorAParpadeoLedSinDelay.ino
 * Encendido-Apagado del primer LED de la segunda fila
 * con el pulsador A. Simultáneamente, parpadeo del
 * último LED de la segunda fila.
*/

boolean pulsadorA;
/* 
 * millisIniciales
 * Esta variable servirá para guardar los milisegundos
 * que han pasado en el momento que iniciemos una 
 * temporizacion. Es de tipo long (4 bytes de tamaño).
 */
long millisIniciales=0;
boolean estadoLed;

void setup() {
  pinMode(28,OUTPUT);    //Programamos los pines
  pinMode(23,OUTPUT);    //como salidas.
  pinMode(7,OUTPUT);
  pinMode(5, INPUT);     //pin pulsador como entrada
  digitalWrite(28,HIGH); //Activamos la fila.
  digitalWrite(23,HIGH); //apagamos LED 
  digitalWrite(7,HIGH);  //apagamos LED
}

void loop() {
  /*
   * Si los milisegundos transcurridos menos los
   * milisegundos que guardamos al iniciar la temporización
   * son mayores de 2000, significa que ya ha
   * transcurrido ese tiempo y tenemos que cambiar
   * el estado del LED y volver a iniciar una nueva
   * temporización.
   * El programa no se detiene, sino que va realizando
   * esta pregunta en cada pasada del bucle.
   */
  if(millis() - millisIniciales > 2000){
    estadoLed=!estadoLed;
    digitalWrite(7, estadoLed);
    millisIniciales = millis();   //iniciamos nueva temporización
  }
  pulsadorA=digitalRead(5);
  if (pulsadorA == 0) {
    digitalWrite(23,LOW);
  }
  if (pulsadorA == 1) {
    digitalWrite(23,HIGH);
  }
}


Otra manera de hacerlo es creando una nueva función que podríamos llamar "delay_" que utilizaría la instrucción millis() y generaría un nuevo bucle que estaría ejecutándose todo el tiempo de dure la temporización. En él pondríamos las instrucciones relativas a los pulsadores:
/* 
 * pulsadorAParpadeoLedDelay_.ino
 * Encendido-Apagado del primer LED de la segunda fila
 * con el pulsador A. Simultáneamente, parpadeo del
 * último LED de la segunda fila.
*/

boolean pulsadorA;

void setup() {
  pinMode(28,OUTPUT);    //Programamos los pines
  pinMode(23,OUTPUT);    //como salidas.
  pinMode(7,OUTPUT);
  pinMode(5, INPUT);     //pin pulsador como entrada
  digitalWrite(28,HIGH); //Activamos la fila.
  digitalWrite(23,HIGH); //apagamos LED 
  digitalWrite(7,HIGH);  //apagamos LED
}

void loop() {
  digitalWrite(7, LOW);
  delay_(2000);
  digitalWrite(7, HIGH);
  delay_(2000);
}

/* 
 * delay_
 * Creamos una función que realiza la temporización utilizando
 * la instrucción millis() y genera un nuevo bucle que se sigue
 * ejecutando durante el tiempo que dure el retardo.
 * La variable millisIniciales está declarada como local dentro 
 * de la función.
 * Para hacer ese bucle hemos utilizado la instrucción:
 *     while(condición){  instrucciones }
 * mientras que se cumpla la condición se ejecutan las instruciones
 * que hay entre las llaves.
 * En lugar de incluir entre las llaves todas las instrucciones
 * de los pulsadores las hemos colocado en una nueva función 
 * loop_
 * mientras que (millis - millisIniciales < tiempo) 
 * ejecutamos las instrucciones colocadas en loop_
 */
void delay_(long tiempo){
  long millisIniciales = millis();
  while(millis() - millisIniciales < tiempo){
    loop_();
}
}

void loop_(){
  pulsadorA=digitalRead(5);
  if (pulsadorA == 0) {
    digitalWrite(23,LOW);
  }
  if (pulsadorA == 1) {
    digitalWrite(23,HIGH);
  }  
}










lunes, 16 de diciembre de 2019

Los pulsadores (II)

En esta entrada vamos a realizar dos programas cuya lógica es más complicada de lo que en un principio podría parecer. En el primero de ellos se trata de encender y apagar un LED con un único pulsador, de manera que, si está apagado, al pulsar se encienda y, si está encendido, al pulsar se apague.

/* 
 * encendidoApagadoPulsandoA.ino
 * Encendido-Apagado del primer led de la segunda fila
 * con el pulsador A. Si está apagado, al pulsar se
 * enciende; si está encendido, al pulsar se apaga.
*/

boolean pulsadorA;
/*
 * hasLevantado: variable que nos indicará si hemos
 * quitado el dedo del pulsador, para evitar que mientras
 * mantenemos presionado el programa lo detecte como 
 * nuevas pulsaciones.
 */
boolean hasLevantado=true;
/*
 * estaApagado: para guardar el estado del LED, encendido
 * (false) o apagado (true).
 */
boolean estaApagado=true; //

void setup() {
  pinMode(28,OUTPUT);    //Programamos los pines
  pinMode(23,OUTPUT);    //como salidas.
  pinMode(5, INPUT);     //pin pulsador como entrada
  digitalWrite(28,HIGH); //Activamos la fila.
  digitalWrite(23,HIGH); //apagamos LED 
}

void loop() {
  pulsadorA=digitalRead(5);
  /*
   * si pulsado y has levantado previamente y está 
   * apagado, entonces lo enciendo.
   */
  if (pulsadorA==0 && hasLevantado && estaApagado) {
    digitalWrite(23,LOW);
    hasLevantado=false;   //ahora lo tenemos presionado.
    estaApagado=false;    //el LED está encendido.
  }
  /*
   * si pulsado y has levantado previamente y no (!) está
   * apagado, entonces lo apago.
   */
  if (pulsadorA==0 && hasLevantado && !estaApagado) {
    digitalWrite(23,HIGH);
    hasLevantado=false;
    estaApagado=true;     //ahora el LED está apagado
  }
  /*
   * si pulsador levantado hago hasLevantado cierto (true)
   */
  if (pulsadorA==1){
    hasLevantado=true;
  }
  delay(30);             //evita los rebotes del pulsador
}
En el segundo programa se trata de encender uno a uno los 5 LEDs de la segunda fila conforme vamos accionando el pulsador A.

/*
 *encendidoProgresivoFila2Pulsador.ino
 *Los led de la fila 2 se van encendiendo
 *de uno en uno cada vez que accionamos
 *el pulsador A.
*/

boolean pulsadorA;
boolean hasLevantado;
/*
 * n: variable de tipo byte (número entero
 * entre 0 y 255), para contar las pulsaciones
 * realizadas
 */
byte n=0;

void setup() {
  pinMode(28,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(23,OUTPUT);
  pinMode(24,OUTPUT);
  pinMode(25,OUTPUT);
  pinMode(5,INPUT);
  digitalWrite(28,HIGH); //activamos la fila.
  digitalWrite(25,HIGH); //desactivamos las columnas
  digitalWrite(24,HIGH); //para apagar los LEDs
  digitalWrite(23,HIGH);
  digitalWrite(9,HIGH);
  digitalWrite(7,HIGH);
}

void loop() {
  pulsadorA=digitalRead(5);
  /*
   * si pulsador A accionado y lo has levantado
   * previamente, entonces incrementamos n
   */
  if(pulsadorA==0 && hasLevantado){               
    n++;        //incrementamos n (también n=n+1;)
    if(n==6){
      n=0;      //para volver a iniciar el ciclo
    }  
    hasLevantado=false; 
  }
  /*
   * si levantamos el pulsador 
   * le damos el valor true a la variable.
   */
  if(pulsadorA==1){
    hasLevantado=true;
  }
  /*
   * Ahora vamos encendiendo los LEDs según
   * el valor de n.
   */
  if(n==0){
    digitalWrite(23,HIGH); //apagamos todos los LEDs 
    digitalWrite(24,HIGH);
    digitalWrite(25,HIGH);
    digitalWrite(9,HIGH);
    digitalWrite(7,HIGH);
  }
  if(n==1){
    digitalWrite(23,LOW);  //encendemos el primer LED
  }
  if(n==2){
    digitalWrite(24,LOW);  //encendemos el segundo LED
  }
  if(n==3){
    digitalWrite(25,LOW);  //encendemos el tercer LED
  }
  if(n==4){
    digitalWrite(9,LOW);   //encendemos el cuarto LED
  }
  if(n==5){
    digitalWrite(7,LOW);   //encendemos el quinto LED
  }
  delay(30);               //evita los rebotes del pulsador
}
Vamos a tratar de simplificar la escritura del programa anterior. Para ello, en primer lugar, vamos a utilizar un "array" (matriz); se trata de un conjunto de variables agrupadas bajo un mismo nombre y podemos seleccionar mediante un índice. Crearemos un array con los números de los pines de las columnas internas de los LEDs y luego lo usaremos para simplificar la parte final del programa:
/*
 *encendidoProgresivoFila2Pulsador.ino
 *Los led de la fila 2 se van encendiendo
 *de uno en uno cada vez que accionamos
 *el pulsador A.
 */

boolean pulsadorA;
boolean hasLevantado;
byte n=0;
byte pinesCol[5]={23,24,25,9,7};
/*
 *Hemos creado un array de 5 elementos de tipo byte.
 *A cada uno de ellos podemos acceder mediante un índice:
 *pinesCol[0] sería 23, pinesCol[1] sería 24,... pinesCol[4] sería 7.
 */

void setup() {
  pinMode(28,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(23,OUTPUT);
  pinMode(24,OUTPUT);
  pinMode(25,OUTPUT);
  pinMode(5,INPUT);
  digitalWrite(28,HIGH); //activamos la fila.
  digitalWrite(25,HIGH); //desactivamos las columnas
  digitalWrite(24,HIGH); //para apagar los LEDs
  digitalWrite(23,HIGH);
  digitalWrite(9,HIGH);
  digitalWrite(7,HIGH);
}

void loop() {
  pulsadorA=digitalRead(5);
  
  if(pulsadorA==0 && hasLevantado){               
    n++;
    if(n==6) n=0; 
    hasLevantado=false; 
  }
  
  if(pulsadorA==1){
    hasLevantado=true;
  }
  
  if(n==0){
    digitalWrite(23,HIGH); //apagamos todos los LEDs 
    digitalWrite(24,HIGH);
    digitalWrite(25,HIGH);
    digitalWrite(9,HIGH);
    digitalWrite(7,HIGH);
  }
/*
 *Ahora utilizamos pinesCol para ir encendiendo los LEDs
 *dependiendo del valor que tenga n. 
 *Por ejemplo: si n=1 digitalWrite (pinesCol[n-1], LOW)
 *                    digitalWrite (pinesCol[0], LOW)
 *                    digitalWrite (23, LOW)
 *El íncide del array empieza en 0, por eso restamos 1 a n. 
 */
 if(n>0){
    digitalWrite(pinesCol[n-1],LOW);
   }
   delay(30);               //evita los rebotes del pulsador
}
La segunda simplificación consiste en utilizar el buble "for". Con él podemos repetir un determinado número de veces las instrucciones que deseemos:
/*
 *encendidoProgresivoFila2Pulsador.ino
 *Los led de la fila 2 se van encendiendo
 *de uno en uno cada vez que accionamos
 *el pulsador A.
*/

boolean pulsadorA;
boolean hasLevantado;
byte n=0;
byte pinesCol[5]={23,24,25,9,7};

void setup() {
  pinMode(28,OUTPUT);
  /*
   * Vamos a repetir 5 veces la instrucción pinMode
   * for(declaración variable; condición; incremento)
   * for(byte i=0;             i<5;       i++)
   * Mientras se cumpla la condición se vuelven a ejecutar
   * las instrucciones encerradas entre las llaves, con
   * la variable i incrementada en cada ciclo.
   * pinMode(pinesCol[0], OUTPUT)....pinMode(pinesCol[4],OUTPUT)
   */
  for(byte i=0; i<5; i++){
    pinMode(pinesCol[i],OUTPUT);
  }
  pinMode(5,INPUT);
  digitalWrite(28,HIGH);
  for(byte i=0; i<5; i++){
    digitalWrite(pinesCol[i],HIGH);
  }
}

void loop() {
  pulsadorA=digitalRead(5);

  if(pulsadorA==0 && hasLevantado){               
    n++;
    if(n==6) n=0;  
    hasLevantado=false; 
  }
  
  if(pulsadorA==1){
    hasLevantado=true;
  }
  
  if(n==0){
    for(byte i=0; i<5; i++){
      digitalWrite(pinesCol[i],HIGH);
    }
  if(n>0){
    digitalWrite(pinesCol[n-1],LOW);
  }
  delay(30);
}









domingo, 8 de diciembre de 2019

Los pulsadores (I)

Micro:bit cuenta con dos pulsadores (A y B), conectados internamente a los pines 5 y 11. Al presionar un pulsador el voltaje existente en su pin es cercano a 0 voltios (0 o LOW), mientras que si está levantado el voltaje será del orden de 3 voltios (1 o HIGH). Para poder leer estos valores y conocer el estado del pulsador vamos a utilizar una nueva instrucción del IDE Arduino:

digitalRead(pin)
Lee el valor (HIGH o LOW) que tiene el pin y lo devuelve al programa. Generalmente, este valor se almacena en una variable.

Una variable es una pequeña porción de la memoria del procesador a la que le asignamos un nombre y un valor (este puede ser modificado posteriormente todas las veces que se desee). Generalmente, para crear una variable hay que declararla al inicio del programa, indicando el tipo de datos que vamos a guardar en ella, su nombre y, si se desea, su valor inicial.

También va a ser necesaria una instrucción que nos permita tomar decisiones según que se cumpla o no una determinada condición:

if(condición){
    instrucciones si cierta;
}
Si se cumple la condición se ejecutan las instrucciones que hay entre las llaves, si no se saltan y se continúa con la siguiente. Los operadores que podemos usar en la condición son los siguientes: ==, !=, <, >, <=, >=; es decir, igualdad, distinto, menor, mayor, menor o igual y mayor o igual.

En el siguiente programa vamos a controlar el primer LED de la segunda fila con el pulsador A:

/* 
 * EncendidoApagadoPulsadorA.ino
 * Encendido-Apagado del primer led de la segunda fila
 * (pines 28-3) con el pulsador A (pin 5). Al pulsar se enciende, 
 * al levantar se apaga.
*/

boolean pulsadorA;  //variable donde guardaremos el estado del pulsador.
                    //De tipo booleano, ya que sólo puede tomar
                    //dos valores (0 o 1).
                    
void setup() {
  pinMode(28, OUTPUT);
  pinMode(23, OUTPUT);
  pinMode(5, INPUT);      //pulsador A (pin 5) como entrada
  digitalWrite(28, HIGH); //activamos fila interna
  digitalWrite(23,HIGH);  //desactivamos columna (LED apagado)
}

void loop() {
  pulsadorA = digitalRead(5); //leemos el estado del pulsador.
  if (pulsadorA==0) {         //si pulsador A presionado
    digitalWrite(23, LOW);    //encendemos.
  }
  if (pulsadorA==1) {         //si pulsador A levantado
    digitalWrite(23, HIGH);   //apagamos.
  }
}

Ahora vamos a hacer que el diodo sólo se encienda si mantenemos presionados los dos pulsadores; apagándose cuando se levante uno cualquiera de ellos. Para conseguirlo podemos utilizar el operador && (y), que nos permite preguntar si se cumplen varias condiciones a la vez, y el operador || (o), con el que podemos saber si se cumple una cualquiera de las condiciones.

/*  
 * EncendidoDoblePulsadoresAB.ino
 * Encendido del primer led de la segunda fila (pines 28-23)
 * si los dos pulsadores están presionados. Apagado con uno
 * de ellos levantado.
 * pulsador A (pin 5) 
 * pulsador B (pin 11)
*/

boolean pulsadorA;  //Variables para guardar el estado de
boolean pulsadorB;  //los pulsadores.

void setup() {
  pinMode(28, OUTPUT);
  pinMode(23, OUTPUT);
  pinMode(5, INPUT);
  pinMode(11, INPUT); 
  digitalWrite(28, HIGH);  //activamos fila
  digitalWrite(23, HIGH);  //desactivamos columna (LED apagado)
}

void loop() {
  pulsadorA=digitalRead(5);
  pulsadorB=digitalRead(11);
  if (pulsadorA==0 && pulsadorB==0) {  //si los dos presionados
    digitalWrite(23, LOW);             //enciende el LED
  }
  if (pulsadorA==1 || pulsadorB==1) {  //si uno de ellos levantado
    digitalWrite(23, HIGH);            //apaga el LED
  }
}




martes, 3 de diciembre de 2019

Encendemos LEDs (multiplexado)

El programa que vamos a incluir en esta entrada sólo pretende ser una primera aproximación al multiplexado de la matriz de LEDs, para que podamos visualizar la imagen que deseemos. En este ejemplo, dibujaremos una X con los LEDs:


/* multiplexadoFacil.ino
 * Dibujamos una X en la matriz de LEDS
 * Estos son los pines a los que están conectados
 * cada uno de los LEDs, entre paréntesis los que
 * hay que encender:
 *  (26-3)  27-23  26-4   27-24 (26-10)
 *   28-23 (28-24) 28-25 (28-9)  28-7
 *   27-4   26-6  (27-10) 28-6   27-3
 *   26-7  (26-9)  26-25 (26-24) 26-23
 *  (28-10) 27-9   28-3   27-25 (28-4)
 */

void setup() {
 pinMode(26,OUTPUT); //Programamos los pines
 pinMode(27,OUTPUT); //como salidas.
 pinMode(28,OUTPUT);
 pinMode(3,OUTPUT);
 pinMode(4,OUTPUT);
 pinMode(10,OUTPUT);
 pinMode(9,OUTPUT);
 pinMode(24,OUTPUT);
}

void loop() {
  digitalWrite(26,HIGH); //activamos la fila 1 (interna)
  digitalWrite(3,LOW);   //encendemos los LEDs de esa fila
  digitalWrite(10,LOW);
  digitalWrite(9,LOW);
  digitalWrite(24,LOW); 
  delay(1);              //esperamos 1 milisegundo
  digitalWrite(26,LOW);  //desactivamos la fila 1
  digitalWrite(3,HIGH);  //y apagamos los LEDs
  digitalWrite(10,HIGH);
  digitalWrite(9,HIGH);
  digitalWrite(24,HIGH);
  
  digitalWrite(27,HIGH); //activamos la fila 2 (interna)
  digitalWrite(10,LOW);  //encendemos los LED de esa fila (sólo uno)
  delay(1);              //esperamos 1 milisegundo
  digitalWrite(27,LOW);  //desactivamos la fila 2
  digitalWrite(10,HIGH); //y apagamos el LED
  
  digitalWrite(28,HIGH); //activamos la fila 3 (interna)
  digitalWrite(4,LOW);   //encendemos los LEDs de esa fila
  digitalWrite(10,LOW);
  digitalWrite(9,LOW);
  digitalWrite(24,LOW);
  delay(1);              //esperamos 1 milisegundo
  digitalWrite(28,LOW);  //desactivamos la fila 3
  digitalWrite(4,HIGH);  //y apagamos los LEDs
  digitalWrite(10,HIGH);
  digitalWrite(9,HIGH);
  digitalWrite(24,HIGH);
}

Si vamos aumentando los tiempos de la instrucción delay podremos comprobar como se obtiene la X por el multiplexado de las tres filas internas de la matriz de LEDs.

Mas adelante volveremos sobre estas cuestiones.