You are on page 1of 28

Aprendiendo a Programar

Aprendiendo a Programar
Introducci n Hace aproximadamente tres a os, comenzamos a mostrar c mo se deben escribir programas en nuestra querida revista Saber Electr nica; fue as que tanto Federico Prado, el Ing. Alberto Picerno y este autor hemos escrito art culos con la explicaci n de programas sencillos que le permitan aprender a utilizar las instrucciones del PIC 16X84. En este cap tulo utilizaremos la misma t cnica comenzando desde el principio, bas ndonos en prototipos reales.

Circuito de un Entrenador
En Saber Electr nica hemos desarrollado un entrenador para aprender a programar y cargar PICs, este entrenador se muestra en la figura 1.
Figura 1

Microcontroladores PIC

97

Aprendiendo a Programar
Sea el programa: ; Primer programa de pr ctica List equ org movlw tris movlw nop goto p = 16C84 0x06 h0 h0 ptob h 0f ciclo ; voy a utilizar el PIC16C84
; inicializo la variable ptob en ; la direcci n 06h

ptob Reset Inicio

; comienza el programa ; cargo a W con 0


; mando el contenido de W a ptob

ciclo

; cargo a W con el n mero binario 15 ; rutina nula ; vaya a ciclo

El programa comienza con un (;), por lo tanto, lo que sigue en el rengl n es tomado como un comentario. Los signos = forman un resalte para indicar el inicio del programa y ayudan a darle una distribuci n agradable a la vista. El programa se lista en cuatro columnas; la primera sirve para colocar las variables que utilizaremos como registros y las etiquetas que son ubicaciones del programa ad nde se debe ir cuando el operando de una instrucci n as lo requiera. En la segunda columna se coloca la instrucci n y en la tercera el operando de la instrucci n. La cuarta columna siempre va precedida de (;) y se utiliza para colocar observaciones que le sirvan al programador como gu a para saber qu quiso hacer o qu funci n cumple esa sentencia, obviamente, al compilar esa instrucci n, las observaciones no son tenidas en cuenta. List p = 16C84 Es el encabezado del programa que le indica al ensamblador qu tipo de PIC se est utilizando para que ste pueda reconocer qu set de instrucci n debe utilizar. ptob equ 06 Nombr a la variable ptob y la coloqu en la direcci n 06 de la RAM, esta direcci n est reservada para el PUERTO B , es decir, ratifico que ptob es el registro del PORT B. Cuando, m s adelante, deba enviar informaci n al puerto b, s lo debo mencionar ptob. Reset org 0 Significa que al realizarse el reset, el programa comienza por la posici n 0 de la memoria del programa. Pero de inmediato pasa a la posici n 1 que tiene

98

Microcontroladores PIC

Aprendiendo a Programar
escrita la siguiente sentencia: Inicio movlw 0 Con esto se carga el registro w con el hexadecimal 0 (es decir el binario 00000000) tris ptob Esta instrucci n env a la informaci n del registro W al puerto B para indicarle que todos sus pines son de salida (si se hubiera cargado el binario 11111111 todos los pines ser an de entrada y si se hubiera cargado 11001010 algunos ser an entradas y otros salidas). movlw 0f Carga el registro W con el hexadecimal 15 que equivale al binario 00001111 y movlw ptob Env a el valor cargado al puerto "B" que producir un estado alto en RB0, RB1, RB2 y RB3 y un estado bajo en RB4, RB5, RB6 y RB7. La informaci n del puerto pasa al buffer que lleva las patas 15, 16, 17 y 18 de un PIC16C84 a masa encendiendo los leds D7, D8, D9 Y D10. Ciclo nop Realiza una rutina nula, es decir, que no efect a operaci n alguna. goto ciclo Env a el programa hacia la etiqueta "ciclo". Las dos ltimas operaciones hacen que al ejecutarse un programa, ste se quede en un lazo que se llama loop cerrado . La nica manera de salir de este loop es pulsando RESET. Entonces se observa que los leds se apagan hasta que se suelta el pulsador y el programa comienza nuevamente por la etiqueta RESET. Obviamente, este programa debe ser editado en un utilitario adecuado (MPLAB, por ejemplo) y luego debe ser compilado (utilizando el MPASM o el mismo MPLAB) para obtener el archivo .hex que me permitir cargar el PIC que deber colocar en el circuito de la figura 1 para verificar que realmente hace lo que estamos diciendo. Para cargar el PIC con el programa .hex se utiliza un prototipo adecuado (cargador de PICs) que consiste en un circuito que es manejado por un programa para permitir la carga. Todo este proceso se explica con total claridad en el primer texto de esta serie, titulado: Todo Sobre PICs .

Microcontroladores PIC

99

Aprendiendo a Programar
En la figura 2 se puede observar un diagrama de flujo que refleja el funcionamiento del programa que acabamos de explicar. Cu nto tardan en encenderse los leds luego de soltar el bot n de reset? Si se observa el circuito, se ver un cristal de clock de 4MHz (0,25 S de per odo); como internamente existe un divisor x4 cada operaci n se realizar en 0,25 x 4 = 1 S. Si contamos las sentencias hasta llegar a cargar el puerto B, veremos que hay 5 (cinco renglones de programa); por lo tanto, la demora es de 5 S.

Fig. 2

Encendido Intermitente de un Led


Si Ud. no quiere armar el entrenador completo (que sirve para un sinf n de aplicaciones que se ense an en el Curso: Todo Sobre PICs , que est en venta en varios pa ses de Am rica a trav s de la red de distribuidores de Editorial Quark y Centro Japon s - vea en InterFig. 3 net la direcci n: www.webelectronica.com.ar) le proponemos que arme s lo lo imprescindible para encender los leds seg n el circuito de la figura 3; un circuito tan simple que no requiere plaqueta, puede armarse tipo ara a sobre la mesa de trabajo,

100

Microcontroladores PIC

