Continuamos avanzando en nuestro tutorial de programación de microcontroladores. Hoy aprenderemos todo lo referente a la toma de decisiones en BASIC. Sabemos que un programa es, básicamente, una lista de instrucciones que el microcontrolador debe ejecutar en orden para realizar una tarea determinada. De alguna manera, se asemeja a una receta de cocina (programa) que el cocinero (microcontrolador) debe ir ejecutando. Con mucha frecuencia el cocinero debe tomar decisiones en función de diversos parámetros, y realizar una cosa u otra.
Esto también ocurre en el mundo de los microcontroladores. En cualquier programa medianamente complejo que realicemos, seguramente en algún punto debamos tomar alguna decisión basándonos en el estado de una entrada o en el valor de una variable. Por supuesto, PIC BASIC incorpora instrucciones que nos permiten este tipo de comportamiento, siendo la más sencilla y frecuentemente utilizada la sentencia IF - THEN - ELSE - ENDIF.
Resulta muy sencillo entender su funcionamiento si traducimos su significado al español. IF - THEN - ELSE - ENDIF significa algo así como “SI ocurre tal cosa ENTONCES realizo esta tarea SINO hago esta otra FIN SI”.
Existen varias formas de utilizar esta instrucción en PIC BASIC. Veremos los tres casos posibles, comenzando por el más sencillo.
Primer caso: IF-THEN con una sola instrucción
El caso más simple es el siguiente:
IF condición THEN instrucción
Como vimos, "IF" significa "SI....", y "THEN" significa "LUEGO" o "ENTONCES". Por lo tanto, el caso anterior puede leerse como "SI se cumple la condición, entonces ejecuto la instrucción"
La "condición" es una expresión lógica que puede ser verdadera o falsa. En caso de ser verdadera, la instrucción a continuación del THEN será ejecutada. En caso de que la condición sea falsa, el programa seguirá su ejecución con la instrucción siguiente al "IF - THEN".
Seguramente un ejemplo servirá para que lo comprendamos mejor. Supongamos el siguiente programa:
ALLDIGITAL 'Todos los pines como E/S.
TRISA = %11111111 'Todo el PORTA como entradas
DIM AUX AS BYTE 'Declaro la variable "AUX" como BYTE
DIM TOTAL AS BYTE 'Declaro la variable "TOTAL" como BYTE
TOTAL = 100 'Le asigno el valor 100 a la variable "TOTAL"
AUX = 5 'Le asigno el valor 5 a la variable "AUX"
IF PORTA.4 = 1 THEN AUX = 4
TOTAL = TOTAL + AUX 'Sumo a "TOTAL" el valor de "AUX"
Cuando comienza el programa, se declaran dos variables tipo BYTE (que como vimos antes, pueden almacenar valores entre 0 y 255). A una de ellas, TOTAL, se le asigna el valor "100" y a la restante, AUX el valor "5". Hasta aquí, no hay nada que no hayamos visto antes.
La línea siguiente realiza la siguiente tarea: evalúa si la condición PORTA.4 = 1 es cierta. En caso de que efectivamente el valor presente en el bit 4 del PORTA sea "1" (ese pin del microcontrolador estará en estado alto), se ejecuta la instrucción a continuación del THEN, por lo que la variable "AUX" toma el valor "4", y se pasa a la instrucción de abajo. Si PORTA.4 es igual a "0", se pasa a la instrucción siguiente sin más.
El valor final de la variable "TOTAL" depende entonces de cuál sea el estado de PORTA.4 al momento de hacer la evaluación. Si es igual a "1", "TOTAL" tendrá un valor de 104 (100 + 4). Si PORTA.4 = 0, "TOTAL" tendrá un valor de 105 (100 + 5).
Veamos algunos ejemplos válidos de este caso:
IF PULSADOR = 1 THEN PORTA.0 = 1
IF B > A THEN LED=ON
IF B = 5 THEN A = 0
IF (A = 0) OR (B = 5) THEN C = 2
IF PORTA.0 THEN PORTB.3 = 0
En el último ejemplo, la condición PORTA.0 equivale a PORTA.0 = 1.
Segundo caso: un bloque de instrucciones con ENDIF
Muchas veces, luego de evaluar la condición, necesitamos ejecutar más de una instrucción. En los ejemplos vistos en el caso anterior siempre se ejecutaba una sola instrucción cuando la condición era cierta. La manera de ejecutar múltiples sentencias dentro de una estructura IF-THEN implica emplear el ENDIF, con lo que la sintaxis de la instrucción queda como sigue:
IF condición THEN
instrucción 1
instrucción 2
...
instrucción n
ENDIF
En realidad, no varía prácticamente nada respecto del primer caso. Solo debemos saber que en esta ocasión se van a ejecutar todas las instrucciones del bloque que se encuentren entre el THEN y el ENDIF cada vez que la condición sea verdadera.
Veamos un ejemplo. Supongamos el siguiente programa:
DIM A AS BYTE 'Declaro la variable "A" como BYTE
DIM B AS BYTE 'Declaro la variable "B" como BYTE
DIM C AS BYTE 'Declaro la variable "C" como BYTE
DIM D AS BYTE 'Declaro la variable "D" como BYTE
DIM TOTAL AS BYTE 'Declaro la variable "TOTAL" como BYTE
TOTAL = 0 'Le asigno el valor 0 a la variable "TOTAL"
A = 2 'Le asigno el valor 2 a la variable "A"
B = 5 'Le asigno el valor 5 a la variable "B"
C = 1 'Le asigno el valor 1 a la variable "C"
D = 0 'Le asigno el valor 0 a la variable "D"
IF A = 2 THEN
A = B + (C * D)
TOTAL = A * B
ENDIF
En el ejemplo anterior, la condición "A = 2" es verdadera, ya que ese es el valor que le asignamos a "A" al comienzo del programa. Esto significa que las dos instrucciones dentro del bloque THEN-ENDIF se ejecutarán. Esto hace que TOTAL tome el valor de 25 (¡chicos, hagan las cuentitas!). Si "A" hubiese tenido otro valor, esas dos sentencias no se ejecutarían y TOTAL seguiría valiendo "0" al terminar el programa. ¿Fácil, verdad?
Tercer caso: elegir entre dos bloques con ELSE
Ahora, analicemos el último caso posible. A veces, de acuerdo con el resultado de la condición, necesitamos ejecutar un grupo u otro de instrucciones. Para eso, utilizamos la cláusula ELSE, que todavía no habíamos empleado. La sintaxis en este caso queda como sigue:
IF condición THEN
instrucciónv 1
instrucciónv 2
...
instrucciónv n
ELSE
instrucciónf 1
instrucciónf 2
...
instrucciónf n
ENDIF
Es decir, si la condición es verdadera, se ejecutan las sentencias entre THEN y ELSE. Y si la condición es falsa, las que estén entre ELSE y ENDIF. "ELSE" puede ser traducido como "en otro caso" o "si no...".
Veamos un ejemplo de esta situación. Supongamos el siguiente programa:
ALLDIGITAL 'Todos los pines como E/S.
TRISA = %11111111 'Todo el PORTA como entradas
DIM AUX AS BYTE 'Declaro la variable "AUX" como BYTE
DIM TOTAL AS BYTE 'Declaro la variable "TOTAL" como BYTE
TOTAL = 10 'Le asigno el valor 10 a la variable "TOTAL"
AUX = 2 'Le asigno el valor 2 a la variable "AUX"
IF PORTA.4 = 1 THEN
AUX = 4
TOTAL = TOTAL + 5
ELSE
AUX = 0
TOTAL = TOTAL + 15
ENDIF
En el ejemplo anterior, la condición PORTA.4 = 1 determina qué bloque de instrucciones se ejecuta. Si es verdadera, AUX = 4 y TOTAL = TOTAL + 5 son usadas. En caso contrario se ejecutan AUX = 0 y TOTAL = TOTAL + 15. Luego, independientemente de cuál haya sido el caso, el programa sigue con la sentencia que se encuentre a continuación del ENDIF.
Anidamiento de estructuras IF-THEN-ELSE-ENDIF
Por último, tenemos que saber que es posible "anidar" instrucciones IF-THEN-ELSE-ENDIF, con lo que se pueden tomar decisiones verdaderamente complejas, con forma de "árbol", donde cada condición representa una "rama" diferente. Por supuesto, tenemos que ser cautos en el uso de esta característica, ya que debido a limitaciones en el tamaño de la pila y en la cantidad de memoria disponible del PIC podemos ocasionar un desborde y el programa colapsará. Este sería un ejemplo de un anidamiento:
IF PORTB.1 = 1 THEN
IF A = 2 THEN
A = B + (C * D)
TOTAL = A * B
ELSE
A = 0
ENDIF
ELSE
A = 19
ENDIF
Las sentencias en color rojo corresponden a una estructura IF-THEN-ELSE-ENDIF y las que están en azul a la otra, que se encuentra dentro ("anidada" en) de la primera.
La función LOOKUP
IF… ENDIF no es la única instrucción de toma de decisiones que veremos. Antes de terminar esta cuarta entrega, aprenderemos a utilizar la potente función LOOKUP. La función LOOKUP puede ser utilizada para seleccionar un dato tipo "Byte" desde una lista de constantes del mismo tipo, en función del valor de un índice (que también debe ser de tipo "Byte"). El resultado de la selección se almacena (¡como no!) también en una variable tipo byte.
La forma de la función LOOKUP es realmente sencilla:
variable = LOOKUP(byte0, byte1, ..., byteN), indice
Cuando se ejecuta, "variable" tendrá el valor correspondiente al elemento que se encuentre en la posición "índice" de la lista de valores que está entre los paréntesis. Es importante recordar (lo vamos a repetir varias veces) que el primer elemento de la lista corresponde al valor "0" de "índice".
Veamos un ejemplo sencillo:
DIM indice AS BYTE
DIM variable AS BYTE
indice = 3
variable = LOOKUP(25, 35, 55, 70, 85, 100), indice
...
variable tendrá el valor "70" (decimal) al ejecutar este código. El primer elemento de la lista, recordemos, corresponde al valor "0" de indice. Si bien la lista puede contener un máximo de 255 elementos, que es el máximo direccionable por una variable indice de tipo byte, hay que asegurarse de que el microcontrolador que estamos empleando tenga memoria suficiente para albergarla.
El segundo ejemplo (extraído de la propia ayuda del PIC SIMULATOR IDE) nos muestra cómo manejar un display LED de siete segmentos conectado al puerto B:
Dim digito As Byte
Dim mascara As Byte
'Comienzo el bucle principal
loop:
TRISB = %00000000
digito = 7
mascara = LookUp(0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f), digito
PORTB = mascara
WaitMs 1000 'Espero un segundo…
Goto loop
Lo que hace concretamente ese trozo de código es buscar dentro de la lista cuál es el valor binario que corresponde asignar al PORTB para que los segmentos adecuados enciendan en el display, mostrando el valor que contiene la variable "digito".
LOOKUP con cadenas de caracteres
Si algunas o todas las constantes de la lista son valores ASCII, se puede hacer más corta y legible la misma utilizando como parte de ella una cadena de caracteres, como se ve a continuación.
MASK = LOOKUP("ABCDEFGHIJK"), INDEX
"A" sería el valor que tendría MASK cuando INDEX vale "0", y "K" cuando INDEX tenga el valor "10".
Por último, en caso de que el valor de INDEX sea mayor que la cantidad de argumentos de la lista, el valor de la variable (en este ejemplo MASK) no cambia.
Cierre de la cuarta entrega
Con esto terminamos la cuarta entrega del tutorial. En la siguiente, veremos cómo construir un proyecto que emplee todo lo visto hasta aquí. ¡Hasta la próxima semana!
Más información