Al hacer proyectos con microcontroladores, es necesario conocer las instrucciones que controlan el flujo o saltos de un programa. para esto, hay varias instrucciones condicionales que serán estudiadas en este articulo. la mayoría de automatismos y circuitos inteligentes modernos, hacen uso de estas instrucciones. de ahí la importancia de conocerlas y comprender como funcionan y como se pueden utilizar. Programadores como Bill Gates y muchos otros, recomiendan que toda persona debería aprender a programar procesadores y microcontroladores, pues esto desarrolla el sentido común, lógica e inteligencia.
Todas las instrucciones de un microcontrolador se pueden clasificar en 3 clases principales:
1 – Instrucciones de Salto Condicional.
2 – Instrucciones de memorias (movimientos de variables).
3 – Instrucciones Aritméticas y Lógicas.
En el presente artículo vamos a estudiar y poner en práctica las instrucciones de salto condicional. En la Figura 1 podemos observar la tarjeta Arduino Uno y en la Figura 2 la IDE o ambiente de desarrollo, la cual puede ser bajada de la página web de Arduino. Usaremos estas herramientas de hardware y de software para el desarrollo de los ejemplos presentados en este artículo.
INSTRUCCIONES DE SALTO CONDICIONAL.
Normalmente las instrucciones que hay dentro de un microcontrolador, son ejecutadas en forma secuencial, es decir, se ejecuta una después de otra y así sucesivamente. Mas en la mayoría de circuitos que usan microcontroladores, es necesario romper esta secuencia, para poder hacer inteligentes los proyectos.
La lógica de programación es muy similar al comportamiento humano ante la vida. Por ejemplo, si está lloviendo, es muy probable que la mayoría de las personas, cojan un paraguas, caso vayan a salir de casa. O si una persona tiene sed, buscar agua para beber. En los microcontroladores es muy similar la lógica.
Por ejemplo, censar la temperatura ambiente de un lugar. Si está muy caliente, encender un ventilador, pero si está muy frio, encender un calefactor. En este artículo, veremos cómo podemos emular o imitar el comportamiento humano en los microcontroladores. Para esto, es necesario conocer las instrucciones de control de flujo o instrucciones de salto condicional que se pueden ejecutar en un microcontrolador y para entender mejor el funcionamiento de las instrucciones condicionales, es necesario conocer las partes principales de un microcontrolador.
PARTES PRINCIPALES DE UN MICROCONTROLADOR.
En la Figura 3 podemos observar las partes principales de un microcontrolador. La siguiente es una lista de esas partes:
1 – CPU (Central Processing Unit) se encarga de procesar las instrucciones.
2 – Memoria de Programa. Donde se almacenan las instrucciones que la CPU debe ejecutar.
3 – Memoria de Datos (RAM). Donde se almacenan los datos del programa.
4 – Puertos de Entrada/Salida. Por donde podemos sacar o entrar datos.
5 – Memoria EEPROM. Para almacenamiento permanente de datos.
En la Figura 4 podemos observar un mapa conceptual de las partes de un microcontrolador. En este mapa podemos observar que un microcontrolador es formado principalmente por la CPU o Unidad Central de Procesamiento y Memorias. A la vez, la CPU es formada por la unidad de Búsqueda, Decodificación y Ejecución de instrucciones y por la ALU o Unidad Lógico y Aritmética.
Las memorias son formadas por: La memoria de programa, la memoria RAM, puertos de Entrada/Salida y memoria EEPROM. Podemos entender la Figura 4, como las partes de un microcontrolador.
La Figura 5 muestra como son interrelacionados o conectadas estas partes. La CPU es directamente conectada a la memoria de programa, para así poder buscar, decodificar y ejecutar las instrucciones. También podemos observar que la CPU se conecta con la memoria RAM y los puertos de entrada/salida.
La Figura 6 muestra cómo se almacenan las instrucciones en la memoria de programa, las cuales se ejecutan en forma secuencial y normalmente siempre se repiten en forma de bucle o loop, es decir que cuando se ejecuta la última instrucción, el programa vuelve a comenzar.
La Figura 7, muestra una instrucción condicional, es decir que si la lectura del sensor es mayor a 16, almacenada en la variable setpoint en la memoria de datos, las instrucciones 3 y 4, se ejecutan, pero si es menor, estas instrucciones no se ejecutan. Aquí estamos haciendo uso de una instrucción condicional.
TOMA DE DECISIÓN EN BASE A UNA CONDICION.
Todas las instrucciones condicionales son formadas por 3 partes:
1 – Lainstrucción .
2 – La condición de teste.
3 – El bloque de código que se ejecutara caso la condición se cumpla.
La Figura 8 muestra las partes de una instrucción condicional.
La siguiente es una lista de las instrucciones usadas para testar una condición:
if
if else
for
while
do while
Para la condición es necesario usar un operador que realice el teste de algún valor, normalmente leído de un puerto de entrada/salida o almacenado en alguna variable en memoria. Los principales operadores son:
== (igualdad)
¡= (diferencia)
< (menor que)
> (mayor que)
<= (menor o igual que)
>= (mayor o igual que)
&& (lógica AND)
|| (lógica OR)
Los bloques de código a ejecutar, normalmente prenden o apagan actuadores conectados a los pines de salida. Vamos a estudiar cada una de las instrucciones condicionales, que podemos usar en un microcontrolador. En los títulos de las instrucciones mostradas en este artículo, escribimos la palabra en mayúsculas (IF, WHILE, ETC), sola para resaltar el nombre de la instrucción, pero al escribir en el editor (IDE), es necesario hacerlo con letras minúsculas (if, while, etc.), ya que el lenguaje usado en Arduino es C/C++ y todas estas instrucciones de salto condicional se escriben en letra minúscula.
LA INSTRUCCIÓN IF (if).
La instrucción if, puede ser entendida como: “si sucede algo, haga esto”. Por ejemplo veamos la siguiente instrucción:
if (temperatura > 16) { Bloque de Codigo a Ejecutar. }
Esta instrucción condicional, está diciendo: si la temperatura es mayor a 16 grados centígrados, ejecute el bloque de código.
La Figura 9 muestra un diagrama de flujo donde un LED prende, si la lectura del sensor de temperatura es mayor a 16 grados centígrados. El código para este ejemplo seria:
if (temperatura > 16) { digitalWrite(led, HIGH); }
Con este ejemplo, podemos ver las infinitas posibilidades de testar los valores de variables numéricas. Los ejemplos mostrados en este artículo pueden ser encontrados en el menú:Archivos->Ejemplos->05.Control . Vea la Figura 10.
LA INSTRUCCIÓN IF ELSE (if…else).
Esta instrucción permite ejecutar unbloque de código, caso la condición se cumpla y otro bloque de código caso la condición no se cumpla. Por ejemplo, el siguiente programa prende un LED, caso la temperatura sea mayor a 30 grados centígrados, pero si esto no se cumple, entonces apaga el LED. Esta instrucción puede ser entendida como: “Si se cumple la condición, haga esto, caso contrario, entonces, haga otra cosa”.
La Figura 11 muestra el diagrama de flujo.
if (temperatura > 16) { digitalWrite(led, HIGH); } else { digitalWrite(led, LOW); }
El siguiente ejemplo de instrucción condicional if..else, puede ser encontrado en el menú: Archivo->Ejemplos->05.Control->IfStatementConditional
const int analogPin = A0; // pin that the sensor is attached to const int ledPin = 13; // pin that the LED is attached to const int threshold = 400; // an arbitrary threshold level that's in the range of the analog input void setup() { // initialize the LED pin as an output: pinMode(ledPin, OUTPUT); // initialize serial communications: Serial.begin(9600); } void loop() { // read the value of the potentiometer: int analogValue = analogRead(analogPin); // if the analog value is high enough, turn on the LED: if (analogValue > threshold) { digitalWrite(ledPin, HIGH); } else { digitalWrite(ledPin, LOW); } // print the analog value: Serial.println(analogValue); delay(1); // delay in between reads for stability }
El anterior programa, configura un pin analógico como entrada y un pin de salida para controlar un LED. El programa lee el pin analógico y lo almacena en la variableanalogValue . Si este valor es mayor que el dado a la variablethreshold, el LED enciende. Caso contrario el LED se apaga. La Figura 12 muestra el circuito electrónico para testar este programa.
LA INSTRUCCIÓN SWITCH/CASE (switch/case).
Esta instrucción es basada en el operador de igualdad (==) y se usa para valores numéricos. El siguiente es un ejemplo de esta instrucción. Caso la variable temperatura, sea igual a 7, entonces prenda LED rojo. Caso la variable sea igual a 12, entonces, prenda el LED verde. Caso la variable sea igual a 32, prenda el LED azul.
switch (temperatura) { case 7: digitalWrite(redLED, HIGH); break; case 12: digitalWrite(greenLED, HIGH); break; case 32: digitalWrite(blueLED, HIGH); break; }
El siguiente ejemplo puede ser encontrado en el menú:Archivo->Ejemplos->05.Control->SwitchCase . En este ejemplo conectamos una resistencia variable a la luz o LDR al puerto analógico 0. El programa lee el sensor y mapea a 4 valores (0, 1, 2 y 3). Coloque el circuito en un lugar iluminado. Ahora puede mover la mano sobre el sensor o LDR y puede ver los resultados en el monitor serial. Este es un buen ejemplo de la instrucciónswitch…case . El circuito electrónico para testar este programa puede ser visto en la Figura 13.
// these constants won't change. They are the lowest and highest readings you // get from your sensor: const int sensorMin = 0; // sensor minimum, discovered through experiment const int sensorMax = 600; // sensor maximum, discovered through experiment void setup() { // initialize serial communication: Serial.begin(9600); } void loop() { // read the sensor: int sensorReading = analogRead(A0); // map the sensor range to a range of four options: int range = map(sensorReading, sensorMin, sensorMax, 0, 3); // do something different depending on the range value: switch (range) { case 0: // your hand is on the sensor Serial.println("dark"); break; case 1: // your hand is close to the sensor Serial.println("dim"); break; case 2: // your hand is a few inches from the sensor Serial.println("medium"); break; case 3: // your hand is nowhere near the sensor Serial.println("bright"); break; } delay(1); // delay in between reads for stability }
El siguiente ejemplo puede ser encontrado en el menú:Archivo->Ejemplos->05.Control->SwitchCase2 . En este ejemplo usamos el Monitor Serial para demostrar o testar el uso de la instrucción switch. El monitor serial se encuentra en el menú:Herramientas->Monitor Serie . Abra el monitor serial y envié cualquier carácter. Los caracteres a, b, c, d y e, prenderán algún LED. Cualquier otro carácter apagara los LEDs. El circuito usado para testar este programa, puede ser visto en la Figura 14.
void setup() { // initialize serial communication: Serial.begin(9600); // initialize the LED pins: for (int thisPin = 2; thisPin < 7; thisPin++) { pinMode(thisPin, OUTPUT); } } void loop() { // read the sensor: if (Serial.available() > 0) { int inByte = Serial.read(); // do something different depending on the character received. // The switch statement expects single number values for each case; in this // example, though, you're using single quotes to tell the controller to get // the ASCII value for the character. For example 'a' = 97, 'b' = 98, // and so forth: switch (inByte) { case 'a': digitalWrite(2, HIGH); break; case 'b': digitalWrite(3, HIGH); break; case 'c': digitalWrite(4, HIGH); break; case 'd': digitalWrite(5, HIGH); break; case 'e': digitalWrite(6, HIGH); break; default: // turn all the LEDs off: for (int thisPin = 2; thisPin < 7; thisPin++) { digitalWrite(thisPin, LOW); } } } }
LA INSTRUCCIÓN FOR (for).
La instrucción for, es usada para ejecutar bloques de código por varias veces (loops). La cantidad de veces que el bloque de código se repite, es controlada por un contador. Las partes de la instrucción for, son 5, las cuales enumeramos en la siguiente lista:
1 – La instrucción.
2 – Inicialización del contador.
3 – La condición o hasta cuantas veces se repite el bloque de código.
4 – El incremento del contador.
5 – El Bloque de código.
Como podemos observar, esta instrucción es especializada para repetir la ejecución de un Bloque de código, un número determinado de veces. Por ejemplo el siguiente programa ejecutara el Bloque de código 18 veces.
for (int counter = 0; counter < 18; counter ++) { Bloque de codigo }
El siguiente ejemplo puede ser encontrado en el menú: Archivo->Ejemplos->05.Control->ForLoopIteration . En este ejemplo podemos ver el uso de la instrucción for. El circuito usado para testar este programa puede ser visto en la Figura 15. Lo que hace este programa es: primero enciende las luces secuencialmente en un sentido, desde el pin más bajo al más alto, luego lo hace en el otro sentido, del más alto al más bajo. Algo muy parecido a las luces del popular auto fantástico.
int timer = 100; // The higher the number, the slower the timing. void setup() { // use a for loop to initialize each pin as an output: for (int thisPin = 2; thisPin < 8; thisPin++) { pinMode(thisPin, OUTPUT); } } void loop() { // loop from the lowest pin to the highest: for (int thisPin = 2; thisPin < 8; thisPin++) { // turn the pin on: digitalWrite(thisPin, HIGH); delay(timer); // turn the pin off: digitalWrite(thisPin, LOW); } // loop from the highest pin to the lowest: for (int thisPin = 7; thisPin >= 2; thisPin--) { // turn the pin on: digitalWrite(thisPin, HIGH); delay(timer); // turn the pin off: digitalWrite(thisPin, LOW); } }
LA INSTRUCCIÓN WHILE (while)
La instrucción while se puede entender como: “mientras se cumpla la condición, ejecute el bloque de código”. La diferencia entre la instrucción if y la instrucciónwhile, está en que if, si se cumple lacondición, ejecutara el bloque de código una sola vez, mientras que la instrucciónwhile, ejecutara elbloque de código, tantas veces como la condición se cumpla. Una vez que la condición sea falsa, el programa saltara y saldrá de la instrucciónwhile . Por ejemplo, esta instrucción puede ser útil en una cerradura electrónica y caso el usuario no entre la llave correcta, el bucle se repetirá. El siguiente es un ejemplo:
while (userKey != currentKey) { Bloque de código. LlaveIncorrecta(); }
Mientras la llave sea diferente, se ejecutara el bloque de código: LlaveIncorrecta.
El siguiente ejemplo puede ser encontrado en el menú: Archivo->Ejemplos->05.Control->WhileStatementConditional . Este ejemplo muestra cómo usar la instrucción while, para calibrar el valor de un sensor análogo. Cuando el programa entra en el loop principal, lee el valor de la fotorresistencia o LDR conectado en la entrada análoga 0. Pero si, el pulsador conectado a la entrada digital 2, es presionado, el programa llama la rutina calibrate(). Esta rutina verifica los niveles más altos y bajos para el sensor. Liberado el botón, el programa continúa en el loop principal. Esta técnica permite, actualizar los valores máximos y mínimos de un local o ambiente, cuando las condiciones de luz cambian.
La Figura 16 muestra el esquema eléctrico y la Figura 17 como puede ser montado en un protoboard.
Conecte el sensor al pin análogo 0, con un resistor de 10Kohms a tierra. Conecte el botón al pin digital 2, con un resistor a tierra. Conecte el LED al pin digital 9 a través de un resistor de 330 Ohms. El siguiente es el programa.
const int sensorPin = A0; // pin that the sensor is attached to const int ledPin = 9; // pin that the LED is attached to const int indicatorLedPin = 13; // pin that the built-in LED is attached to const int buttonPin = 2; // pin that the button is attached to // These variables will change: int sensorMin = 1023; // minimum sensor value int sensorMax = 0; // maximum sensor value int sensorValue = 0; // the sensor value void setup() { // set the LED pins as outputs and the switch pin as input: pinMode(indicatorLedPin, OUTPUT); pinMode(ledPin, OUTPUT); pinMode(buttonPin, INPUT); } void loop() { // while the button is pressed, take calibration readings: while (digitalRead(buttonPin) == HIGH) { calibrate(); } // signal the end of the calibration period digitalWrite(indicatorLedPin, LOW); // read the sensor: sensorValue = analogRead(sensorPin); // apply the calibration to the sensor reading sensorValue = map(sensorValue, sensorMin, sensorMax, 0, 255); // in case the sensor value is outside the range seen during calibration sensorValue = constrain(sensorValue, 0, 255); // fade the LED using the calibrated value: analogWrite(ledPin, sensorValue); } void calibrate() { // turn on the indicator LED to indicate that calibration is happening: digitalWrite(indicatorLedPin, HIGH); // read the sensor: sensorValue = analogRead(sensorPin); // record the maximum sensor value if (sensorValue > sensorMax) { sensorMax = sensorValue; } // record the minimum sensor value if (sensorValue < sensorMin) { sensorMin = sensorValue; } }
LA INSTRUCCIÓN DO.. WHILE (do…while)
La instrucción do…while, es similar a la instrucción while, con la diferencia que la instrucción do…while forza el programa a ejecutar por lo menos una vez, el bloque de código que este entre los corchetes de la instrucción do . Veamos un ejemplo:
do { currentKey = ReadKeyboard(); } while (userKey ¡= currentKey);
Este código asegura que por lo menos una vez sea leído el teclado (keyboard). El uso de la instrucción while o do…while depende de las necesidades de la aplicación o proyecto.
Como todo en la vida, con práctica podemos obtener buenos resultados. No se preocupe, si al principio el programa no resulta como lo ideo. Verifique con el monitor serial el programa y redefina o mejore el programa.