Aprendiendo a Programar
porque no es algo para mostrar sino para aprender. Observe que los leds est n conectados directamente al puerto B predispuesto como de salida por el programa. En efecto, el puerto de salida admite perfectamente los 3mA que toma cada led al conectarlos con resistores en serie de 1k5. Como nica precauci n para el armado debe utilizar un z calo de 18 patas para el PIC. Las conexiones de X1, C1 y C2 deben ser lo m s cortas posibles. Si usted repara TV y videograbadores seguramente tendr cristales de 3,58MHz (de PALN o NTSC o inclusive de PALM). Puede utilizarlos sin mayor inconveniente pero recuerde que los tiempos calculados estar n afectados de un coeficiente igual a 3,58/4. Nuestro programa va a hacer uso de lo que se llama una subrutina. Una subrutina es como un segundo programa anexo al principal. El programa principal va a la subrutina a trav s de una instrucci n "CALL" que no hab amos empleado hasta ahora. Esta instrucci n salta del paso del programa donde se encuentra, a la subrutina y cuando se ejecuta por completo vuelve al mismo punto del programa en donde se hab a producido el salto. Una subrutina debe estar siempre dirigida a una etiqueta para que el salto se realice con un destino bien determinado. Por lo general, las subrutinas se escriben a continuaci n del programa principal, pero esto es simplemente una costumbre, bien podr an ubicarse en otro lugar como el principio. Recuerde que todo comienza cuando se pulsa reset o cuando se enciende el equipo y, en ese momento, se ejecuta lo indicado con la etiqueta reset y se salta al rengl n siguiente. Cuando termina una subrutina, se debe colocar una referencia "RETURN", que significa retornar al siguiente rengl n del programa principal donde est la invocaci n a la subrutina. Otra instrucci n nueva que no hab amos utilizado es "decfsz" que siempre va acompa ada de un n mero hexadecimal. Esta instrucci n se utiliza para programar retardos. Por ejemplo, si su paso del programa fuera: CONST equ decfsz 06 CONST

En el encabezado se declara que la variable CONST estar en la direcci n 06 luego, al ejecutarse la instrucci n el contenido de la variable CONST se ha decrementado en una unidad. Esto parece m s una complicaci n sin sentido, pero no es as ; en un programa bien estructurado los cambios se realizan en el encabezado y, adem s, muchas veces es necesario cambiar solidariamente varios operandos y el m todo propuesto lo logra f cilmente. Tambi n vamos a utilizar una instrucci n de doble uso y que forma parte de

Microcontroladores PIC

101

Aprendiendo a Programar
una subrutina (generalmente la cierra). Se trata de "retlw" que debe ir acompaado de un literal (n mero hexadecimal). Esta operaci n carga el registro w con el literal que lo acompa a y retorna al programa principal todo en un solo rengl n. Su funcionamiento se apreciar mejor cuando veamos el programa de aplicaci n pero es equivalente a: movlw return 0x12 ; cargo a W con el n mero 12h

A esta altura Ud. debe recordar unas cuantas sentencias y todav a faltan unas cuantas m s (recuerde todo lo visto en el cap tulo 2). Como ayuda para que no deba estudiar todas las sentencias de memoria le decimos que lo mejor es relacionarlas con palabras comunes del idioma ingl s. Por ejemplo "equ" proviene de "equal" que significa igual. En la figura 4 le damos una tabla con m s ayudas.
Fig. 4

Para dise ar un programa usted debe tener en claro la funci n del proyecto. En nuestro caso queremos que al encender la fuente el led conectado a RB0 se encienda y apague con un tiempo que pueda modificarse f cilmente para alterar el programa. Al apretar el pulsador de reset se debe apagar el led y al soltarlo se encender luego de un retardo y continuar alternando per odos de encendido y apagado.

102

Microcontroladores PIC

Aprendiendo a Programar
El programa completo lo vamos a dividir en dos partes: un programa principal y una subrutina que llamamos retardar . Los hacemos as porque casi todos los programas incluyen retardo y el dise o de esa subrutina lo podemos emplear posteriormente y nos ahorraremos trabajo. Un programa debe estar estructurado en forma clara, se deben diferenciar claramente los distintos sectores y esto se hace no s lo para que sea agradable a la vista, sino para facilitar los futuros cambios. La primera secci n es el t tulo y la declaraci n de qu PIC se va a utilizar. La segunda es el encabezado en donde se le da nombre a los literales (n meros hexadecimales) m s importantes. La tercera es el programa principal y la cuarta las subrutinas:
;=========================================================== ; Secuenciador de encendido de un led list p=16C84 ;micro PIC16C84 ;=========================================================== portb equ 06 ;el puerto b se ubica en la direccin de ;memoria RAM06 reg1 equ 0c ;asignacin de los registros donde se cargan los reg2 equ 0d ;diferentes literales reg3 equ 0e ; grueso equ 40 ;al literal 40 lo llamamos grueso medio equ 40 ;al literal 40 lo llamamos medio fino equ 50 ;al literal 50 lo llamamos fino ;=========================================================== reset org 0 ;un reset enva a la posicin 00 de la memoria ;de programa. Goto comienzo ;reenvo al comienzo del programa ;===========================================================

Analicemos el encabezado, las primeras l neas hasta la etiqueta portb ya las conocemos del programa anterior (aqu empleamos portb en lugar de ptob). Luego tenemos tres sentencias que le dan nombre reg1, reg2 y reg3 a tres registros de uso general, ubicados en las posiciones de memoria RAM destinadas a los registros. Recuerde que las primeras 12 posiciones de memoria hasta 0B est n ocupadas por registros fijos. A partir de 0C se ubican los registros de prop sitos generales de los cuales usaremos 3, a saber: 0C, 0D y 0E (que son los registros que hemos denominado reg1, reg2 y reg3). En esos registros volcamos inicialmente los literales 30, 40 y 50 que nombramos como grueso , medio y fino . Esto se realiza en las tres ltimas sentencias del encabezado que son aquellas que Ud. debe variar si desea que el

Microcontroladores PIC

103

Aprendiendo a Programar
ritmo de encendido se haga m s lento o m s r pido. Desde luego que estas tres sentencias no son imprescindibles pero si no se usan, luego habr a que buscar dentro de la subrutina para cambiar los literales. Trabajando de esta manera es m s f cil introducir cambios porque los hacemos desde el encabezado. La secci n principal del programa es muy similar a la utilizada en el primer programa para encender permanentemente un led:
reset comenzar ppal org goto movlw tris movlw movwf call movlw movwf call goto 0 comenzar 00 ptob 01 ptob demora 00 ptob demora ppal ;un reset enva el programa a la posicin 0 ;se enva el control al comienzo ;se carga w con 00 ;se programa el puerto b como salida ;se carga w con 01 ;se descarga w en el puerto b para ;encender el led conectado RB0 ;se mantiene el led encendido por ;un intervalo de tiempo ;se carga w con 00 ;se descarga w en el puerto b para ;apagar el led de RB0 ;se mantiene el led apagado por ;un intervalo de tiempo ;continua el programa en el principio ;hasta realizar un reset

