> Capitulo 06 - Variables:
Vamos a comenzar a ver algo de la programación en el BASIC incluido en el entorno PIC SIMULATOR IDE, al que en adelante nos referiremos como "BASIC", a secas.
La programación seria prácticamente imposible sin el uso de variables. Podemos hacernos una imagen mental de las variables consistente en una caja en la que podemos guardar algo. Esa caja es una de las muchas que disponemos, y tiene en su frente pegada una etiqueta con su nombre. Estas cajas tienen ciertas particularidades, que hace que solo se puedan guardar en ellas determinados tipos de objetos.
En esta analogía, cada caja es una variable, su contenido es el valor que adopta, y la etiqueta es el nombre de la variable. Como su nombre lo indica, y como veremos mas adelante, el contenido de una variable puede ser modificado a lo largo del programa.
El BASIC tenemos distintos tipos de variable, según el dato que puedan almacenar:
- Bit (un bit de longitud, almacena 0 o 1 únicamente)
- Byte (un byte de longitud, almacena números enteros entre 0 y 255)
- Word (dos bytes de longitud, almacena números enteros entre 0 y 65,535)
- Long (cuatro dos bytes de longitud, almacena números enteros entre 0 y 4,294,967,295)
El tipo "Long" solo esta disponible mediante un modulo opcional al PIC SIMULATOR IDE.
A diferencia de otros BASIC, la declaración de variables puede ser hecha en cualquier parte del programa, y todas son consideradas globales, es decir, su valor es accesible desde todas las subrutinas y zonas del programa. El numero de variables esta lógicamente limitado al monto de memoria RAM disponible en cada microcontrolador. Las variables las declaramos utilizando la instrucción DIM, como se muestra en los siguientes ejemplos:
DIM A AS BIT
DIM B AS BYTE
DIM X AS WORD
DIM Y AS LONG
También es posible utilizar vectores, que son una matriz de dimensiones 1xN . Por ejemplo, la sentencia siguiente:
DIM A(10) AS BYTE
declara un vector (al que nos referiremos algunas veces como "array") de diez elementos del tipo BYTE, que serán accedidos mediante el uso de subíndice (entre paréntesis) del 0 al 9.
LA sentencia RESERVE le permite al programador reservar un numero de posiciones de la RAM para su uso en rutinas en assembler o para el In-Circuit Debugger de MPLAB. Simplemente, si queremos reservar 20 bytes de RAM, escribimos:
RESERVE 20
Las variables tipo Word, como vimos, están compuestas por dos bytes. el primero de ellos es llamado byte "alto" y el otro "bajo", dado que el primero contiene los 8 bits mas significativos. En BASIC podemos acceder individualmente a cada uno de los bytes que componen un Word mediante las extensiones ".HB" (High byte, o byte alto) y ".LB" (Low Byte o byte bajo) . Veamos un ejemplo:
DIM A AS BYTE
DIM B AS WORD
A = B.HB
A = B.LB 'Esto es lo mismo que A = B
B.HB = A
B.LB = A
B = A 'Esto también borra el byte alto de la variable B
Los bits individuales de cada variable pueden ser accedidos individualmente también, simplemente poniendo como extensión ".n" donde "n" es el numero de bit (1,2, 3, etc. )
DIM A AS BYTE
DIM B AS BIT
B = A.1
B = A.7
A.0 = A.5
Todos los registros del microcontrolador esta disponibles para usar en los programas BASIC, como si se tratase de variables del tipo BYTE con el nombre del registro utilizado en las datasheet (PORTA, PORTB, TRISA, etc.). Por supuesto, se puede acceder a bits individuales de los registros con la técnica vista párrafos atrás. Algunos ejemplos:
TRISA.1 = 0
TRISB = 0
PORTA.1 = 1
PORTB = 255
STATUS.RP0 = 1
INTCON.INTF = 0
Existe una "forma corta" de acceder a los bits individuales de cada port, simplemente usando las variables BASIC tipo byte RA, RB, RC, RD, RE o bien las tipo bit RA0, RA1, RA2, ..., RE6, RE7
RA = 0xFF
RB0 = 1
En BASIC también podemos usar punteros. En realidad, cualquier variable definida como tipo BYTE o WORD pude ser usada como un putero de memoria, usándola como argumento de la función POINTER. El valor contenido por la variable debe tener un valor comprendido entre 0 y 511. Ejemplos:
DIM X AS WORD
DIM Y AS BYTE
X = 0x3F
Y = POINTER(X)
Y = Y + 0x55
X = X - 1
POINTER(X) = Y
Y = 0xAA
X = X - 1
POINTER(X) = Y
Una forma de escribir programas que nos resulten mucho mas fáciles de entender es el uso de nombres simbólicos, o SYMBOL. Un "symbol" es una cadena que contiene código, asignado a un nombre. Al momento de compilar, PIC BASIC hace la "búsqueda y reemplazo" de nuestros símbolos y luego genera el código ASM y el HEX. Supongamos que tenemos un LED conectado al bit cero del puerto B. Mediante SYMBOL podemos hacer:
SYMBOL LED1 = PORTB.0
Luego, si queremos encender el LED, en lugar de
PORTB.0 = 1
podemos hacer
LED1 = 1
que es mucho mas claro y fácil de leer.
Las constantes (valores que usamos en nuestro programa, y que, por ejemplo, asignamos a las variables) pueden ser escritas en decimal (directamente el valor), en hexadecimal (anteponiendo "0x" o posponiendo "H" al valor) o en binario (anteponiendo "%" al valor). Por ejemplo:
DIM A AS BIT
DIM B AS BYTE
A = TRUE
B = 0x55
B = %01010101
Por supuesto, se pueden asignar nombres a las constantes, usando la instrucción CONST:
DIM A AS WORD
CONST PI = 314
A = PI
Hay tres instrucciones para el manejo individual de bits, que si bien no hacen nada que no se puede resolver con otras instrucciones o símbolos, ayudan mucho en la lectura del código. Se tratan de HIGH, LOW y TOGGLE, que ponen el bit en alto, bajo o lo invierten, respectivamente.
Importante: Si el bit implicado como argumento de una de estas instrucciones es un bit de un PORT, el mismo bit en el TRIS correspondiente es puesto en cero, y dicho pin queda configurado como salida. Algunos ejemplos:
HIGH PORTB.0
LOW ADCON0.ADON
TOGGLE OPTION_REG.INTEDG
> Capitulo 07 - Operaciones Lógicas y Matemáticas:
PIC SIMULATOR IDE dispone de cinco operaciones matemáticas básicas, disponibles para las variables tipo Byte y Word. Estas son la suma (operador +), la sustracción (operador -), el producto (operador *), el cociente (operador /) y el módulo (operador MOD) .Por supuesto, el compilador es capaz de combinarlas para obtener operaciones matemáticas mas complejas.
DIM A AS WORD
DIM B AS WORD
DIM X AS WORD
A = 123
B = A * 234
X = 2
X = (12345 - B * X) / (A + B)
Es posible calcular raíces cuadradas (aunque el resultado debe ser entero) con la función SQR:
DIM A AS WORD
A = 3600
A = SQR(A)
Para las variables de tipo Bit existen siete operaciones lógicas disponibles. Solo es posible efectuar una operación lógica por instrucción (aunque es muy posible que próximas versiones permitan mas flexibilidad. Este al tanto de las novedades!). Estas operaciones también están disponibles para variables tipo Word o Byte. Veamos algunos ejemplos:
DIM A AS BIT
DIM B AS BIT
DIM X AS BIT
X = NOT A
X = A AND B
X = A OR B
X = A XOR B
X = A NAND B
X = A NOR B
X = A NXOR B
DIM A AS WORD
DIM B AS WORD
A = A OR B
PORTB = PORTC AND %11110000
> Capitulo 08 - Mi primer programa: Un LED parpadeando
Luego de todos estos capítulos de introducción, puramente teóricos, vamos a encarar nuestro primer programa. A diferencia de un programa de ordenador, donde uno escribe el programa, lo compila, lo ejecuta y ya, en el mundo de los microcontroladores hay que, previamente, definir el tipo de microcontrolador que se va a utilizar, cual va a ser su frecuencia de clock, como va a ser el circuito en que se va a utilizar el mismo, etc.
Para estas practicas, utilizaremos un PIC16F628A, uno de los mas difundidos y que mas o menos viene a reemplazar al viejo y popular PIC16F84, ya obsoleto. El diagrama circuital que utilizaremos para las primeras practicas es el siguiente:

Si bien se supone que quien esta leyendo este tutorial tiene una buena idea sobre electrónica y microcontroladores, igualmente vamos a hacer una muy breve descripción del circuito.
En primer lugar, vamos a aprovechar el oscilador interno del 16F628A y nos evitaremos el xtal y condensadores asociados. El puerto B del micro (pines 6 al 13) esta conectado a 8 LEDs mediante 8 resistencias de 220ohms, que tienen como función limitar la corriente que circula por los LEDS. Estos serán nuestras "salidas". Los pines 17 y 18, correspondientes al PORTA.0 y PORTA.1 están conectados a sendos pulsadores, que al ser presionados conducen 5V (un "1") al pin respectivo. Cuando están en reposo, las resistencias R1 y R2 se encargan de mantener el pin en "0". Por ultimo, el pin 1 (PORTA.2) comanda un parlante mediante un transistor, para hacer alguna prueba con sonidos.
El circuito debe alimentarse con 5v bien filtrados y regulados. Si no sabes como construir una fuente, puedes leer algo sobre el tema aquí.
Volviendo a nuestro programa, vamos a escribir el "hola mundo" de los microcontroladores: encender un LED.
El primer paso es, desde el menú "Opciones" -> "Select Microcontroller", elegir el PIC16F628A.

Luego, debemos configurar los bits correspondientes:

Lo destacable por ahora de esta configuración es que estamos dejando la memoria (FLASH y EEPROM) sin protección, que el pin RESET se va a comportar como I/O y que usaremos como oscilador el oscilador interno INTRC.
Una vez hecho esto, arrancamos el edito de BASIC (presionando CTRL-C, por ejemplo), y escribimos el siguiente código:

Vamos a analizarlo línea por línea para entender su funcionamiento:
La línea 001 utiliza la sentencia AllDigital para convertir todos los pines del micro en pines de E/S. Esto equivale a deshabilitar los comparadores, conversores A/D y todos los módulos que pudiese tener nuestro microcontrolador. No es la única manera de hacer esto, pero si la mas sencilla desde el punto de vista del programador BASIC.
Las líneas 003 y 004 convierten todos los pines del puerto A en entradas ( TRISA = %11111111 ) y los del puerto B en salidas ( TRISB = %00000000 ). El "%" indica que el numero que viene a continuación esta en binario. Se podría haber escrito, por ejemplo TRISB = 0 y hubiera sido lo mismo. Personalmente me gusta esta manera, ya que "veo" el estado de cada pin. Por supuesto, es valido activar como entrada algunos pines, y como salidas otros, haciendo algo parecido a TRISB = %11000111 .
En la línea 006 encontramos una "etiqueta" ( loop: ). Esta no hace nada, solo sirve como referencia para enviar el flujo del programa a esa línea desde otro lugar, mediante la sentencia "Goto".
La línea 007 pone en "1" el pin correspondiente a PORTB.0, de manera que en el pin 6 del microcontrolador habrá 5V. Esta tensión hará que circule una corriente a través de la resistencia limitadora y el LED1, haciendo que este se encienda, ya que el cátodo se encuentra conectado a 0V.
En 008 tenemos la sentencia WaitMs 500 . WaitMs se encarga de hacer una pausa en milisegundos. La duración de la pausa esta dada por el numero que sigue a la instrucción, en este caso 500 milisegundos, o medio segundo.
Luego, en 009, otra vez se vuelve a poner en 0 el pin 6, mediante PORTB.0 = 0 , lo que provoca que ese pin se ponga a 0V, y no haya mas circulación de corriente a través de la resistencia y del LED, con lo que este se apaga.
En 010 se hace nuevamente una pausa de medio segundo, y por ultimo, la línea Goto Loop hace que el programa continúe en la línea 006 (que es donde esta la etiqueta Loop).
El programa se repite indefinidamente, encendiendo el LED medio segundo, apagándolo otro medio segundo.
Si presionamos F9 o vamos al menú que vemos a continuación

PIC SIMULATOR IDE compilara el programa, y cargara el HEX resultante en el simulador. Aparecerá el cuadro de dialogo siguiente, en donde se nos informa entre otras cosas que no han ocurrido errores, el tamaño del programa (69 words), y la ruta a donde se ubicaron los archivos generados.

Si volvemos a la ventana principal del PIC SIMULATOR IDE, y desde "Tools" -> "Microcontroller View" abrimos la vista del microntrolador, al darle "Start" a la simulación tendremos algo parecido a lo que sigue:

En la captura se puede apreciar que el pin 6, correspondiente a RB0 esta en "ON". Si esperamos lo suficiente, veremos como pasa a "OFF", y mas tarde vuelve a "ON", etc. Si queremos esperar menos tiempo, y esto lo debemos tomar como una regla general al correr simulaciones, podemos disminuir el tiempo indicado en las instrucciones "WaitMS" a valores iguales a 1, de esta manera la simulación será mucho mas ágil. Por supuesto, al momento de llevar el HEX a nuestro microcontrolador en el circuito "real", debemos cambiar a los tiempos originales y volver a compilar. Caso contrario, el LED permanecería encendido solo una milésima de segundo, luego apagado el mismo tiempo, etc., por lo que nuestro ojo lo percibiría como encendido a medias, incapaz de discriminar su verdadero estado.
Se podría haber utilizado la instrucción SYMBOL para hacer mas claro el programa. En el siguiente ejemplo, hemos hecho algunos cambio y obtenido un programa que hace exactamente lo mismo que el anterior, pero que resulta mas claro de entender, ya que se aproxima algo mas al "lenguaje natural":

El programa BASIC puede descargarse desde [aquí], y el correspondiente archivo HEX desde [aquí] .
> Capitulo 09 - Mi segundo programa: Usando un pulsador
En la segunda practica del lenguaje BASIC veremos como leer una entrada del PIC. Utilizaremos el mismo esquema que vimos antes, y el programa que mostramos a continuación:

Como resulta evidente a simple vista, el programa ejemplo2.bas es muy similar al ejemplo1.bas que vimos en el capitulo anterior. Las diferencias están dentro del bucle. La instrucción de la línea 007 ( PORTB.0 = PORTA.0 ) hace que el valor del bit 0 del PORTB tome el valor del bit 0 del PORTA. Que ambos bits sean el cero es solo una coincidencia, se podrían haber elegido otros valores.
Al ejecutarse el programa, cada vez que se accione el pulsador conectado a PORTA.0, ese pin se pondrá a estado alto, ya que la corriente circulara desde +V al pin 17 del PIC por medio del pulsador. Ese "estado alto" se interpreta dentro del PIC como un "1", y es el valor que se le asigna a PORTB.0 , con lo que el también pasara a estado alto. Eso provocara que el led conectado en ese pin se ilumine.
Cuando soltamos el pulsador, PORTA.0 vuelve a estado bajo, ya que se pone a masa a través de la resistencia de 10K, y PORTB.0 hará lo propio, apagando el LED.
Nuestro sencillo (sencillísimo!) programa todo lo que hace es "copiar" en el LED el estado del pulsador.
Si presionamos F9 o vamos al menú que vemos a continuación

PIC SIMULATOR IDE compilara el programa, y cargara el HEX resultante en el simulador. Aparecerá el cuadro de dialogo que nos informa que no han ocurrido errores y que el tamaño del programa esta vez es de 20 words.
Si volvemos a la ventana principal del PIC SIMULATOR IDE, y desde "Tools" -> "Microcontroller View" abrimos la vista del microntrolador, al darle "Start" a la simulación tendremos algo parecido a lo que sigue:

El pin 6, correspondiente a RB0 esta en "OFF" por que el pulsador del pin 17 (RA0) esta en OFF. Si con el mouse hacemos un click sobre la "T" que esta al lado del pin 17, la vista del microcontrolador pasara al estado que muestra la imagen siguiente:

Recordemos que el botón "T" significa "cambio" (Toggle) por lo que el estado del pin 17 permanecerá en alto hasta que lo pulsemos otra vez, y el estado del microcontrolador volverá a ser el inicial. Como en cualquier curso, conviene realizar estas practicas, que aunque puedan parecer muy sencillas nos ayudaran a conocer las herramientas disponibles y "tomar confianza" al programa. También es interesante el realizar cambios en el programa BASIC, recompilar y analizar los resultados.
El programa BASIC de este capitulo puede descargarse desde [aquí], y el correspondiente archivo HEX desde [aquí] .
> Capitulo 10 - IF - THEN - ELSE - ENDIF
En cualquier programa medianamente complejo que queramos realizar, seguramente necesitaremos en algún punto tomar alguna decisión basándonos en el estado de una entrada o en el valor de una variable. PIC BASIC incorpora instrucciones que nos permiten este tipo de comportamiento, siendo la mas sencilla y frecuentemente utilizada la sentencia IF - THEN - ELSE - ENDIF.
Existen varias formas de utilizar esta instrucción. Comenzaremos con los casos mas sencillos y a lo largo de este capitulo iremos agregando complejidad hasta ver todas las posibilidades.
CASO 1: El caso mas simple es el siguiente:
IF condición THEN instrucción
"IF" significa "SI....", y "THEN" significa "LUEGO" o "ENTONCES". 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 la condición sea falsa, el programa seguirá su ejecución con la instrucción siguiente al "IF - THEN".
Veamos un ejemplo. Supongamos el siguiente programa:
ALLDIGITAL 'Voy a usar todos los pines como E/S.
TRISA = %11111111 'Todo el PORTA como entradas
DIM A AS BYTE 'Declaro la variable "A" como BYTE
DIM TOTAL AS BYTE 'Declaro la variable "TOTAL" como BYTE
TOTAL = 10 'Le asigno el valor 10 a la variable "TOTAL"
A = 2 'Le asigno el valor 2 a la variable "A"
IF PORTA.4 = 1 THEN A = 4
TOTAL = TOTAL + A 'Sumo a "TOTAL" el valor de "A"
Cundo comienza el programa, se declaran dos variables tipo BYTE (que pueden almacenar valores entre 0 y 255), y a TOTAL se le asigna el valor "0" y a "A" el valor "2". 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", se ejecuta la instrucción a continuación del THEN, la variable "A" toma el valor "4", y se pasa a la instrucción de abajo. Si PORTA es igual a "0", se pasa a la instrucción siguiente sin mas.
El valor final de la variable "TOTAL" depende entonces de cual sea el estado de PORTA.4 al momento de hacer la evaluación. Si es igual a "1", "TOTAL" tendrá un valor de 14 (10 + 4). Si PORTA.4 = 0, "TOTAL" tendrá un valor de 12 (10 + 2).
Veamos algunos ejemplos validos de este caso:
IF A = B THEN PORTA.0 = 1
IF B > A THEN A = B
IF B = 5 THEN A = 0
IF (A = 0) OR (B = 5) THEN C = 2
IF PORTA.0 THEN PORTB.3 = 0
En el ultimo ejemplo la condición PORTA.0 equivale a PORTA.0 = 1.
CASO 2: Muchas veces, luego de evaluar la condición necesitamos ejecutar mas de una instrucción. En los ejemplos vistos en el CASO 1 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:
IF condición THEN
instrucción 1
instrucción 2
...
instrucción n
ENDIF
No varia prácticamente nada respecto del primer caso, solo que esta vez se van a ejecutar todas las instrucciones que se encuentren entre el THEN y el ENDIF cada vez que 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
El ejemplo anterior, la condición A = 2 es verdadera (puesto que ese es el valor que le asignamos a "A" mas arriba), por lo que las dos instrucciones dentro del THEN-ENDIF se ejecutaran. Esto hace que TOTAL tome el valor de 10 (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.
CASO 3: Hay veces que de acuerdo a la condición, queremos ejecutar un grupo u otro de instrucciones. Para eso, utilizamos el ELSE:
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. Supongamos el siguiente programa:
ALLDIGITAL 'Voy a usar todos los pines como E/S.
TRISA = %11111111 'Todo el PORTA como entradas
DIM A AS BYTE 'Declaro la variable "A" como BYTE
DIM TOTAL AS BYTE 'Declaro la variable "TOTAL" como BYTE
TOTAL = 10 'Le asigno el valor 10 a la variable "TOTAL"
A = 2 'Le asigno el valor 2 a la variable "A"
IF PORTA.4 = 1 THEN
A = 4
TOTAL = TOTAL + 5
ELSE
A = 0
TOTAL = TOTAL + 15
ENDIF
El ejemplo anterior, la condición PORTA.4 = 1 determina que bloque de instrucciones se ejecutan. Si es verdadera, A = 4 y TOTAL = TOTAL + 5 son usadas. Caso contrario se ejecutan A = 0 y TOTAL = TOTAL + 15. Luego, independientemente de cual haya sido el caso, el programa sigue con la sentencia que se encuentre a continuación del ENDIF.
Por ultimo, tenemos que saber que es posible "anidar" instrucciones IF-THEN-ELSE-ENDIF, con lo que se pueden tomar decisiones verdaderamente complejas. 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 cantidad de memoria disponible del PIC podemos ocasionar un desborde y el programa colapsara. Este seria 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. |