En las dos primeras entregas de nuestro tutorial vimos cómo manejar una de las salidas del microcontrolador para encender y apagar un LED. Hoy aprenderemos a leer el estado de un pulsador y a usar variables.

Entradas y salidas en el PIC

Como aprendimos, los pines de los puertos del PIC pueden emplearse como salidas (como en el ejemplo del LED) o como entradas. Cuando mediante la instrucción TRIS indicamos al microcontrolador que un pin determinado se comportará como entrada, este colocará en el bit correspondiente del registro de dirección del puerto en cuestión un “1” si el pin está en estado alto (por ejemplo, conectado a +5V) o un “0” si se encuentra en estado bajo (conectado a masa o 0V).

Sabemos que PIC BASIC tiene variables definidas para cada puerto (PORTA, PORTB, etc.), por lo que es muy simple interpretar el estado de las entradas.

Variables en PIC BASIC

Antes de ver cómo emplear un pulsador como entrada, vamos a ver en profundidad cómo se emplean las variables, un tema indispensable para poder escribir (e interpretar) programas que funcionen.

La programación sería prácticamente imposible sin el uso de variables, ya que solo podríamos escribir programas “rígidos”, que no modificaran su comportamiento. Pero ¿qué es una variable?

Es sencillo: podemos imaginar las variables como “cajas” en las que podemos guardar algo. Supongamos que disponemos de muchas de esas cajas, que en su frente tienen pegada una etiqueta con su nombre. Estas cajas tienen ciertas particularidades, lo 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 nos deja adivinar, el contenido de una variable puede ser modificado a lo largo del programa.

En BASIC tenemos distintos tipos de variable, dedicadas a guardar distintos tipos de datos:

  • 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 bytes de longitud, almacena números enteros entre 0 y 4,294,967,295)

(El tipo “Long” solo está disponible mediante un módulo opcional al PIC SIMULATOR IDE).

A diferencia de otros BASIC, la declaración de variables puede hacerse en cualquier parte del programa, y todas son consideradas globales, es decir, su valor es accesible desde todas las subrutinas y zonas del programa. Algunos puristas pueden considerar esto como una falencia del lenguaje, pero en general se puede sacar bastante provecho de esta situación, como veremos a lo largo de esta serie de tutoriales.

El número de variables está lógicamente limitado por la cantidad de memoria RAM disponible en cada microcontrolador. Las variables deben declararse utilizando la instrucción DIM, como se muestra en los siguientes ejemplos:

DIM A AS BIT
DIM TEMPERATURA AS BYTE
DIM TIEMPO AS WORD
DIM AUX AS LONG

También es posible utilizar vectores, que son una matriz de dimensiones 1xN. Por ejemplo, la sentencia siguiente:

DIM DIAS(7) AS BYTE

declara un vector (al que nos referiremos algunas veces como “array”) de siete elementos del tipo BYTE, que serán accedidos mediante el uso de subíndice (entre paréntesis) del 0 al 6.

La sentencia RESERVE le permite al programador reservar un número 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

Acceso a bytes y bits

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 más 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 uno a uno también, simplemente poniendo como extensión “.n” donde “n” es el número 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 están disponibles para usar en los programas BASIC, como si se tratase de variables del tipo BYTE con el nombre del registro utilizado en las datasheets (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 puerto, 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

Punteros, símbolos y constantes

En BASIC también podemos usar punteros. En realidad, cualquier variable definida como tipo BYTE o WORD puede ser usada como un puntero de memoria, usándola como argumento de la función POINTER. El valor contenido en la variable debe estar comprendido entre 0 y 511. A continuación, algunos 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 más 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
SYMBOL ENCENDIDO = 1

Luego, si queremos encender el LED, en lugar de

PORTB.0 = 1

podemos hacer

LED1 = ENCENDIDO

que es mucho más 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 pueda 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

Leer el estado de un pulsador

Con todo lo visto en mente, vamos a ver cómo hacer para leer el estado de un pulsador. Deberemos ver primero cómo conectarlo al PIC. En el esquema (figura 1) que hay más abajo puede verse cómo conectarlo. Veamos cómo funciona eléctricamente: cuando el pulsador está abierto, el pin del PIC está puesto a tierra (masa, o 0V) a través de la resistencia de 10K (figura 2) que llamamos R1, por lo que el bit correspondiente a ese pin (por ejemplo, PORTA.7) se pondrá en “0”.

TUTORIAL: Programación de microcontroladores - Entrega 3
TUTORIAL: Programación de microcontroladores - Entrega 3

Cuando presionamos el pulsador, la corriente circulará como se ve en la figura 3, pasando por el pulsador y entrando al PIC por el pin en cuestión. El bit correspondiente se pondrá en “1”. Antes de que me olvide, una parte de la corriente casi despreciable irá también a masa a través de R1, pero a fines prácticos no lo tenemos en cuenta. La función de esa resistencia es que no se produzca un cortocircuito entre +V y masa cuando presionamos el pulsador.

Hay un tema a tener muy en cuenta, y lo haremos en la quinta o sexta entrega, que es el denominado “rebote” que se produce en los contactos del pulsador. Por ahora despreciaremos ese efecto.

¿Cómo debería ser el programa que pueda “leer” el estado del pulsador conectado al bit 7 del PORTA? Así:

AllDigital

TRISA.7 = 1 'Defino PORTA.7 como ENTRADA
TRISA.6 = 0 'Defino PORTA.6 como SALIDA

Symbol pulsador = PORTA.7
Symbol led = PORTA.6

loop:
led = pulsador
Goto loop

Analicemos el programa: ALLDIGITAL indica al compilador que se deben emplear todos los pines del PORTA como E/S. Las dos líneas siguientes usan la función TRIS para definir el pin 7 del PORTA como ENTRADA (poniendo ese bit en “1”) y el pin 6 del mismo puerto como SALIDA.

Las líneas SYMBOL declaran dos nombres simbólicos para que el programa quede más claro. Es obvio que esto tiene más utilidad en programas extensos, pero es bueno ir acostumbrándonos a usarlo siempre.

El resto del programa conforma un bucle que se repite eternamente, ejecutando la única línea existente entre LOOP: y GOTO LOOP. En ella se asigna al pin cuyo nombre simbólico es LED (PORTA.6) el valor que tome el pin de entrada llamado PULSADOR (PORTA.7). Esto hará que el LED “copie” el estado del pulsador. Si utilizamos el entrenador de 18 pines que ya explicamos cómo construir, veremos que cada vez que pulsamos el pulsador, el LED se enciende hasta que lo soltamos. No se trata de una aplicación demasiado útil, pero es suficiente para ilustrar el funcionamiento de las entradas.

La próxima semana usaremos un módulo conectado al entrenador que contiene 8 LEDS y 8 pulsadores para poder crear programas más complejos. Hasta entonces.