Siempre se comienza a analizar el programa observando qu ocurre al realizar un reset. En este caso, vemos que todo comienza en la posici n 00 de la memoria de programa. En la siguiente posici n se env a el control del programa a la posici n comenzar , en donde se predispone el puerto b para operar como salida. Luego, comienza el programa principal en el etiqueta ppal, en donde se carga el registro w con el n mero hexadecimal 01. Cuando este n mero se descarga en el puerto b se enciende el led conectado en RB0. Ahora se env a el control a la subrutina demora porque pretendemos que el led quede encendido algo menos de un segundo. Cuando se cumpla ese tiempo volver el control desde demora continuando por la carga de w con el hexadecimal 00. Al descargar w en el punto b se produce el apagado de led conectado en RB0. Nuevamente el control pasa a la subrutina demora, en donde se queda por algo menos de 1 segundo antes de volver al programa principal. Cuando vuelve es enviado de inmediato al comienzo de ppal con un nuevo encendido de RB0 y as permanentemente hasta que se opere el reset. Es el turno de platicar sobre la subrutina demora. Esta subrutina es m s complicada que el programa principal. Lo que ocurre es que la dise amos pa-

104

Microcontroladores PIC

Aprendiendo a Programar
ra uso general, ya que la vamos a aplicar en una infinidad de programas. En principio, la demora la vamos a poder variar con tres n meros hexadecimales que fueron definidos en el encabezamiento como grueso , medio y fino porque operan de ese modo sobre el retardo final:
demora movlw movwf dem3 movlw movwf dem2 movlw movwf decfsz grueso reg1 medio reg2 fino reg3 reg3 ;recarga w con el nmero hexadecimal ;grueso es decir, con 30 ;se vuelca grueso en el registro ;reg 1 ;se carga w con el nmero hexadecimal ;medio es decir con 40 ;se vuelca "medio" en el registro ;reg2. ;se carga w con el nmero hexadecimal "fino" ;es decir con 50. ;se vuelca "fino" en el registro reg3. ;se decrementa el registro reg3 en una unidad ;y si el resultado es cero se salta a la siguiente ;instruccin, si no se sigue decrementando. ;retorno a la frecuencia etiqueta dem1. ;idem con reg2. ;retorno a la etiqueta dem2. ;idem con reg1. ;retorno a la etiqueta dem3. ;se carga w con 0 y se retorna al programa ;principal

dem1

goto decfsz goto decfsz goto retlw end

dem1 reg2 dem2 reg3 dem1 0

La subrutina comienza cargando los registros de uso general con los n meros hexadecimales declarados en el encabezado. Luego que los tres registros especiales est n cargados con los hexadecimales 30, 40 y 50 se comienza a decrementar el registro 3 de a una unidad; mientras el registro tenga un n mero mayor o igual a 1 se contin a este proceso, pero al llegar a cero se salta a la siguiente sentencia que justamente es un "goto". Es como si el control retornara y fuera a dem1 tantas veces como lo indica el n mero hexadecimal cargado en el registro 1. Luego la subrutina pasa a la siguiente sentencia que comienza a decrementar el n mero cargado en reg2, s lo que el "goto" es a la etiqueta dem2 que es anterior a la dem1, es decir, que se vuelve a pasar por "dem1" a hacer todo el primer decremento del n mero guardado en "reg3" y reci n despu s se hace el siguiente decremento en el registro "reg2". Cuando "reg2" llega a cero se produce el primer decremento de "reg3" y para que se produzca el segundo tienen que decrementarse a cero "reg2". En una palabra, que la subrutina da much simas vueltas sobre "dem 1", muchas sobre "dem2"

Microcontroladores PIC

105

Aprendiendo a Programar
y pocas sobre "dem3". Finalmente cuando "reg3" llega a cero, se pasa retlw que reenv a el control al programa principal justo en la sentencia que invoc a la subrutina pero antes de retornar lleva el registro w a cero. El programa termina en "end" que no puede considerarse como una sentencia, ya que no realiza ninguna tarea; sin embargo, su existencia es imprescindible para que el programador de PICs d por terminada su tarea. Cuando no se coloca el programa ensamblador emite un mensaje de error. Tenga en cuenta que le estamos ense ando a programar , es decir, a utilizar las instrucciones del set del PIC. Si Ud. es programador, estos ejemplos le van a parecer muy torpes, dado que el experto sabe c mo utilizar rutinas que permitan hacer lo mismo con menos esfuerzo de programaci n. Ud. est aprendiendo como cuando comenz a sumar y en aquella oportunidad seguramente le costaba entender que 1 + 1 es igual a dos, aunque hoy le parezca un ejemplo muy burdo. Volviendo a nuestro programa, sabemos que la subrutina "demora" tiene una cierta duraci n. Con un m nimo conocimiento de matem ticas podemos calcular ese retardo en funci n de los n meros hexadecimales que hallamos elegido. Tomemos primero el "loop" (rulo) m s interno de la subrutina; mientras no se cumpla que "reg 3" = 0 se consume un ciclo de instrucci n por vuelta (gen ricamente podemos decir que todas las sentencias consumen un ciclo salvo las de salto como la "goto" que consume dos). N mero de ciclos "reg 3" = 3 x "grueso" = 3 x 80 (ya que 50 Hex = 80 decimal) = 240 ciclos de instrucci n Para calcular la demora de los dos loops siguientes es conveniente realizar un diagrama de los mismos tal como el mostrado en la figura 5. Ahora vemos que el loop intermedio dura 2 + 1 + 1 + 1 = 5 ciclos de instrucci n a los cuales se les suman los 240 ciclos del loop m s interno. En total ser an 5 + 240 = 245. Pero este loop es recorrido hasta que el hexadecimal 40 (equivalente al decimal 64) se anule: n mero de ciclos "reg 2" = 245 x medio = 245 x 64 = 15.860 ciclos de instrucci n. El loop m s externo dura 2 + 1 + 1 + 1 = 5 ciclos de instrucci n a los cuales se les suman los 15.860.

106

Microcontroladores PIC

Aprendiendo a Programar

Fig. 5

Pero este loop es recorrido hasta que el hexadecimal 40 (equivalente al decimal 64) se anule: n mero de ciclos "reg 1" = 15.860 x fino = 15.860 x 64 = 1.015.040 ciclos de instrucci n. Nos quedar an 1 + 1 ciclos al comienzo de la subrutina y 2 ciclos al final para llegar a un valor de 1.015.044 ciclos de instrucci n. La duraci n de un ciclo de instrucci n depende del cristal utilizado. Si usamos un cristal de 4MHz cada ciclo de instrucci n dura 1 s y tendr amos una demora de 1,015 seg. El led estar a encendido casi un segundo y apagado otro tanto. El funcionamiento del programa principal y la subrutina de demora pueden representarse en un diagrama de flujo como el mostrado en la figura 6 para el programa principal.
Fig. 6

Microcontroladores PIC

107

Aprendiendo a Programar

Secuenciador de 4 Canales
Elegimos trabajar con cuatro leds para que usted pueda entender el tema con m s facilidad. Pero todo lo que veremos puede aplicarse a un secuenciador de 8 leds. Nuestro dispositivo enciende cuatro leds en secuencia de manera que nunca est n encendidos todos al mismo tiempo. En el ejemplo anterior vimos primero el programa y luego el diagrama de flujo. En realidad el m todo es el inverso. Un diagrama de flujo permite visualizar el funcionamiento de un programa y por lo tanto debe efectuarse previamente. Por ejemplo, en la figura 7 puede ver el diagrama de flujo correspondiente a la rutina demora del programa que permite el encendido intermitente de un led. El programa es la interpretaci n del diagrama de flujo; en el programa se plasman los detalles que no tiene el diagrama de flujo. Para construir el programa de nuestro secuencial, nos basamos en el diagrama de flujo de la figura 8. Observe que luego de iniciado, el proceso no se detiene jam s, ya que terminada la secuencia, vuelve a comenzar. Obviamente, la aplicaci n de un reset apagar todos los leds mientras se mantenga el reset pulsado. En este programa se van a utilizar nuevas instrucciones que debemos detallar. Entre otras utilizamos la instrucci n movf que se acompa a con un doble operando. Es una sentencia que sirve para realizar transferencias en el contenido de cualquier registro nombrado como primer operando al registro W. Otra instrucci n nueva es rlf, cuya fun-

Fig. 7

Fig. 8

108

Microcontroladores PIC

Aprendiendo a Programar
ci n es desplazar el contenido de un registro nombrado como operando. El contenido se desplaza hacia la izquierda y el primer bit vuelve a ingresar como ltimo. Finalmente se utiliza tambi n la instrucci n btfss que incluye dos operandos; el primer operando indica en qu registro se realiza la operaci n y el segundo que posici n del registro se analiza (en que bit, del bit 0 al bit 7). La operaci n consiste en analizar la posici n siguiente a la nombrada como segundo operando. Por ejemplo: btfss rot,4

Aqu se analiza el estado de 5 bit (bit 4, dado que el primer bit o bit menos significativo es el bit 0), si ste es cero se continua con la sentencia siguiente , si es 1 se saltea en la siguiente secuencia. En la figura 9 se realiza un dibujo que explica estas ltimas instrucciones.
Fig. 9

Microcontroladores PIC

109

Aprendiendo a Programar
Imag nese a movf como si volcara el contenido de un balde en otro, se lo asimila a un transvasamiento. La sentencia rlf la puede imaginar como si apretara un registro con el dedo puesto por la derecha del mismo; el bit que se cae por la izquierda lo toma con la mano y lo pone por la derecha donde qued un espacio vac o. Vea a btfss como si estuviera con la mano sobre el quinto bit. Si esa posici n esta fr a, mande el control de programa al rengl n siguiente. Si est caliente, saltee el que sigue y mande el control al posterior a ste. Bien, el programa del secuenciador es en realidad una modificaci n del programa de encendido y apagado de un led. Como vemos, los programas dif cilmente se inicien desde cero siempre se utilizan otros programas que se modifican y adem s se utilizan subrutinas completas como la "demora":
; SECUENCIADOR list P=16C84 ;el dispositivo usa el 16C84 ;------------------------------------------------------------------------------------------ptob equ 06 ;el puerto f se ubica en 06 de la RAM. Rot equ 0d ;al registro ubicado 0d se lo llama est. Reg1 equ 0e ;al registro ubicado en 0e se lo llama reg1 reg2 equ 0f ;al registro ubicado en 0f se lo llama reg1 reg3 equ 10 ;al registro ubicado en 10 se lo llama reg3 grueso equ 00 ;se asignan nombres a los literales. medio equ 40 ;se asignan nombres a los literales. fino equ 50 ;se asignan nombres a los literales. ;------------------------------------------------------------------------------------------reset org 0 ;reset en direccin 00h goto comenzar ;se lleva el control a comienzo comenzar movlw 00 ;se carga w con 00h tris ptob ;se programa el puerto "b" como salida. ppal movlw 01 se carga w con 01h movwf rot ;se descarga w en el registro rot ;preparndolo para la rotacin. sec movf rot,0 ;el contenido de rot pasa a w movwf ptob ;enciende el led conectado donde indica w call demora ;se mantiene encendido el led. rlf rot ;se mueven los bits de "rot". btfss rot,4 ;se comprueba el 5 bit. goto sec ;si el 5 bit es cero se reenva a sec. goto ppal ;si el 5 bit es uno se reenva a ppal para ;comenzar una nueva secuencia.

110

Microcontroladores PIC

Aprendiendo a Programar
demora movlw movwf dem3 movlw movwf dem2 movlw movwf dem1 decfsz grueso reg1 medio reg2 fino reg3 reg3 ;recarga w con el nmero hexadecimal ;grueso es decir, con 30 ;se vuelca grueso en el registro ;reg 1 ;se carga w con el nmero hexadecimal ;medio es decir con 40 ;se vuelca "medio" en el registro ;reg2. ;se carga w con el nmero hexadecimal ;"fino" es decir con 50. ;se vuelca "fino" en el registro reg3. ;se decrementa el registro reg3 en una ;unidad y si el resultado es cero se salta a ;la siguiente instruccin, si no se sigue ;decrementando. ;retorno a la frecuencia etiqueta dem1. ;idem con reg2. ;retorno a la etiqueta dem2. ;idem con reg1. ;retorno a la etiqueta dem3. ;se carga w con 0 y se retorna al ;programa principal.

goto decfsz goto decfsz goto retlw end

dem1 reg2 dem2 reg3 dem1 0

Todo comienza cargando el registro w con el hexadecimal 01, es decir que el registro se llenar con el binario correspondiente 00000001. Este contenido se transvasa a "rot" y luego devuelta a "w", desde donde se lo env a al puerto "b" para encender el ltimo led. Ahora el control del programa se env a a demora para que el led permanezca encendido cerca de 1 segundo. A continuaci n se produce la rotaci n de los bits de "rot" con lo cual queda cargado con el binario 00000010. Ahora se comprueba el estado del quinto bit; como es 0 se reenv a el control a "sec , se carga "w" con el contenido de "rot" se transvasa "rot" a "w" y luego se env a "w" al "ptob", entonces el segundo led se enciende y as sucesivamente, hasta que se enciende el cuarto led. En ese momento, al hacer la rotaci n se lleva el quinto bit a 1, momento en que se saltea el goto "sec", se hace un goto a ppal y comienza una nueva secuencia. Si lo analizamos globalmente, lo nico que hace el programa es cargar el puerto w con los binarios 00000001; 00000010; 00000100; 00001000 y realizar un cambio cada segundo aproximadamente. Sugerimos que cambie inicialmente la subrutina demora para que la secuencia sea m s r pida. Luego lo invitamos a realizar un cambio mayor, que

Microcontroladores PIC

111

Aprendiendo a Programar
implique secuenciar 8 leds en lugar de 4, y por ltimo lo invitamos a construir una guirnalda para un arbolito de navidad. Lo nico que se modifica en este caso es que con el sistema simplificado, se requiere manejar un solo led. Para manejar una cantidad mayor, le sugerimos el circuito de la figura 10.
Fig. 10

Generador de Se ales de Audio


El programa que reci n vimos puede utilizarse para generar se ales de audio con forma de escalera, que son el medio id neo para comprobar la linealidad de los amplificadores de potencia de audio. En efecto, una escalera de 8 escalones de 1kHz de frecuencia aproximada, nos permite probar si la amplificaci n es lineal. Basta con usar un osciloscopio y medir si los 8 escalones generados son id nticos. En funci n del escal n deformado, se puede luego determinar en qu parte del amplificador se encuentra la falla. Por ejemplo, los escalones centrales se deforman cuando falla el circuito de corriente vac o. Una falla en los escalones extremos significa que no funcionan correctamente los circuitos de boostrap. (fi-

112

Microcontroladores PIC

Aprendiendo a Programar
gura 11). Esta forma de onda es tambi n ideal para controlar la respuesta en frecuencia del amplificador de potencia, sin necesidad de realizar varias mediciones con un oscilador de audio. Un amplificador sobrecompensado en frecuencia (realce de agudos) producir sobreimpulsos en los escalones y uno subcompensado (falta de agudos) redondear los frentes (figura 12).
Fig. 11

Fig. 12

La fabricaci n de un generador escalera nos da motivo para estudiar el manejo de llaves anal gicas con un PIC. Esta es una de las utilidades m s importantes del PIC y se usa en una gran cantidad de aplicaciones. Por ejemplo, un uso frecuente es la codificaci n y decodificaci n de se ales de TV. En este proyecto usaremos dos llaves CD4066 para conmutar 8 valores de tensi n de salida, que forman una se al escalera de 8 pelda os y 3,5V de amplitud pico a pico. El circuito se muestra en la figura 13. Los resistores R15 a R23 forman cada pelda o de la escalera y su valor debe ser sumamente exacto. Si no puede conseguir resistores al 1%, los debe conseguir con resistores en paralelo medidos con un buen t ster digital. Una

Microcontroladores PIC

113

Aprendiendo a Programar
Fig. 13

vez armada la serie y con CI1 fuera del z calo se deben controlar las tensiones continuas en las uniones de los resistores con un t ster digital. Los resistores R6 a R9 y R11 a R14 se utilizan como reparadores para facilitar el service. En el uso normal no cumplen funci n especial alguna. El reset es autom tico al encender la fuente de 5V a trav s de R10 y C3 que demoran el crecimiento de la tensi n sobre el terminal 4 (MCLR ). El puerto A se conecta con resistores de 1k a los +5 V para evitar captaciones espurias. La salida de se al en escalera se produce sobre la conexi n com n de salida de las llaves anal gicas y se aten a con el fin de reducir la impedancia de salida mediante R24 y RV1. La tensi n de salida m xima es de 100mV pap aproximadamente. La nica modificaci n que requiere el programa es la modificaci n de las constantes literales para que cada escal n dure 12 S. (8 escalones duraran 8 x 125 = 1000 S = 1mS). Si repasamos la secci n de la subrutina demora del art culo anterior veremos que cada instrucci n simple demora 1 S y que el loop definido con "FINO" dura 3 x FINO S. En nuestro caso, debemos buscar un n mero hexadecimal cuyo equivalente anal gico sea 125. Lo m s simple es recurrir al tanteo, usando hexadecimales que terminen en cero; sabiendo que el hexadecimal 10 corresponde al anal gico 16, podemos deducir que el anal gico 20 corresponde al 32, el 30 al 48 y as sucesivamen-

114

Microcontroladores PIC

Aprendiendo a Programar
te, ir multiplicando el hexadecimal x 1,6. Si 3 x FINO = 125 S implica que FINO = 125/3 = 41,6 anal gico, que dividido por 1,6 es 26. Aproximamos al hexadecimal 30, ya que con ste tendremos que el loop "FINO" demorar 3 x 30 Hex = 3 x 48 = 144 S. La frecuencia obtenida ser de F = 1/(144 x 8) = 868Hz que se aproxima a la buscada. Si desea mayor precisi n ajuste el literal "FINO". Sintetizando: el programa debe variarse haciendo que fino valga 30 y medio y grueso sean ambos iguales a 00. Piense que el circuito propuesto puede tener muchas posibilidades de modificarse a voluntad y adaptarse a otros usos. Por ejemplo, si en lugar de conectar las entradas de las llaves a tensiones continuas, las aplican a se ales alternas provenientes de 8 osciladores sinusoidales, se puede hacer un barredor de audio. Si reduce la frecuencia de repetici n puede construir un generador musical de acordes de 8 notas, similares a los utilizados como bocina musical en autom viles y etc., etc., etc. Piense en otras posibilidades m s. Los Formatos de los Archivos Mencionamos el programa MPASM, que es un ensamblador que transforma el programa dise ado por nosotros en un idioma hexadecimal correspondiente al PIC. El programa generado a mano (el que escribe el programador, en base a las pautas dadas en este cap tulo) tienen una extensi n ASM (de ASEMBLER" o ensamblador) y cuando lo aplic bamos al MPASM se generaban tres archivos con terminaciones diferentes .err donde se encuentran los mensajes de error, si se cometi un error de sintaxis al escribir el archivo .asm; un archi.asm vo .lst con los renglones numerados para ubicar en cual se produce el error y el m s importante, el archivo .hex que contiene los datos que supuestamente entiende el microprocesador. En realidad del microprocesador no entiende el archivo con terminaci n .hex. El necesita otro programa que transforma el .hex en un lenguaje binario .hex de datos en serie que es lo nico que entiende el microprocesador PIC, sto es lo que hace un PROGRAMADOR o cargador de PICs.

Secuenciador de 8 Canales
Veamos un programa diferente al propuesto para rotar el encendido de 4 leds. Este programa sirve para encender 8 leds en sucesi n de la figura 3 y con algunos cambios para excitar las llaves anal gicas del generador de escalera que acabamos de analizar:

Microcontroladores PIC

115

Aprendiendo a Programar

En la primera sentencia se observa la declaraci n del microprocesador utilizado. Como puede observar en lugar de utilizar el 16C84, como lo hicimos hasta ahora, utilizamos el 16F84. La diferencia entre ambos radica en el tipo de memoria utilizada. El 16F84 contiene una memoria m s r pida del tipo EEPROM FLASH que se puede reescribir un mill n de veces. El almacenamiento de esta informaci n est garantizado por el fabricante por un lapso mayor a 40 a os con el dispositivo sin alimentaci n. El 16C84 es totalmente similar, pero contiene una memoria EEPROM com n, de acceso m s lento y con menores garant as de mantenimiento de la informaci n grabada. Tambi n se puede utilizar el 16F83 similar al 16F84 pero con la mitad de capacidad de memoria y por lo tanto m s barato. Cualquiera de los tres integrados puede alimentarse con tensiones de 4 a 6V. En el sector entre l neas punteadas, se declara donde se van a ubicar 3 variables que utiliza el programa, llamadas ptob, j y k. Aqu se puede observar que la posici n de memoria puede declararse de diferentes modos. En el primer rengl n s lo escribimos 06. Cuando no se coloca el n mero entre acentos, ni se coloca una letra adelante, se interpreta que se trata de un n mero hexadecimal. Es decir que el programa ensamblador (el MPASM) interpreta un n mero "por defecto" como si fuera hexadecimal. En el segundo y tercer rengl n se utiliza un criterio diferente. La letra H significa que el n mero entre acentos ser hexadecimal; en este caso particular se utilizar n las posiciones de memoria 1F y 1E para guardar a las variables j y k. En la carpeta reset se ubica la parte del programa que se ejecuta s lo cuan-

116

Microcontroladores PIC

Aprendiendo a Programar
do se presiona el pulsador de reset. Se trata de 5 renglones o sentencias de las cuales la primera ubica el control en la posici n de memoria de programa cero. En la segunda se carga el registro de trabajo w con el n mero cero. Observe que en este caso utilizamos otra notaci n diferente; la letra B indica que a continuaci n se colocar un n mero binario de 8 d gitos (el PIC empleado s lo puede manejar hasta 8 bits). En la siguiente sentencia se vuelca el contenido del registro de trabajo en ptob, que en el copete se declar ubicado en la posici n de memoria 06. Esta posici n de memoria controla la disposici n del puerto "B" como de salida (aclaramos que el uso del comando TRIS generar un mensaje de precauci n pero que por ahora no debemos tener en cuenta). En la sentencia 4 de la carpeta "reset", se vuelve a cargar el registro de trabajo con el binario "00000001"; esto no implica que se realice ninguna modificaci n en la disposici n de puertos, ya que una vez cargada la posici n de memoria, sta no se modificar hasta que se produzca un reset. El registro w se utiliza para cargar informaci n nueva y ubicarla luego donde se desea. Por ejemplo, en la sentencia 5 se descarga w en el puerto de salida B, que con TRIS se declar como de salida. Esto significa que se va a encender el led ubicado en la pata 6 del mismo (B0). Aqu termina la carpeta reset y no se volver a ella hasta que se oprima reset o se desconecte y reconecte al microprocesador. La siguiente carpeta se llama "mciclo" y comienza con el comando "rlf" utilizado para mover posteriormente el bit puesto en uno del puerto B, una posici n hacia la izquierda. De cualquier modo al ejecutar rlf no se producir todav a el cambio de estado en las salidas. En la segunda sentencia de la carpeta "mciclo" se carga el registro de trabajo, pero ahora utilizamos otra notaci n diferente. La letra D indica que vamos a cargar un n mero decimal y luego entre acentos decimos que ese n mero ser el 50 (esto es adecuado para usar el microprocesador como generador de onda en escalera, pero debe cambiarse por 500 si se va a utilizar como secuenciador; como vemos este n mero determina la duraci n de los escalones o el tiempo de encendido de cada led). En la tercera sentencia se descarga el n mero 50 en la variable "j" que antes se declar ubicada en la posici n de memoria 1F. Aqu empieza la carpeta "jciclo" de una sola sentencia, en donde el mismo n mero 50 se vuelca en la variable "k" que antes se declar ubicada en la posici n de memoria 1E. La siguiente carpeta llamada "kciclo" se utilizar para decrementar estos n meros 50 ubicados en 1F y en 1E de modo de establecer una demora necesaria para que los escalones o el encendido de los leds dure el valor que nosotros impongamos. La primer sentencia de la carpeta "kciclo", decrementa k en una unidad. El

Microcontroladores PIC

117

Aprendiendo a Programar
comando "decfsz" es del tipo condicional; si el valor de k es superior a cero (1 a 50 en nuestro caso), el control del programa va a la sentencia siguiente que es incondicional y reenv a el control de programa a la carpeta "kciclo". Cuando k llega al valor cero, env a el control a la sentencia posterior a "goto", obviando el bucle "kciclo". En una palabra que los dos primeros renglones de la carpeta "kciclo" decrementan el n mero 50 hasta cero y como cada sentencia dura 1 seg, ya que estamos usando un cristal de 4MHz, podemos calcular que este bucle del programa dura 100 seg. En la tercera sentencia se decrementa la variable "j", del mismo modo que se hiciera con "k" pero se retorna a la carpeta "jciclo" para generar un nuevo bucle de 100 seg. Esto implica que los dos bucles tienen una duraci n de 100 x 100 seg, es decir: 10.000 seg o 10mseg (con 500, cada bucle dura 1000 seg y el producto es 1.000.000 de seg o 1seg). Transcurrido este tiempo el control pasa a la quinta sentencia de la carpeta "kciclo" que es una sentencia incondicional goto que env a el control a la carpeta "mciclo". Reci n en este momento se produce la rotaci n de ptob, se apaga el primer led y se enciende el siguiente por otro lapso de tiempo de 1seg. El programa ir cambiando el binario acumulado en el puerto B, correr el 1 a la izquierda hasta que salga del registro y vuelva a ingresar por la derecha.

Compilaci n de un Programa
Cuando uno tiene escrito el programa con el edit del DOS, debe verificarlo exhaustivamente, controlar el encolumnado y los errores de sintaxis antes del punto y coma separador. De cualquier modo siempre se puede deslizar un error que provocar una falla en el programa. Si usted trabaja correctamente, el programa ensamblador MPASM le avisar de los errores y le dar algunas advertencias tiles sobre el uso de algunas sentencias. Para ver c mo funciona sto, volvamos a escribir el programa del secuencial de 8 canales modific ndolo de modo que presente algunos errores:

118

Microcontroladores PIC

Aprendiendo a Programar

A continuaci n se abri el MPASM y se ejecut el archivo modificado con intenci n de obtener el archivo HEX que requiere el programador de pics. Pero si lo solicitamos se pueden generar los archivos .err y .lst que ser n de gran utilidad para encontrar errores de tipeo (si no sabe c mo se realiza este proceso, lea el texto: Todo Sobre PICs , de Editorial Quark). Veamos c mo se produce la limpieza de un programa con errores. Al ejecutar el archivo con F10 del MPASM, el ensamblador ofrece un resultado como el indicado en la figura 14. Luego se muesFig. 14 tra la pantalla graficada en la figura 15 donde se indican 5 errores y 6 precauciones, adem s de la indicaci n de la cantidad de l neas totales ensambladas (30 en este caso). Ahora se deben encontrar las l neas con errores y para eso se debe
Fig. 15

Microcontroladores PIC

119

Aprendiendo a Programar
editar el archivo .ERR con el mismo editor de DOS que estamos utilizando. Por cada error o advertencia hay una l nea de respuesta que comienza con "WARNING" (advertencia) o ERROR (error) y un n mero de c digo por el que se pueden obtener m s detalles sobre el error o la advertencia, luego el nombre de archivo analizado con el rbol de directorios correspondiente y el n mero de l nea que contiene el error o advertencia. Luego un separador dos puntos y el tipo de error o advertencia. Los mensajes de advertencia no impiden el desarrollo de la posterior programaci n, pero los mensajes de error s . Vamos a atender primero los mensajes de error y luego los de advertencia. No es necesario numerar las l neas de programa a mano, y a veces no es conveniente, porque la falta de un separador (punto y coma) puede generar l neas fantasmas y confundirnos. Es preferible editar el archivo .lst (en nuestro caso SECU8NO.LST) que tiene al mismo tiempo los mensajes de error y el n mero de l nea. Ver figura 16. En la primera columna se marcan los errores o precauciones y la localizaci n y valor de las variables. En la segunda, el n mero de l nea y a continuaci n la l nea de programa. El primer error lo tenemos indicado antes de la l nea 00005: y nos indica que las variables o s mbolos LA y VARIABLE no est n definidos previamente y que se produzco un cambio de operador. Mirando la l nea se observa que el nico problema es que falta el separador de comentarios y el ensamblador confundi el comentario con una sentencia. El siguiente error est en la l nea 12 y nos indica que el error se produce despu s de la primera columna, entre par ntesis se encuentra el argumento u orden equivalente (molw por movlw). En la l nea 22 est el siguienFig. 16 te error y consiste en la

120

Microcontroladores PIC

Aprendiendo a Programar
orden GOTO escrita como GORO. Veamos ahora los mensajes de advertencia. El primero est en la primer l nea y se produce porque esta l nea est dirigida al programador y no puede ser entendida por el ensamblador. Se puede dejar sin inconvenientes. La siguiente est en la l nea 12 y seguramente va a desaparecer cuando se corrija la orden movlw. Luego pasamos a la l nea 13 en donde est la orden TRIS; ocurre que en los microprocesadores m s modernos esta orden se modifica por otra m s compleja; por ahora se puede seguir usando TRIS para programar los puertos si se usa el 16C84 o el 16F84. La siguiente precauci n est en la l nea 18 y se debe a que el decimal 50000 est fuera del rango admitido por el microprocesador. Esta precauci n debe ser considerada modificando el valor por uno menor. La siguiente precauci n est en la l nea 22 y se corregir al reemplazar GORO por GOTO. Al corregir los errores y agregar el separador, el MPASM va a reportar s lo dos advertencias. Una en la l nea 1 por la declaraci n del microprocesador dirigida al programador de PICs y la otra es la referida a la orden TRIS que puede ser ignorado por el momento.

Carga del Programa en un PIC


Para programar un PIC se deben conectar varias de sus patas en una disposici n determinada que hace accesible su memoria para recibir datos serie desde el puerto paralelo de su PC. El puerto paralelo de la PC, en donde usted conecta habitualmente la impresora, tiene varias v as de conexi n, cuyo estado puede modificarse por medio de un programa adecuado. Con un programa adecuado como el NOPP Ud. podr a variar el estado de la pata 14 del puerto paralelo de la PC en una r pida sucesi n que se corresponda con la informaci n serie necesaria para cargar el PIC. En una palabra que cada hilo de un puerto paralelo puede transformarse en un puerto serie. Cabe aclarar que este programa, de la empresa Gernsback, puede bajarlo de Internet de la direcci n: ftp://ftp.gernsback.com/pub/EN/noppp.zip En el momento de grabar un PIC se lo debe disponer seg n lo indicado en la figura 17. Las conexiones de fuente y las se ales deben ser aplicadas seg n una secuencia predeterminada que debe respetarse invariablemente:
A) El primer paso es colocar el PIC en el z calo del programador con se ales y fuentes a potencial de masa. B) Levantar la tensi n de fuente VDD a un potencial de 5V 0,2V por la pata 14 (VDD). C) Levantar la tensi n de fuente VPP a un potencial de 13V 0,3V por la pata 4 (MCLR NEGADO).

Microcontroladores PIC

121

Aprendiendo a Programar
Fig. 17

D) Esperar en esas condiciones un tiempo superior a 1mS. E) Posicionar el primer dato en la pata 13 (RB7) con un potencial alto (mayor a 4V) o bajo (menor a 1V). F) Cuando la pata 12 (RB6) pase a un estado alto, superior a 4V, el dato se carga en la memoria. G) Continuar cargando los datos con el mismo criterio a un ritmo tal que el dato est presente por lo menos durante 100nS. H) Cuando todos los datos fueron cargados se debe esperar 1 segundo. I) Desconectar la fuente de 13V. J) Desconectar la fuente de 5V. K) Retirar el micro grabado.

Por sobre todas la cosas, debe respetarse el criterio de no sacar un PIC de su z calo con las fuentes conectadas porque puede desprogramarse o, peor a n, daarse definitivamente. Tambi n es muy importante respetar las tensiones de fuente y no demorar la llegada de datos, luego conectar VPP (porque podr an cargarse datos falsos por interferencias electromagn ticas). Como usted puede observar de la descripci n anterior la entrada RB6 es utilizada como clock y la RB7 como entrada de datos en una cl sica operaci n de transferencia de datos en serie. Hay un tiempo entre el momento en que MCLR (MCLR NEGADO) sale de la condici n de reset hasta que aparece el primer dato o el primer pulso de clock. Tambi n hay un tiempo m nimo para la permanencia de un dato en la entrada (la representaci n usada para un dato significa que el mismo puede ser alto o bajo, depende del bit que se est grabando). Lo que no hay es un tiempo m ximo pero evidentemente cuando mayor es este tiempo m s lenta ser la carga del programa. El tiempo m s adecuado depende de factores tales como el largo del cable utilizado para conectar el programador a la PC. Si el cable es largo, los pulsos tienden a deformarse y atenuarse, sobre todo cuando son de corta duraci n (100nS equiva-

122

Microcontroladores PIC

Aprendiendo a Programar
len a una frecuencia de 10MHz). Para evitar problemas todos los programadores trabajan a velocidades inferiores a la m xima, sobre todo considerando que la capacidad de memoria no es muy grande y para el uso casero no son imprescindibles grandes velocidades de grabaci n. Luego de grabar un PIC se puede verificar que su programa haya sido correctamente grabado. Esto se realiza utilizando la misma pata 13 del PIC (RB7) que, como sabemos, es de I/O (Input/Output ; entrada/salida). La transformaci n del modo de escritura a lectura no requiere cambios de hardware; es decir que vale lo indicado en la figura 17. El micro detecta los primeros bit emitidos para saber si debe grabar o leer y luego transforma RB7 en puerto de salida para poder leer los datos grabados. As contin a todo hasta el final de la lectura de datos. La mejor prueba para saber si la grabaci n es exitosa consiste en modificar la tensi n de la fuente VDD desde 4 a 6V. Primero se debe realizar una verificaci n a 5V exactos, luego se debe pasar a 6V y realizar una nueva verificaci n; por ltimo ubicar la fuente en 4V y realizar la ltima prueba. Esta verificaci n es sumamente importante porque un PIC mal grabado puede presentar fallas en su funcionamiento o peor a n puede funcionar correctamente al principio pero borrarse en pocos d as. Vamos ahora a adaptar nuestro programador para que funcione con el software NOPP. Las diferencias entre un programador y otro muchas veces se deben a la utilizaci n de diferentes patas del puerto paralelo de la PC. En efecto el software del programador puede elegir diferentes patas para utilizarlas como entradas y salidas de datos serie. Tambi n es posible que se utilicen otras patas para generar las tensiones de control de fuentes y, por ltimo, algunos software s lo controlan la fuente VPP; que es justamente el caso que nos ocupa. Por ltimo los programadores m s completos utilizan una fuente regulada que puede ajustarse entre 4 y 6V para comprobar la efectiva grabaci n de los datos. En la figura 18 realizamos dos modificaciones. Por un lado agregamos la llave LL1 para operar la fuente de 5V a mano. Cuando la llave est cerrada Q1 conduce y el micro est alimentado con 5V por la pata 14. La acci n de la llave se controla por el encendido del led verde. Por lo tanto, usted debe colocar el PIC en el z calo o retirarlo s lo cuando el led verde est apagado. Con referencia a la fuente agregamos un regulador LM317 en lugar del 78L5 que tiene la posibilidad de poder programar su tensi n de salida. En efecto el preset RV1 en un extremo debe ajustar la salida en 4V y en el otro lo debe ajustar en 6V. Tanto la secci n de fuente como el volt metro necesitan ajustes a realizar con un t ster digital. La fuente requiere el ajuste de R21. Comience colocando resistores de 1k; luego debe variar ambos valores hasta que la salida cambie entre los l mites

Microcontroladores PIC

123

Aprendiendo a Programar
Fig. 18

especificados de 4 y 6V. En el volt metro se deben ajustar los resistores de 27 y 22k para que el led se encienda en el rango de 4,8 a 5,2V. Con referencia a los cambios en el puerto paralelo preferimos modificar el cable de conexi n dejando el conector de la plaqueta sin modificaciones. Observe que si la plaqueta est desconectada o la fuente est apagada el software NOPP reconoce este estado a trav s de la pata 5 de la ficha DB9 que est a potencial alto por medio de R9 y aborta la operaci n de carga. El transistor Q2 que controla la fuente VPP se maneja a trav s de CI1 desde la pata 1 de la ficha DB9. Pero la fuente de 5V deber manejarse a mano por intermedio de la llave LL1. *************************************************** Con este tema damos por finalizada esta obra que complementa al texto: Todo Sobre PICs que puede conseguir a trav s de la red de distribuidores de Editorial Quark y Centro Japon s. Aclaramos que en estos momentos estamos elaborando el tercer texto de la serie que explica la construcci n y programaci n de Sistemas con PICs para que Ud. pueda construir instrumentos electr nicos, codificadores y decodificadores de se ales de TV, aut matas programables, emuladores y un sinf n de dispositivos.

124

Microcontroladores PIC

You might also like