Introducción

Siempre ha sido tema de debate la eleccion del ensamblador o un compilador de alto nivel como lenguaje de programacion. Cada herramienta tiene sus ventajas e inconvenientes. Siempre habra aplicaciones donde no sea necesaria la eficiencia del ensamblador o la flexibilidad de un compilador, asi como aplicaciones que sí demanden de ambas facilidades. Sea como sea, un buen disenador debe estar preparado para emplear el ensamblador como eventual recurso. Una razon de gran peso que quiero anadir por la que tarde o temprano se debe aprender el ensamblador es que la mayor parte (casi toda) la informacion proporcionada por Microchip en los datasheets, notas de aplicación o manuales de referencia sobre sus PICmicros se incluyen codigos en ensamblador.

Estructrura del programa en ensamblador
Vamos a analizar el primer programa, el que hace parapadear un LED.
;**************************************************************************** ; FileName: BlinkLed.asm ; Processor: PIC16F84A ;**************************************************************************** list p = PIC16F84A ; Seleccionar procesador #include <P16F84A.inc> ; incluir archivo P16F84A.inc __config _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF cont1 cont2 Start bsf bcf bcf MLoop bsf call bcf call goto PORTB, 0 pausa PORTB, 0 pausa MLoop STATUS, RP0 TRISB, 0 STATUS, RP0 equ equ org 0x0C 0x0D 0x00 ; Ubicar cont1 en dirección 0x0C ; Ubicar cont2 en dirección 0x0D ; ; ; ; ; ; ; ; ; ; ; Código ejecutable empieza aquí Iniciar RP0 = 1. Cambiar al Banco 1 Configurar pin RB0 como salida RP0 = 0. Cambiar al Banco 0 Bucle principal Prender el led Llamar pausa Apagar el led Llamar pausa Saltar a MLoop

;**************************************************************************** ; Subrutina pausa. Genera 325 milisegundos aprox. pausa movlw d'100' ; Mover 100 a W movwf cont1 ; Mover W a cont1 (cargar cont1) movlw d'255' ; Mover 255 a W movwf cont2 ; Mover W a cont2 (cargar cont2) loop1 decfsz cont1, f ; decrementar cont1, saltear si da 0 goto loop2 ; saltar a loop2 decfsz cont2, f ; decrementar cont2, saltear si da 0 loop2 goto loop1 ; saltar a loop1

return end

; retornar de subrutina ; Fin del código

Visto el código de izquierda a derecha podemos notar que sobresalen cuatro columnas. (Los colores ayudan mucho, aunque no determinan nada.)
y

y y

y

y y

La primera columna es la que contiene las etiquetas y también algunas directivas. Las etiquetas sirven para marcar las direcciones de las instrucciones a las que preceden. Pueden ser útiles para algunas instrucciones y en otros casos están de adorno. Por ejemplo, la etiqueta Start vale por la dirección de bsf STATUS, RP0. El nombre de las etiquetas es un identificador. Pueden opcionalmente ir seguidas de dos puntos (:). En este programa tenemos las etiquetas Start, MLoop, pausa, loop1 y loop2. En la segunda columna están los mnemónicos de las instrucciones, como bsf, movlw, etc. (en el ejemplo aparecen de color marrón), macros o directivas del ensamblador. Las directivas (cuyos nombres aparecen de color azul) no son instrucciones del PIC ni constituyen código ejecutable. Son ordenes que el ensamblador tendrá en cuenta en el momento de ensamblar el código fuente. Tienen diversas funciones y ayudan bastante, como lo veremos en adelante. Ahora, lo que puede resultar desalentador es la cantidad de directivas que existen. De la enorme lista disponible solo un grupo de ellas son realmente elementales e iremos conociéndolas de a poco. La lista inicial indispensable está formada por list, include, __config, banksel, equ, org, end, define, cblock, endc, dt. La tercera columna está constituida básicamente por los operandos de instrucciones, como STATUS, RP0; TRISB; pausa; etc. En la cuarta columna sobresalen los comentarios. Un comentario es todo lo que sigue en el renglón a un punto y coma (;) y será ignorado por el ensamblador (en el listado es todo lo que se ve de color verde). Digamos que son texto de adorno que solo sirve para documentar el programa. De hecho, los comentarios pueden ponerse en cualquier parte del listado.

Ésa es una forma algo burda de estructurar un programa, pero ayuda muchísimo. Ahora pasaremos a revisarlo de arriba abajo. Los identificadores son los nombres que podemos usar para identificar ciertos elementos del código fuente, como registros GPR, etiquetas, macros, etc. Los nombres se forman a criterio del usuario, usando los carecteres alfanuméricos disponibles y los signos _ y ?, respetando las siguientes reglas:
y y y y y

No debe haber dos indentificadores iguales. No deben empezar con números. Ejemplo, 1n4148. No deben comenzar con dos rayas bajas seguidas. Ejemplo, __var. No deben incluir espacios. Ejemplo, contador 1. No deben ser palabras reservadas, como directivas o mnemónicos. Ejemplo, include, processor, retlw, etc.

A diferencia de los mnemónicos, por defecto el ensamblador distingue entre caracteres mayúsculas y minúsculas.

Set de instrucciones de los PIC de familia Mid-Range
Hasta el momento hemos visto la operación de varias de las instrucciones del PIC. Como habrás notado, son bastante fáciles de usar. Ahora, antes de continuar y para no tener que seguir explicándolas al vuelo, conoceremos el resto de las 35 instrucciones de los PICs Mid-range. Es un alivio saber que estos PICs tengan tan pocas instrucciones, haciendo todo el honor a su procesador de arquitectura RISC (Reduced Instruction Set Computer). Es probable que en el futuro también estudiemos otros módulos, dedicados a otros microcontroladores, aparte de los PICmicros. La mayoría de ellos, aunque también tienen un diseño RISC, poseen cerca de 100 instrucciones. Así que ponle ganas y termina cuanto antes este capítulo.
Convención de términos
y y y y y

f w b k d

Cualquier registro (SFR o GPR) de la RAM del PIC Work register o Registro de trabajo Posición de bit Campo literal, dato constante o etiqueta Selección de destino; d = 0: el resultado se almacena en W d = 1: el resultado almacenar en el registro F

y y y y y y

PC Program Counter o Contador de Programa TOS Top Of Stack o Cima de la Pila < > Posición de bit dentro de un registro TO Bit de Time-Out PD Bit de Power-down Label Nombre de una etiqueta

Notación de números en ensamblador
y y y y y

Hexadecimal: 0x15, H45, h7A Decimal: D'41', d'156 ', .25 Binario: B'11110010', b'01010000' Octal: O'32', o'77' Ascii: 'A', 'a', 'b'

Expresiones adoptadas

Para fines de documentación vamos a emplear algunos signos o expresiones que resumen el significado de alguna operación, como:
y y y y y

Se utiliza para indicar la transferencia de un dato. Por ejemplo: 0x00 (TRISB) Significa cargar el dato 0x00 en el registro TRISB . ( ) Se utiliza para referenciar el contenido de un registro. Por ejemplo: (W) (PORTB) Significa el contenido del registro W se pasa al registro PORTB . = A pesar de la sencillez de estos símbolos, yo prefiero emplear el operador =, propia de los lenguajes de alto nivel. Solo debes acostumbrarte a que la transferencia del dato se dirige de

significa el bit Z será igual a 0. . Aunque en la documentación de Microchip se adoptan los signos < > para indicar la posición de un bit dentro de un registro. /PD 1 Z 1 Z 1 or 2 --2 --1 Z 1 or 2 --1 Z 1 Z 1 --- . Z = 0. d decf f. cnt = PORTB + 0x06. PORTB = PORTA. Por ejemplo PORTB. significa PORTA invierte su valor. saltear si da 0 Saltar a k Incrementar F Incrementar F. PORTB = PORTB + 1. d goto k Incf f. d decfsz f. d bcf f. significa PORTB es incrementado en 1.. d iorlw k iorwf f. TRISB = 0x00.2 = 0. saltear si es 0 Testear bit en F. PORTA = ~ PORTA. TRISA. significa limpiar el bit 2 del registro PORTB. Z 1 C. Puedes notar que se prescinde de la expresión el contenido del registro. saltear si da 0 OR Inclusiva entre literal y W OR Inclusiva entre W y F Mover literal a W CiclosStatus Afectado 1 C. Z 1 Z 1 Z 1 --1 --1 or 2 --1 or 2 --2 --1 Z 1 Z 1 /TO. b bsf f. b btfsc f. INTCON. Por ejemplo. significa PORTB valdrá igual que PORTA.y y y derecha a izquierda y entender que el signo = no implica una ecuación matemática (donde los dos miembros de la expresión son iguales de antemano). solo significa que el valor del segundo miembro es asignado al primero. DC. d andlw k andwf f. b call k clrf f clrw clrwdt comf f. significa setear el bit 5 del registro TRISA. aquí también prefiero cambiar a otra notación. DC. C = 1. Operandos addlw k addwf f.. significa cnt será la suma de PORTB y la constante 0x06. significa TRISB pasará a valer 0x00. punto en este caso. d movlw k Descripción Sumar literal con W Sumar W con F And entre literal y W And entre W y F Limpiar bit en F Setear bit en F Testear bit en F.GIE = 0. significa el bit C será igual a 1. . significa limpiar el bit GIE del registro INTCON. b btfss f. saltear si es 1 Llamar subrutina Limpiar F Limpiar W Limpiar Watchdog Complementar F Decrementar F Decrementar F. Resumen de instrucciones de los PIC16F84A y PIC16F87xA Mnemónicos.5 = 1. ya que se asume que de eso se trata. d incfsz f.

d swapf f. d y CiclosStatus Afectado Mover F 1 Z Mover W a F 1 --No operación 1 --Retornar de interrupción 2 --Retornar con literal en W 2 --Retornar de subrutina 2 --Rotar a izquierda mediante Carry 1 C Rotar a derecha mediante Carry 1 C Entrar en modo Standby 1 /TO. d xorlw k xorwf f. PORTB = PORTB + W Después de instrucción PORTB = 0x1E W = 0x36 C = 1 . d sleep sublw k subwf f. k y el resultado es Ejemplo 1: ADDWF PORTB. d movwf f nop retfie retlw k return rlf f. Z Restar W de F 1 C. /PD Restar W de literal 1 C.Mnemónicos. DC. Operandos movf f. Z Intercambiar nibbles de F 1 --OR exclusiva entre literal y W 1 Z OR exclusiva entre W y F 1 Z Descripción Descripción de instrucciones de los PIC16F84A y PIC16F87xA ADDLW Sintaxis: Operandos: Operación: Status Afectado: Descripción: Add Literal and W ADDLW k 0 ” k ” 255 (W) + k (W) C. W = REG + W Después de instrucción REG = 0x05 W = 0xF7 C = 0 . El resultado no es cero . No hubo desbordamiento Z = 0 . DC. Z El contenido del registro W se suma al literal de ocho bits colocado en el registro W. W Antes de instrucción REG = 0x05 W = 0xF2 C = ? Z = ? . DC. Ocurrió un desbordamiento Z = 0 . El resultado no es cero . F Antes de instrucción PORTB = 0xE8 W = 0x36 C = ? Z = ? Ejemplo 2: ADDWF REG. d rrf f.

No hubo desbordamiento Z = 0 .1] (W) AND (f) (W) . W = W AND 11110000 Después de instrucción W = 0x00 Z = 1 . Descripción: Si d = 0. W = REG + W Después de instrucción REG = 0x05 W = 0xF7 C = 0 . El resultado no es cero Ejemplo 1: ADDWF PORTB. W Antes de instrucción REG = 0x05 W = 0xF2 C = ? Z = ? ANDLW Sintaxis: Operandos: Operación: Status Afectado: Descripción: And Literal with W ANDLW k 0 ” k ” 255 (W) AND k (W) Z Aplica la operación AND entre el literal de 8 bits µk¶ y el registro W. W = W AND 00001111 Después de instrucción W = 0x04 Z = 0 . Ocurrió un desbordamiento Z = 0 . . el resultado se almacena en el registro µf¶. El resultado no es cero . El resultado no es cero . F Antes de instrucción PORTB = 0xE8 W = 0x36 C = ? Z = ? Ejemplo 2: ADDWF REG. Z Sumar el contenido del registro W con el registro µf¶. PORTB = PORTB + W Después de instrucción PORTB = 0x1E W = 0x36 C = 1 . d 0 ” f ” 127 d [0. El resultado es cero Ejemplo 1: ANDLW b'00001111' Antes de instrucción W = 0x34 Z = ? Ejemplo 2: ANDLW b'11110000' Antes de instrucción W = 0x0D Z = ? ANDWF Sintaxis: Operandos: Operación: And W with f ANDWF f. Si d = 1. d Operandos: 0 ” k ” 255 Operación: (W) + (f) (destino) Status Afectado:C. El resultado es colocado en W.Add W and f ADDWF Sintaxis: ADDWF f. . DC. el resultado se almacena en el registro W.

W = REG AND W Después de instrucción REG = 01010101 W = 01010000 Z = 0 . F Antes de instrucción STATUS = 11111111 W = 01010101 Z = ? Ejemplo 2: ANDWF REG. STATUS = STATUS AND W Después de instrucción STATUS = 01010101 W = 01010101 Z = 0 . Si d = 1. Resultado no cero Ejemplo 1: ANDWF STATUS. b 0 ” f ” 127 0”b”7 Operación: 1 (f<b>) Status Afectado:Ninguno . Descripción: Si d = 0. . el resultado es almacenado en el registro µf¶. Ejemplo 1: BCF REG. 6 Antes de instrucción REG = 01101001 Ejemplo 2: BCF REG. REG.And W with f ANDWF Status Afectado:Z AND entre el registro W y el registro µf¶. Resultado no cero .1 = 0 Después de instrucción REG = 00001001 Descripción de instrucciones de los PIC16F84A y PIC16F87xA BSF Sintaxis: Operandos: Bit Set f BSF f. REG.6 = 0 Después de instrucción REG = 00101001 . 1 Antes de instrucción REG = 00001001 . W Antes de instrucción REG = 01010101 W = 11110000 Z = ? BCF Sintaxis: Operandos: Bit Clear f BCF f. el resultado es almacenado en el registro W. b 0 ” f ” 127 0”b”7 Operación: 0 (f<b>) Status Afectado:Ninguno Descripción: Limpiar (poner a cero) el bit µb¶ del registro µf¶.

REG 0xFF siguiente instrucción si RB0 = 1 0x20 = W . Si el bit µb¶ en el registro µf¶ es µ0¶. MOVLW BTFSS MOVLW MOVWF 0xFF PORTB. REG. entonces la siguiente instrucción es descartada y en su lugar se ejecuta un NOP. Saltear siguiente instrucción si RB0 = 0 IsOne GOTO Sub1 . entonces la siguiente instrucción es descartada y en su lugar se ejecuta un NOP. 0 0x20 REG . Ejemplo 1: Here BTFSC PORTB. entonces se ejecuta la siguiente instrucción. haciendo de ésta una instrucción de 2 ciclos. Saltar a Sub1 si RB0 = 1 IsZero GOTO Sub0 . b 0 ” f ” 127 0”b”7 Saltear si (f<b>) = 0 Ninguno Si el bit µb¶ en el registro µf¶ es µ1¶. 0 Antes de instrucción REG = 00010000 .BSF Descripción: Ejemplo 1: Bit Set f Setear (poner a uno) el bit µb¶ del registro µf¶.5 = 1 Después de instrucción REG = 10100011 . 5 Antes de instrucción REG = 10000011 BSF REG. W = .0 = 1 Después de instrucción REG = 00010001 Ejemplo 2: BTFSC Sintaxis: Operandos: Operación: Status Afectado: Descripción: Bit Test f. Skip if Set BSF f. W = . entonces se ejecuta la siguiente instrucción. Saltear . b 0 ” f ” 127 0”b”7 Saltear si (f<b>) = 1 Ninguno Si el bit µb¶ en el registro µf¶ es µ0¶. PORTA. PC = Address (IsZero) Si RB0 = 1. Si el bit µb¶ es µ1¶. Saltar a Sub0 si RB0 = 0 Antes de instrucción Después de instrucción PC = Adress (Here) Si RB0 = 0. BSF PORTA. 0 . Skip if Clear BTFSC f. PC = Address (IsOne) BTFSS Sintaxis: Operandos: Operación: Status Afectado: Descripción: Ejemplo 1: Bit Test f. haciendo de ésta una instrucción de 2 ciclos.

F Antes de instrucción TRISB = ? Z = ? . REG = 0x20 Call subroutine CALL Sintaxis: CALL k Operandos: 0 ” k ” 2047 (PC) + 1 TOS (Top of the stack) PC<10:0> Operación: k (PCLATH<4:3>) PC<12:11> Status Ninguno Afectado: Llamar subrutina. Los bits superiores de PC se cargan desde PCLATH. Llamar subrutina ³There´ Después de instrucción PC = Address (There) TOS = Address (Here + 1) Clear f CLRF f 0 ” f ” 127 0x00 (f) Operación: 1 Z Status Afectado:Z Descripción: Se limpia el contenido del registro µf¶ y se setea el bit Z. TRISB = 00000000 Después de instrucción TRISB = 00000000 Z = 1 . Resultado es cero Descripción de instrucciones de los PIC16F84A y PIC16F87xA CLRW Sintaxis: Operandos: Operación: Clear W CLRW Ninguno 0x00 (W) 1 Z . REG = 0xFF Si RB0 = 0. Los Descripción: once bits inmediatos de la dirección se cargan en los bits <10:0> del PC.Antes de instrucción REG = ? Después de instrucción Si RB0 = 1. Primero se salva en la Pila la dirección de retorno (PC + 1). Ejemplo 1: Here CALL There Antes de instrucción PC = Address (Here) . CALL es una instrucción de dos ciclos. CLRF Sintaxis: Operandos: Ejemplo 1: CLRF TRISB.

Después de instrucción WDT counter = 0x00 WDT prescaler = 0 /TO = 1 /PD = 1 Ejemplo 1: CLRWDT Antes de instrucción WDT counter = ? /TO = ? /PD = ? COMF Sintaxis: Operandos: Complement f COMF f . Si d = 1. También se resetea el prescaler del WDT. Result is zero CLRWDT Sintaxis: Operandos: Operación: Status Afectado: Descripción: Clear Watchdog Timer CLRWDT Ninguno 0x00 WDT 0 WDT prescaler 1 /TO 1 /PD TO. F Antes de instrucción PORTA = 11110000 Z = ? Ejemplo 2: COMF PORTA. d 0 ” f ” 127 d [0. W Antes de instrucción PORTA = 01010101 W = ? Z = ? .Clear W CLRW Status Afectado:Z Descripción: Se limpia el registro W y se setea el bit Z.1] Operación: (f) (destino) Status Afectado:Z El contenido del registro µf¶ es complemetado a 1. PORTA = ~PORTA Después de instrucción PORTA = 00001111 Z = 0 . Resultado no cero . W = 0x00 Después de instrucción W = 00000000 Z = 1 . . W = ~PORTA Después de instrucción PORTA = 10101010 W = 10101010 Z = 0 . Descripción: Si d = 0. Clear the Watchdog timer. Resultado no cero Ejemplo 1: COMF PORTA. . Ejemplo 1: CLRW Antes de instrucción W = ? Z = ? . el resultado se almacena en el registro W. el resultado se queda en el registro µf¶. Los bits de Status /TO y /PD se setean. PD Resetea el temporizador Watchdog (WDT).

1 Después de instrucción Count = 0x53 Z = 0 . . W Antes de instrucción Count = 0x01 Z = ? DECFSZ Sintaxis: Operandos: Operación: Status Afectado: Decrement f. Count = Count . PC = Address(Here + 1) . Si d = 1. Si el resultado es 0. F . PC = Address(Here + 2) Si Count != 0. el resultado se almacena en el registro W. d 0 ” f ” 127 Operandos: d [0. haciéndola una instrucción de 2 ciclos. Ninguno Se decrementa el contenido del registro µf¶. el resultado se queda en el registro µf¶. el resultado se queda en el registro µf¶. Resultado es cero Ejemplo 1: DECF Count. el resultado se almacena en el registro W. W = Count . Si el resultado no es 0. d 0 ” f ” 127 d [0. Saltar a Loop si el resultado no es 0 Continue ... F Antes de instrucción Count = 0x54 Z = ? Ejemplo 2: DECF Count. Descripción: Si d = 0. El programa salta aquí si el resultado es 0 Antes de instrucción Después de instrucción PC = Address (Here) Si Count = 0.1 (destino) Saltear si el resultado es 0. DECF Sintaxis: Descripción: Si d = 0. Saltear si resultado es 0 GOTO Loop . Si d = 1. Skip if Zero DECFSZ f .1] (f) .1] Operación: (f) . Ejemplo 1: Here DECFSZ Count.1. Count = Count .1 (destino) Status Afectado:Z Decrementar el registro µf¶.1 Después de instrucción Count = 0x00 Z = 1 .Decrement f DECF f . . entonces en su lugar se ejecuta un NOP. se ejecuta la siguiente instrucción. Resultado no es cero .

Skip if Zero INCFSZ f . d 0 ” f ” 127 Operandos: d [0.Descripción de instrucciones de los PIC16F84A y PIC16F87xA GOTO Sintaxis: Operandos: Operación: Status Afectado: Unconditional Branch GOTO k 0 ” k ” 2047 k PC<10:0> (PCLATH<4:3>) PC<12:11> Ninguno Goto es un salto incondicional. Ejemplo 1: Here GOTO There Antes de instrucción PC = Address (Here) . Branch to ³There´ Después de instrucción PC = Address (There) Increment f INCF f . F Antes de instrucción Count = 0x89 Z = ? INCFSZ Sintaxis: Operandos: Operación: Status Afectado: Increment f. d 0 ” f ” 127 d [0. Saltear si el resultado es 0. . INCF Sintaxis: Descripción: Si d = 0.1] (f) + 1 (destino). Count = Count + 1 Después de instrucción Count = 0x8A Z = 0 . Si d = 1. El valor de los once bits inmediatos de la dirección se Descripción: cargan en los bits <10:0>. Los bits superiores del PC se cargan desde PCLATH<4:3>. el resultado se almacena en el registro W. GOTO es una instrucción de 2 ciclos. Ninguno .1] Operación: (f) + 1 (destino) Status Afectado:Z Se incrementa en contenido del registro µf¶. Resultado no es cero Ejemplo 1: INCF Count. el resultado se queda en el registro µf¶.

Count = Count + 1. El resultado queda en el registro W. Saltar a Loop si resultado no es 0 Continue .INCFSZ Increment f.. Saltear si resultado es 0 GOTO Loop . el resultado se queda en el registro µf¶. F Antes de instrucción INTCON = 0x00 . . PC = Address(Here + 2) Si Count != 0. se ejecuta la siguiente instrucción. . INTCON = W OR INTCON Después de instrucción INTCON = 0x10 Ejemplo 1: IORWF INTCON. Si d = 0. Todos los registros de la RAM son cíclicos. Si el resultado es 0. . Si el resultado no es 0. d Operandos: 0 ” f ” 255 Operación: (W) OR (f) (destino) Status Afectado:Z OR inclusiva entre el registro W y el registro µf¶. Si d = 1.. esto es. PC = Address(Here + 1) IORLW Sintaxis: Operandos: Operación: Status Afectado: Descripción: Inclusive OR Literal with W IORLW k 0 ” f ” 255 (W) OR k (W) Z Se aplica un OR entre el contenido del registro W y los ocho bits del literal µk¶. El programa salta aquí si el resultado es 0 Antes de instrucción Después de instrucción PC = Address (Here) Si Count = 0. haciéndola una instrucción de 2 ciclos. entonces se ejecuta un NOP. W = W OR 00001111 Después de instrucción W = 0x3F Z = 0 . F . Si d = 1. el resultado se almacena en el registro W. después de un incremento en 0xFF se producirá un desbordamiento y se convertirá en 0x00. el resultado se almacena en el registro W. Descripción: Si d = 0. Skip if Zero Se incrementa el contenido del registro µf¶. W = W OR 11110000 Después de instrucción W = 0xFD Z = 0 . Resultado no es cero . el resultado se queda en el registro µf¶. Descripción: Ejemplo 1: Here INCFSZ Count. Resultado no es cero Ejemplo 1: IORLW b'00001111' Antes de instrucción W = 0x34 Z = ? Ejemplo 2: IORLW b'11110000' Antes de instrucción W = 0x0D Z = ? Inclusive OR W with f IORWF Sintaxis: IORWF f.

el destino es el registro W. Ejemplo 1: MOVLW 0xFF Antes de instrucción W = ? Ejemplo 2: MOVLW 'A' Antes de instrucción W = ? . dado que afecta el bit de Status Z. d 0 ” f ” 255 d [0. W = 'A' Después de instrucción W = 01000001 MOVF Sintaxis: Operandos: Operación: Status Afectado: Descripción: Move f MOVF f. el destino es el mismo registro µf¶. W Antes de instrucción INTCON = 0x11 W = ? . Resultado no es cero Descripción de instrucciones de los PIC16F84A y PIC16F87xA Move Literal to W MOVLW Sintaxis: MOVLW k Operandos: 0 ” k ” 255 Operación: k (W) Status Afectado:Ninguno Descripción: El literal de ocho bits µk¶ se carga en el registro W.1] (f) (destino) Z Mover el contenido del registro ¶f¶ a un destino que depende del estado de d. W = INTCON Después de instrucción INTCON = 0x11 W = 0x11 Ejemplo 1: MOVF INTCON. W = 0xFF Después de instrucción W = 0xFF . Resultado no es cero Ejemplo 2: IORWF REG.W Z = 0x10 = ? W Z = 0x10 = 0 . . Si d = 1. W = W OR REG Después de instrucción REG = 01010101 W = 11111111 Z = 0 . d = 1 se usa para testear un registro. Si d = 0. W Antes de instrucción REG = 01010101 W = 10101010 Z = ? .

Z = ? Ejemplo 2: MOVF Cnt. Después de instrucción PC = TOS . Solo consume un ciclo RETFIE Sintaxis: Operandos: Operación: Status Afectado: Descripción: Ejemplo 1: Return from interrupt RETFIE Ninguno TOS PC 1 GIE Ninguno Retorno de interrupción. Resultado es cero Move W to f MOVWF Sintaxis: MOVWF f Operandos: 0 ” f ” 127 Operación: (W) (f) Status Afectado:Ninguno Descripción: Mover el dato desde el registro W al registro µf¶. Rehabilitar global de las interrupciones Descripción de instrucciones de los PIC16F84A y PIC16F87xA . Resultado no es cero . El valor de la Cima de la Pila se carga en el PC. Cnt = Cnt Después de instrucción Cnt = 0x00 Z = 1 . Ejemplo 1: MOVWF REGA Antes de instrucción REGA = ? W = 0xF0 . Restaura el Contador de Programa GIE = 1 . Se rehabilitan las interrupciones con el seteo del bit GIE del registro INTCON. F Antes de instrucción Cnt = 0x00 Z = ? Z = 0 . REGA = W Después de instrucción REGA = 0xF0 W = 0xF0 No Opetation NOP Sintaxis: NOP Operandos: Ninguno Operación: No Operación Status Afectado:Ninguno Descripción: No Operación Ejemplo 1: NOP . Retornar de interrupción y rehabilitar . las interrupciones. RETFIE .

Después de instrucción W = value of kn ADDWF PCL. . d 0 ” f ” 127. Retorno de subrutina Después de instrucción PC = TOS . Inicio de Table . El PC se carga desde la Cima de la Pila (la dirección de retorno). RETURN . d [0. El valor de la Cima de la Pila (TOS) se carga en el PC. Si d = 1. el resultado se coloca en registro W. W = Desplazamiento . F RETLW k0 RETLW k1 : RETLW kn Antes de instrucción W = 0x07 RETURN Sintaxis: Operandos: Operación: Status Afectado: Descripción: Ejemplo 1: Return from subroutine RETURN Ninguno TOS PC Ninguno Retorno de subrutina. ésta es una instrucción de dos ciclos. W ahora tiene un valor de la tabla . Final de Table. .1] Status Afectado:C El contenido del registro ¶f¶ se rota a la izquierda un bit mediante el flag de Carry.. Descripción: Si d = 0.RETLW Sintaxis: Operandos: Operación: Status Afectado: Descripción: Ejemplo 1: Table Return with Literal in W RETLW k 0 ” k ” 255 k (W) TOS PC Ninguno El registro W se carga con los ocho bits del literal µk¶. : Table .. el resultado se queda en el registro µf¶. CALL . W contiene el valor de desplazamiento en Table. ésta es una instrucción de dos ciclos. Restaurar Contador de Programa RLF Sintaxis: Operandos: Operación: Rotate Left f through Carry RLF f.

Si d = 1. F Antes de instrucción REG = b'00011011' C = 0 Ejemplo 2: RLF REG. El bit de Status Time Out /TO se setea. . Entrar en modo Standby . d [0. el resultado se coloca en el registro µf¶. el resultado se coloca en el registro W. con el oscilador detenido.1] Status Afectado:C El contenido del registro ¶f¶ se rota un bit a la derecha mediante el flag de Carry. W Antes de instrucción REG = b'11010000' W = ? C = 1 Enter Sleep mode SLEEP Sintaxis: SLEEP Operandos: Ninguno 0x00 WDT 0 WDT prescaler Operación: 1 TO 0 PD Status TO. REG = REG rotado a la derecha Después de instrucción REG = b'00001101' C = 1 . REG = REG rotado a la izquierda Después de instrucción REG = b'00110110' C = 0 . El bit de Status Descripción: Power-Down /PD se limpia. Éste es el modo Standby en que el CPU del PIC se detiene para ahorrar energía. W = REG rotado a la izquierda Después de instrucción REG = b'11010000' W = b'10100001' C = 1 RRF Sintaxis: Operandos: Operación: Rotate Right f through Carry RRF f.Ejemplo 1: RLF REG. El Watchdog timer y su prescaler se resetean. F Antes de instrucción REG = b'00011011' C = 0 Ejemplo 2: RLF REG. W Antes de instrucción REG = b'11010000' W = ? C = 1 . W = REG rotado a la derecha Después de instrucción REG = b'11010000' W = b'11101000' C = 0 Ejemplo 1: RLF REG. PD Afectado: Poner el procesador en modo SLEEP. Ejemplo 1: SLEEP . d 0 ” f ” 127. Descripción: Si d = 0.

1] Operación: (f) .W Después de instrucción W = d'24' Z = 0 . Descripción: SUBWF Sintaxis: . El resultado queda en el registro W.Antes de instrucción WDT counter = ? TO = ? PD = ? WDT WDT /TO /PD Después de instrucción counter = 0x00 prescaler = 0 = 1 . .(W) (destino) Status Afectado:C.W Después de instrucción W = -d'90' = d'166' = 10100110 Z = 0 .W Después de instrucción W = 0x00 Z = 1 . WDT despierta al procesador Descripción de instrucciones de los PIC16F84A y PIC16F87xA SUBLW Sintaxis: Operandos: Operación: Status Afectado: Descripción: Subtruct W from Literal SUBLW k 0 ” k ” 255 k . Z = 0 y C = 0 indican que C = 0 . Z = 0 y C = 1 indican que C = 1 . W = 0x26 . el resultado es positivo . C = 1 . . Este bit se limpia si = 0 . el resultado es negativo. W = 10 . Ejemplo 1: SUBLW d'124' Antes de instrucción W = d'100' Z = ? C = ? Ejemplo 2: SUBLW d'10' Antes de instrucción W = d'100' Z = ? C = ? Ejemplo 3: SUBLW 0x26 Antes de instrucción W = 0x26 Z = ? C = ? Subtruct W from f SUBWF f. d 0 ” f ” 127 Operandos: d [0. Z Restar (mediante complemento a 2) el registro W del registro µf¶.(W) (W) C. DC. Z Restar (con el método de complemento a 2) el contenido del registro W del literal de ocho bits µk¶. Z = 1 Resultado es cero. DC. W = 124 .

Z = 1. Si d = 1. Z = 0 y C = 1 indican que C = 1 . resultado es cero.W Después de instrucción REG = 0x00 W = 0x22 Z = 0 . F Antes de instrucción REG = 0x2C XORLW Sintaxis: Operandos: Operación: Status Exclusive OR Literal with W XORLW k 0 ” k ” 255 (W) XOR k (destino) Z . W = REG . SWAPF Sintaxis: Descripción: Si d = 0. .1] (f<3:0>) (destino<7:4>) Operación: (f<7:4>) (destino<3:0>) Status Afectado:Ninguno Intercambiar los nibbles superior e inferior del registro µf¶. W Antes de instrucción REG = 0x59 W = ? Ejemplo 2: SWAPF REG. Si d = 1. el resultado se almacena en el registro W. . W = REG . el resultado se almacena en el registro W. d 0 ” f ” 127 Operandos: d [0. W Antes de instrucción REG = 9 W = 2 Z = ? C = ? Ejemplo 2: SUBWF REG. el resultado es negativo.W Después de instrucción REG = 0xFF = -d'1' = 11111111 W = 0x01 Z = 0 . Z = 0 y C = 0 indican que C = 0 . el resultado se queda en el registro µf¶.SUBWF Subtruct W from f Si d = 0. Después de instrucción REG = 0xC2 Ejemplo 1: SWAPF REG. el resultado se queda en el registro µf¶. W Antes de instrucción REG = 0x22 W = 0x22 Z = ? C = ? Swap Nibbles in f SWAPF f. el resultado es positivo. REG = REG swapped. W = REG swapped.W Después de instrucción REG = 9 W = 7 Z = 0 . Ejemplo 1: SUBWF REG. REG = REG . F Antes de instrucción REG = 0x00 W = 0x01 Z = ? C = ? Ejemplo 3: SUBWF REG. . . C = 1 . Después de instrucción REG = 0x59 W = 0x95 .

REG = REG XOR W Después de instrucción REG = 11001100 W = 00110011 Z = 0 . d 0 ” f ” 127 Operandos: d [0. W = 00001111 XOR W Después de instrucción W = 01011010 Z = 0 . el resultado se almacena en el registro W.1] Operación: (W) XOR (f) (destino) Status Afectado:Z OR exclusivo entre el contenido del registro W y el registro µf¶. Si d = 1. . . el resultado se queda en el registro µf¶. W = PORTB XOR W Después de instrucción PORTB = 00110011 W = 00110011 Z = 0 . Resultado no es cero. W Antes de instrucción PORTB = 00110011 W = 00000000 Z = ? Ejemplo 2: XORWF REG. . El resultado queda en el registro W. Ejemplo 1: XORLW b'00001111' Antes de instrucción W = 01010101 Z = ? Ejemplo 2: XORLW 0xF0 Antes de instrucción W = 0xF0 Z = ? Exclusive OR W with f XORWF f. Resultado no es cero. . Resultado es cero. Ejemplo 1: XORWF PORTB. Resultado no es cero. XORWF Sintaxis: Descripción: Si d = 0. F Antes de instrucción REG = 00110011 W = 11111111 Z = ? . W = 0xF0 XOR W Después de instrucción W = 0x00 Z = 1 .XORLW Afectado: Descripción: Exclusive OR Literal with W Aplicar un XOR entre el contenido del registro W y los ocho bits del literal µk¶.

Todas las directivas se detallan en el manual respectivo de los tantos que se instalan junto con el paquete MPLAB. para decimal. Es una costumbre poner list con al menos el parametro p = indicando el procesador. r = dec.Directivas del ensamblador Las directivas no son instrucciones del microcontrolador. r: establece el sistema de numeración a usar por defecto. Éste es un archivo secundario de los tantos que genera el ensamblador. el número será considerado en el formato señalado por r. Como se vio al inicio. El valor por defecto es hex (hexadecimal). cuyo significado es: y y y y p: establece el Procesador destino (PICmicro). siguiente directiva list p = PIC16F84A. f: establece el Formato del archivo hexadecimal de salida. que tiene el mismo objetivo. es obvio que se indica decimal. Por ejemplo. etc. INHX8S e INHX32. b: establece la cantidad de espacios en blanco que serán insertados en lugar de cada tabulación del archivo *. descrita continuación. ni siquiera suele ser necesario mover las configuraciones por defecto. Esto será equivalente a la directiva processor. del mismo MPLAB o mediante otra directiva. Aquí presentaremos las que suelen ser las más habituales. es decir. La primera es el valor por defecto. más adelante veremos para que puede servir. Lo curioso es que ninguno puede ser necesario. En el ejemplo. De cualquier forma. En caso de no especificar nada. . el formato de los números se suele especificar por un prefijo: 0x para hexadecimal. son indicaciones que se le dan al ensamblador para controlar algunos aspectos del ensamblado del código. Los parámetros listados se separan por comas. al ensamblador le da igual si las directivas están en mayúsculas o en minúsculas. La directiva list Es una directiva para listar una serie de opciones de ensamblado de diversa índole. Mas aun. . Para este propósito sería más práctivo e intuitivo usar la directiva processor. todos se pueden indicar de otra forma. las opciones disponibles son INHX8M.<//p> Como con los mnemónicos de instrucciones. f = INHX8M tiene cuatro parametros. b = 8.lst. Ya vimos en acción a muchas de ellas y las restantes las empezaremos a usar en adelante. Además de los citados en este ejemplo hay muchos más parámetros que se pueden incluir con la directiva list. ya sea desde el entorno del ensamblador MPASM. Esto es equivalente a la directiva radix.

incluir archivo P16F84A.pegar´ del contenido entero del archivo incluido en el lugar donde aparece la directiva. Estos archivos se hallan en la carpeta de instalación del ensamblador MPASM. Es equivalente a poner la directiva list con el parámetro p. No obstante. processor PIC16F84A La directiva include Es la directiva para incluir como parte del programa el archivo que se indica. cuando se cuando se contruye el proyecto desde MPLAB IDE. como librerías de códigos.hex resultante. No hacen falta.La directiva processor Es una directiva para establecer el PICmicro para el que se ensamblará el código fuente. cuando se ensambla el programa desde Proteus VSM. por ejemplo.inc Include también se usa para incorporar archivos de otros tipos. pero es recomedable hacerlo mediante esta directiva para que formen parte del archivo *.inc Las directivas __config y __fuses __config es más popular que __fuses a pesar de tener el mismo propósito (por lo menos en los PIC16) y de que el nombre de la segunda parecería más ilustrativo. como muestra el siguiente ejemplo. que a su vez se instala con MPLAB IDE. incluir archivo P16F877A. Incluso pueden ignorarse dichos signos asi como poner por delante el signo #.inc> . . aunque en ese sentido hay muy pocas variaciones entre uno y otro ³procesador´. Son directivas para establecer los Fuses de Configuración. resalto que poner estas directivas solo suele ser necesario cuando se llame al ensamblador desde una línea de comandos. incluir archivo lcd. Esta directiva debe aparecer en todos los programas de ensamblador al menos para incluir el archivo de dispositivo del PIC usado. norlmalmente C:\Microchip\MPASM Suite. Es una convención usar los signos < > para referirse a los archivos estándar y los signos ³ ´ para referirse a los archivos de usuario. Solo ponemos la directiva y el ensamblador sabrá dónde buscarlo. La inclusión se realiza como si se aplicara un ³copiar . Queda claro que no se imponen las restricciones que tiene esta directiva en el lenguaje C. por ejemplo. include <P16F84A.inc . por ejemplo). include "lcd.asm" . Con esta directiva el ensamblador sabrá qué tipo de instrucciones utilizar. Los fuses también se pueden establecer en el entorno del programa grabador (WinPic800.asm #include P16F877A.

el ensamblador permite identificarlos con un alías o nombre escogido por el usuario mediante la directiva equ. Por ejemplo. seremos propensos a comenter errores con las designaciones. Las directivas cblock y endc siempre deben ir juntas para formar un bloque de variables que ocupan posiciones consecutivas en la RAM del PICmicro. respectivamente. cont1 cont2 equ equ 0x0C 0x0D . Los fuses se ponen con un número o a través de máscaras que las representan. La directiva equ Cuando se quiere acceder a los registros (SFR o GPR) de la RAM se les debe referenciar por sus direcciones. Para mayor información puedes volver al capítulo 3. Con el siguiente ejemplo se ³bautizan´ con cont1 y cont2 a los registros ubicados en las direcciones 0x0C y 0x0D. De hecho. en la gran mayoría de las prácticas con el PIC16F84A de este curso vamos a usar la siguiente configuración: __config _XT_OSC & _WDT_OFF & _PWRTE_ON que es equivalente a: __fuses _XT_OSC & _WDT_OFF & _PWRTE_ON Para los PIC16F87xA especificaremos los siguientes fuses: __config _XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF Los fuses no indicados. su principal aplicación es ³darles nombres´ a los registros de la RAM. Ubicar cont1 en dirección 0x0C . todos los registros SFR del PIC ya están identificados así en su correspondiente archivo de dispositivo (revísalos. como la Protección de Código. si nuestro programa va a procesar varios datos y variables. A nosotros equ nos servirá para trabajar con los registros GPR. Estas máscaras se encuentran en el archivo de dipositivo del PICmicro. Como eso sería una labor muy tediosa. siendo la dirección de la primera variable la indicada después de cblock. quedarán con su valor por defecto. Aunque equ permite igualar (equal = igual) un identificador con cualquier constante númerica. si quieres). Para tener un mejor contexto de su uso analicemos el siguiente boceto de código: . Ubicar cont2 en dirección 0x0D Las directivas cblock y endc Incluso si usamos la directiva equ para referirnos con más comodidad a los registros GPR.Recuerda que el PIC16F84A tiene 4 fuses y los PIC16F87xA tienen 9. Todos los hemos estudiado antes.

processor PIC16F84A include <P16F84A.inc> cblock cont var temp endc org Start ; ... call ; ... goto subroutine Start 0x0C ; En el PIC16F84A los registros GPR están ; disponibles a partir de la dirección 0x0C

0x000

;************************************************************************** cblock ; Aquí no necesitas establecer una bus ; dirección base del0 endc subroutine ; ... ; ... return end

En el boceto el primer bloque cblock - endc es equivalente a haber puesto las líneas de código:
cont var temp equ equ equ 0x0C 0x0D 0x0E ; Ubicar count en dirección 0x0C ; Ubicar var en dirección 0x0D ; Ubicar temp en dirección 0x0E

Más abajo aparece otro bloque cblock - endc con dos variables, bus y del0, que también se pudieron incluir en el primer bloque, pero que se separan, como muchas veces se preferirá, por guardar alguna relación entre ellas y para tratar de ordenar el código. No obstante, a diferencia de los lenguajes como el C, estos registros serán accesibles desde cualquier parte del programa. Ahora bien, si como dice el comentario, el segundo bloque no necesita una dirección base, ¿qué direcciones les corresponden a esas variables? Como el ensamblador sabe dónde se quedó, a las variables del siguiente bloque les asignará las locaciones subsiguientes. Es decir, el segundo cblock - endc sería equivalente a:
bus del0 equ equ 0x0F 0x10 ; Ubicar bus en dirección 0x0F ; Ubicar del0 en dirección 0x10

De hecho, en un programa puedes poner varios bloques más de este tipo, pero sin olvidar que, a menos que sepas lo que haces, solamente el primero debe tener la dirección de la primera variable, inclusive si está vacío.

La directiva org

Su nombre deriva de origen. Es para indicarle al ensamblador a partir de que dirección empezará a mapear el subsiguiente código ejecutable. Como el CPU empieza a ejecutar el código desde la dirección 0x000, al menos la primera instrucción de código debería estar allí. Sin embargo, poner org 0x00 en programas con código continuo se ha convertido en un hábito a veces hasta innecesario porque el ensamblador, que no es tan tonto, sabe que por defecto debe colocar el código a partir de la dirección 0x000.
org 0x000

Usar org sí es útil cuando se quiere reubicar algunas rutinas de código en determinados segmentos de la memoria de programa.
La directiva end

Es la directiva que pone fin a la lectura del código fuente por parte del ensamblador. Cualquier código ubicado después de esta directiva ya no será tomado en cuenta, lo que implica que solo debería haber una directiva end por programa, incluso si consta de varios archivos. Son sofisticados los programas donde se puede usar varias veces.
La directiva banksel

El nombre de esta directiva es un acrónimo de BANK SELection y, como se prevé, sirve para cambiar de bancos. Como sabemos, la RAM de los PICmicros de familias Baseline y Midrange se divide en varios bancos por los que tenemos que ir saltando para acceder a sus registros, ³jugando´ con los bits RP0 y RP1, del registro STATUS, así: RP1 RP0Banco accedido 00 Banco 0 01 Banco 1 10 Banco 2 11 Banco 3 En los PIC con dos bancos, como el PIC16F84A, solo se trabaja con el bit RP0. Dado el tedio que puede acarrear este trabajo, sobre todo si no recordamos a qué banco pertenece un registro, la directiva banksel puede ser de gran ayuda. Banksel se encargará de colocar las instrucciones necesarias para acceder al registro que se le indica como parámetro.
banksel TRISA ; Seleccionar banco del registro TRISA (Banco 1)

La directiva #define

Es otra directiva que se utiliza muy a menudo. Los que programaron alguna vez en C/C++ saben cómo funciona. Para los otros, deben saber que tiene tres partes: el mismo define, un identificador y toda la cadena restante.
#define identificador cadena ;

Lo que hará el ensamblador es sustituir el identificador por toda la cadena restante tal cual es en cada punto del programa donde se encuentre identificador. Por eso se le conoce como directiva de sustitución de texto. identificador es una palabra con las mismas reglas impuestas a otros identificadores, como las etiquetas o nombres de registros GPR. En cambio, cadena puede estar formado por cualesquiera otros elementos de código ensamblador. Un típico ejemplo de define es para establecer los puertos de interface, más o menos así:
#define switch PORTB, 0

Y cuando más adelante escribamos, por ejemplo,
btfsc Switch ; Ver si switch vale 0

lo que en realidad entenderá el ensamblador será
btfsc PORTB, 0 ; Ver si switch vale 0

La directiva $

Recordemos que las etiquetas en el fondo representan las direcciones de las instrucciones a las que preceden y sirven de referencia para las instrucciones de salto. Así mismo, la directiva $ marca la dirección de la instrucción actual y, por ejemplo, $ - 2 será la dirección de la instrucción que está dos posiciones atrás. Normalmente es preferible colocar etiquetas y dejar el uso de $ solo para los casos donde la propensión a cometer errores sea mínima, por ejemplo, para las instrucciones de saltos cortos. A continuación se muestran dos rutinas de delay de 50 ms aprox. Ambas son equivalentes, solo que la segunda emplea la directiva $ en vez de las etiquetas. Se asume que la variable cont ha sido definida previamente.
delay_50ms movlw movwf laba addlw btfss goto decfsz labb goto .33 cont .1 STATUS, Z labb cont, F laba

; Saltar a labb ; Saltar a laba

Saltar 4 instrucciones atrás La directiva dt Dt construye una tabla de datos a base de instrucciones retlw. Por ejemplo.4 . Dt puede recibir uno o más parámetros separados por comas. Indica no reportar los mensajes identificados por el número msgnum. Ensure that bank bits are . error y advertencias 1.return delay_50ms movlw movwf addlw btfss goto decfsz goto return .33 cont . 2. Saltar 2 instrucciones adelante . Los valores de msgnum forman una lista casi interminable que la puedes encontrar en el manual del ensamblador MPASM.msgnum. Z $ + 2 cont. errores y advertencias. + msgnum. Con la directiva errorlevel podemos indicar cuáles de ellos pueden ser ignorados o reportados. . Las tablas son matrices de constantes a cuyos elementos se puede acceder secuencialmente mediante una variable índice. que dice ³Register in operand not in bank 0. Indica reportar errores. Indica reportar advertencias y errores. un clásico mensaje que aparece constantemente incluso cuando el código está escrito correctamente es el 302. Indica reportar los mensajes identificados por el número msgnum. Sus parámetros pueden ser: y y y y y 0. la siguiente directiva dt "test" será entendida por el ensamblador como: retlw retlw retlw retlw 't' 'e' 's' 't' La directiva errorlevel Después de ensamblar o intentar ensamblar el código el ensamblador generará una serie de mensajes. F $ . Los parámetros de cadenas de texto serán descompuestos en sus letras. Indica reportar mensajes. Los errores no se pueden inhibir. Por ejemplo.1 STATUS.

podemos escribir la siguiente directiva. ifdef.) ifndef __16F876A messg "Este codigo esta realizado para el PIC16F876A" endif Las directivas if. processor PIC16F84A . por ejemplo. errorlevel -302 La directiva messg Sirve para generar mensajes personalizados definidos por el usuario. Mediante ellas indicaremos si una o algunas rutinas de código o directivas inclusive serán tomadas en cuenta o no por el ensamblador. Sabemos que los principales cambios a tener en cuenta son el archivo de dispositivo a incluir. supongamos que hacemos un programa para el PIC16F84A y que eventualmente querremos cambiar el código para que se pueda ensamblar para el PIC16F877A. else y endif. Si nos cansamos de verlo. si es que se trata de ensamblar para otro PICmicro. else y endif Son directivas para ensamblado condicional. para lo que se requiere de algunas otras directivas como if. la siguiente directiva hará surgir el mensaje ³Este código está realizado para el PIC16F876A´. El siguiente boceto de programa considera estos cuatro puntos y se puede ensamblar para uno u utro microcontrolador tan solo cambiando la directiva processor. El uso del bloque de else es opcional. De lo contrario . Su estructura se muestra a continuación. ifdef. aunque tampoco eso sería necesario si el programa se construye desde otro IDE como MPLAB. el inicio de los registros GPR y la configuración del registro ADCON1 de los PIC16F87xA para que el puerto A sea de E/S digital. (la constante __16F876A está definida en el archivo de dispositivo P16F876A.correct´. El mensaje suele ser condicional. Se considerará este código endif A modo de ejemplo. la directiva de los fuses. if (ifdef o ifndef) condition .inc.inc> __config _XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF cblock 0x20 endc . Se considerará este código else . Allí nos avisa que estamos accediendo a un registro que no es del banco 0 y que debemos asegurarnos de estar haciendo lo correcto. ifndef. ifndef. Ejemplo. si condition se cumple (o si es una expresión previamente definida) . Seleccionar PIC destino ifdef __16F877A include <P16F877A.

Resto end __16F877A ADCON1 0x06 ADCON1 . El bit RP1 debe dejarse en 0 por compatibilidad con otros dipositivos. Ya fue descrita anteriormente. Si después quisiéramos acceder al registro OPTION_REG. Si está definido __16F877A .else . Así. A continuación trataré de justificar este mecanismo. Una directiva que nos puede mitigar el fastidio de cambiar de bancos es banksel. Por ejemplo. por ejemplo. asumir que es PIC16F84A include <P16F84A. basta con modificar el bit RP0 para navegar entre ellos. . 0x000 del cuerpo del programa Operaciones de cambio de bancos Una de las cosas más desagradables en el trabajo con los PICs es estar cambiando de bancos para acceder a los registros de la RAM. se accede a los registro del banco 0. En los PICs con dos bancos. tendríamos que movernos al banco 1. se puede entender. Algunas variables del programa endc org Start ifdef banksel movlw movwf endif . de los 8 ó 9 bits de dirección de cada registro (SFR o GPR) de los PIC16F solamente los 7 primeros están presentes en el código de las instrucciones. como el PIC16F84A. Hacer PORTA digital . Si no es PIC16F877A. que cuando RP0 y RP1 valen 0. del registro STATUS y son la razón de la existencia de hasta cuatro bancos. Los 2 bits restantes se extraerán de los bits RP0 y RP1. Por una razón que acabo de borrar (no era liviana). para leer o escribir en el registro PORTA debemos cambiar al banco 0.inc> __config _XT_OSC & _WDT_OFF & _PWRTE_ON cblock 0x0C endc endif cblock .

A los nuevos en el tema puede resultar de gran ayuda . Esta subrutina toma en ejecutarse aproximadamente 20 ms. se llegará a un límite que puede ser insuficiente. Decrementar cont. Por ejemplo. Los delays no son más que rutinas donde se cuenta con una variable. saltear si da 0 Saltar a bucle2 . . el cual cuenta desde 100 hasta 0. Decrementar cont. Para ajustar el tiempo del retardo ahora se pueden modificar los conteos de las dos variables cont y cont2. F bucle1 . .200 cont cont. que estudiaremos luego. 100 a W W a cont2 Decrementar cont2. cblock cont. F bucle1 .100 movwf cont2 bucle2 decfsz cont2. 200 a W . Los Timers permiten realizar temporizaciones con gran presición pero para pausas ordinarias se prefiere poner un delay a base de bucles de conteo anidados.200 movwf cont bucle1 movlw . saltear si da 0 . .Los delays Un delay es una rutina de demora o retardo que sirve para poner una pausa entre dos puntos del programa. 200 a W . En esos casos se puede insertar algún código de relleno entre la etiqueta bucle1 y la instrucción decfsz cont. W a cont . cblock cont endc delay_600us movlw movwf bucle1 decfsz goto return . y genera un delay de 600 us aproximadamente. F goto bucle2 decfsz goto return cont. W a cont . Sin embargo. Para hacer las cosas con más orden ese código de relleno puede ser otro bucle de conteo (bucle anidado). Saltar a bucle El tiempo del retardo se puede ajustar modificando el valor a cargar en el registro cont. sobre todo si nos apoyamos en herramientas como el simulador del MPLAB o Proteus VSM. se puede interpolar o extrapolar otro bucle de conteo hasta conseguir lo deseado. Saltar a bucle A quienes ya estamos acostumbrados a trabajar con estas rutinas los ajustes de los delays no son gran molestia. cont2 endc delay_20ms movlw . la siguiente subrutina decrementa el registro cont desde 200 hasta 0. En la siguiente subrutina se muestra en otro color el bucle de conteo insertado. saltear si da 0 . F. Si el tiempo buscado es mayor que lo que se obtiene así.

Te recomiendo practicar con el generador de delays de http://www. cont2 endc delay_20ms movlw . . para mover una constante a un registro se debe hacer así: movlw 0xF3 . En el sitio web de Microchip se pueden encontrar varios códigos de ejemplo para realizar esas operaciones. aunque son muy complicados. El algoritmo que emplean esos delays es una variante ingeniosa de los bucles de conteo anidados. Por desgracia solo contamos con el repertorio de 35 instrucciones para construir los programas de todos los proyectos con PICs de esta familia. etc. por eso uso este tipo de delays en casi todos mis programas. Saltar 3 instrucciones atrás Aunque al principio te parezca algo enredado seguir el flujo del código para saber cuándo termina. aunque en mi opinión a la gente de Microchip se le paso un poquito la mano con estos PICs. Para cuando nosotros necesitemos emplearlas ya estaremos trabajando con los compiladores de alto nivel. Por ejemplo. no hay instrucciones de multimplicación o división. Otra ventaja que tienen es que es relativamente fácil calcular el valor del delay. Tienen la siguiente forma: cblock cont. y actuar según su significado. No hay instrucciones de comparación. Saltar 2 instrucciones adelante . no hay instruccciones para operar números de más de un byte. Las comparaciones en los PIC16 son las operaciones más insufribles que he conocido en toda mi vida de programador. Hay que hacerlo a través del registro de trabajo W.piclist. W = 0xF3 . 16 a W W a cont (contador principal) 100 a W W a cont2 (contador secundario) . con un poco de observación lo lograrás.recurrir a herramientas libres que se hallan en Internet. El contador secundario se utiliza para afinar la precisión. Por lo pronto conoceremos algunos snippets de comparaciones que nos serán muy útiles en lo que resta del este Módulo.exe.16 movwf cont movlw . . no hay instrucciones para trabajar con números de punto flotante.3 return .com/cgi-bin/delay. Para comparar dos datos tenemos que restarlos uno de otro y luego comprobar los bits Z (zero) y/o C (carry).100 movwf cont2 bucles decfsz cont2 goto $ + 2 decfsz cont goto $ . Comparaciones La filosofía de los procesadores con diseño RISC es reducir en lo posible el juego de instrucciones. del registro STATUS. Primero debemos tener en cuenta que no existen instrucciones para cargar constantes a registros directamente ni para mover datos de memoria a memoria en un solo ciclo. . A mí me fascina la forma cómo están trenzados los dos bucles. En el código de arriba depende sobre todo del contador principal.

O sea.W ¿Z = 1?. .. ¿ registro = 0 ? Para saber si un registro vale 0 hay que aplicarle cualquier instrucción que afecte el flag Z. En seguida se comparan dos registros: movf subwf btfss goto goto .. . W STATUS. W PORTB . Z Notzero Zero . En conclusión. F STATUS. .. En conclusión. W = REG ... significa que el resultado ha sido positivo o cero. PORTB = REG. REGA. C = 1 si el resultado fue mayor que 255 y C = 0 si no fue así. Ésta es una peculiaridad exclusiva de los PICs. .. . Z NotEqual IsEqual .. ¿REG = 0 ? no yes ¿ registro = registro/constante ? El bit C indica si una operación de suma ha producido un resultado mayor que 255 (el máximo valor representado por un número de 8 bits). ¿ REGA = REGB ? no. Veamos como comparar un registro con una constante: movlw subwf btfss goto goto . etc. si después de una resta el bit C se ha seteado. . . ¿ REG = 100 ? no. PORTB = W.. . Si C vale 0. Por otro lado. . sumarle 0. el bit C funciona al revés con las restas. d'100' REG. W = REGA W = REGB .. W = 100 W = REG . . .W ¿Z = 1?. .movwf TRISB . como mover el registro hacia sí mismo. Como vemos. TRISB = 0xF3 Y para mover el contenido de un registro a otro registro se debe hacer así: movf movwf REG.... TRISB = W. . Z NotEqual IsEqual . W STATUS.. .. W REGB. Ejemplo: movf btfss goto goto REG. yes. yes. es que la resta ha dado un resultado negativo. REG = REG ¿Z = 1?.

. W = 100 W = REG . no. REGB... C EsMayor NoMayor . W REGB... W = REGA W = const . . W STATUS. . C EsMenor NoMenor . ... . W = REGB W = REGA .... ... . . movf subwf btfss goto goto . .. . . C EsMenor NoMenor .. C EsMenorIgual NoMenorIgual . ... . ¿ REGA < REGB ? yes.. ¿REGA ” REGB ? yes.. movf sublw REGA.W ¿C = 0 ?.. W STATUS. . ¿ REGA ” const ? yes.... .. . . .. ¿ REGA > REGB ? yes. . REGA. . W = REGA W = REGB . Como ya sabemos evaluar el primer caso.. W REGA. W const STATUS.... entonces es mayor o igual que él. no. .. . . . no. .. REGA. .. . no. . movlw subwf btfss goto goto . W = const . . ..W . W STATUS. .. . ¿ registro > registro/constante ? movf subwf btfss goto goto . movf sublw btfsc goto goto .. .. ¿ REG < 100 ? yes. .W ¿C = 1?. movf subwf btfsc goto goto .. d'100' REG. W = REGA W = REGB . .¿ registro < registro/constante ? Ahora empezaremos a testear el bit C. W const .. no.W ¿C = 1?. .W ¿ C = 0 ?. . .W ¿C = 0?. . los siguientes serán fácilmente deducibles. W = REGA ... . W REGB. ¿ registro registro/constante ? El fundamento de lo que viene es: si un dato no es menor que otro. C EsMenorIgual NoMenorIgual . REGA. .. W STATUS.

desde MPLAB IDE.. aunque se puede Como muchos programas.hex entendible y ejecutable por el microcontrolador... .. no... este ensamblador se puede llamar vía la línea de comandos y es común hacerlo. aunque estos últimos suelen usar sus propios ensambladores. ¿C = 0?. Si instalas el MPLAB IDE en el directorio por defecto.btfss goto goto . El ensamblador de los PICs se llama MPASM o MPASMWIN y también viene incluido en el paquete MPLAB. . puedes encontrar el MPASMWIN en la carpeta C:\Microchip\MPASM Suite. . . . ¿ REGA > const ? yes.. por ejemplo.. El ensamblador MPASM Un programa ensamblador se encarga de convertir el código fuente *. . Proteus VSM o algún compilador de alto nivel. Al ejecutarlo veremos su entorno: El programa MPASM para Windows. Ambos son archivos de texto. . C EsMayor NoMayor . STATUS. pero el programa grabador sabe cómo interpretar el archivo hexadecimal.asm en código máquina *.

se puede hacer hasta con dos pasos: y y Presionamos el botón Browse y escogemos el archivo *. el número de espacios en blanco por tabulación a colocar en el archivo *. No lo hagas. como por ejemplo. Eso es todo. Si el proceso de ensamblado resulta exitoso. el nivel de las advertencias a mostrar (uso de directiva errorlevel). el tipo de archivo hexadecimal de salida (parámetro f de la directiva list). Podemos dejar las demás opciones con sus valores por defecto y luego presionamos el botón Assemble. veremos la siguiente clásica ventanita (creo que ya no aparece en las más recientes versiones del MPASMWIN): Ensamblado de código exitoso con MPASMWIN. Al desmarcar casilla case sensitive harás que el ensamblador trate por igual a los identificadores escritos con letras mayúsculas o minúsculas. el formato por defecto de los números sin prefijo (uso de la directiva radix o parámetro r de la directiva list).hex a grabar en PIC. el procesador destino (uso de directiva processor o parámetro p de la directiva list). Observa que muchos de los parámetros a establecer en este entorno también se pueden indicar desde el código fuente mediante directivas. es recomendable acostumbrarse a trabajar haciendo estas distinciones.lst resultante (parámetro b de la directiva list).Su uso es bastante sencillo. .

aprenderemos el uso de casi las últimas directivas del ensamblador de mi lista básica. la secuencia de conteo será 0. 1. Para los programas complejos es recomendable diseñar su algoritmo en un diagrama de flujo antes de empezar a codificar. 7. ¿verdad? Por último. En esta práctica vamos a implementar un contador de anillo (también llamado contador Johnson) de módulo 8 ascendente/descendente 0-8/8-0 . 5. 8. Los diagramas de flujo o flowcharts son las representaciones gráficas de los algoritmos de los programas o de las subrutinas que los constituyen. basta con verlos para darse cuenta de lo sencillos que son. funcionan así: DecimalBinario Johnson 0 00000000 1 00000001 2 00000010 3 00000100 4 00001000 5 00010000 6 00100000 7 01000000 8 10000000 Simple. 0. 0. que muestra el conteo por el puerto B. Con el diagrama de flujo a la mano. Pero no es así. 4. 4. 8. 3. ¡Qué alivio! . cree que desarrollar un programa 20 veces más grande le costará un esfuerzo 20 veces mayor. en cuanto a eso que algunos llaman ingeniería de software.. por más grande que sea... 6. 1. Están formados por un conjunto de bloques interconectados entre sí mediante flechas para establecer la secuencia en que se llevan a cabo las operaciones. Así que no entraré en más detalle.Práctica 8-1 Los diagramas de flujo Cuando uno es principiante y ve que los simples programas de ejemplo le exigieron cierto esfuerzo de asimilación.. Se dispone de un switch conectado al pin RA2 que establece la dirección del contador: si el switch marca una entrada de 1 lógico. 5. 2. 2. Por si acaso no has oído hablar de los contadores Johnson. Después de esta práctica ya no las volveré a mencionar. Si el switch marca un 0. el conteo será 8. 3. Para no esperar ni mucho ni poco el incremento/decremento se dará cada 700 ms. 2. 1. escribir el código resulta una terea bastante simple y hasta entretenida. 6. Aunque hay toda una normalización ANSI al respecto. 7.

Aunque no todas vayan a ser necesarias. Es posible concebir distintos diagramas de flujo para un mismo problema. Dichas etiquetas estarán presentes en el código fuente..8. . a lo mejor tengas que hacer varios ensayos antes de dar con el diagrama adecuado.El circuito Diagrama de flujo del programa En primer lugar debemos leer bien el enunciado propuesto y a partir de él dibujar el flowchart. Si no tienes mucha práctica con esto.. en este caso conviene aplicar la frase ³más vale que sobre a que falte´. Sobre el flowchart debemos colocar etiquetas básicamente en dos partes: en los nodos de encuentro y en las ramas de bifurcación de los bloques de decisión. He pensado en una subrutina aparte para convertir el valor decimal del contador en el formato del contador Johnson. La estrategia que yo he tomado es ésta: En principio asumo que el contador es decimal con el rango de 0.

No creí conveniente un flowchart para algo tan simple. .Podría parecer que al final del diagrama hay varios nodos. La subrutina BinToLeds activa el bit C y lo rota a la izquierda sobre una variable temporal tantas veces como sea el valor de entrada. pero todos pueden identificarse con la misma etiqueta MLoop.

FileName: JohnsonCntr. . .W (Top) ¿Count < Top? Sí No Count = 0 . empezar desde 0 0x0C . . Switch conectado a RA2 . Innecesario para el proyecto MPLAB #include <P16F84A. Seleccionar banco de registro TRISA (Bank 1) Puerto B salida Entrada para switch Seleccionar banco de registro PORTA (Bank 0) ¿Switch = 1? No Sí En modo asc. C Incrementar Count MLoop Count. W STATUS. . .**************************************************************************** . Modo Ascendente Top a W W = Count . Palabra de Configuración cblock Count tmp Leds endc #define Switch #define Top org Start banksel clrf bsf banksel btfss goto IniciarArriba clrf goto IniciarAbajo movlw movwf MLoop movf call movwf call btfss goto ContarArriba movlw subwf btfss goto ReiniciarArriba clrf goto Incrementar incf goto Count. En el PIC16F84A los registros GPR están . Vector de Reset .7s Switch ContarAbajo Top Count. . Incluir P16F84A. . Bucle principal W = Count Formatear W W (valor devuelto) a PORTB pausa de 0. . f MLoop TRISA PORTB Switch PORTA Switch IniciarAbajo Count MLoop Top Count . Módulo del contador .El código fuente .inc> . 2 d'8' 0x0000 . Purpose: Flowcharts . Processor: PIC16F84A . disponibles a partir de la dirección 0x0C PORTA.asm . . Incrementar contador .7 segundos ¿Switch = 1? No Sí. . . . . . . empezar desde Top . . .**************************************************************************** processor PIC16F84A .inc __config _XT_OSC & _WDT_OFF & _PWRTE_ON . . . . En modo desc. W BinToLeds PORTB delay_0.

sirve para ver si.ContarAbajo movf btfsc goto Decrementar decf goto ReiniciarAbajo movlw movwf goto Count. Genera 0. Sirve para ver si. F MLoop Top Count MLoop . btfsc STATUS. W = tmp (valor final) return . F . Output: W = valor formateado BinToLeds clrf tmp . Saltar 2 instr adelante decfsz cnt2. Overview: Genera 0. . f .5 . Z ReiniciarAbajo Count. . Leds = Leds. F STATUS. saltear si 0 goto loop . Aquí no necesitas establecer una cnt0. saltear si 0 goto $ + 2 . fin movf tmp. f goto $ + 2 . Saltar 2 instr adelante decfsz cnt1.7s . Activar un bit más decfsz Leds. cnt1--. Overview: Devuelve W con un bit activo según el valor de W al entrar . Por otro lado. Cargar cnt2 decfsz cnt0. allí se encuentran todas las etiquetas que marcamos en el flowchart.7 segundos movlw d'159' movwf cnt0 . cnt2 . Sí loop rlf tmp. F .7 segundos cblock . Le dice al ensamblador que pare aquí Descripción del programa Lo bueno de los flowcharts es que allí se resume toda la lógica del programa. He escrito el código lo más cercano posible al flowchart. Z . regresar . De manera que ya no tendré que explicarlo en ese sentido. F . . dirección base endc delay_0. f .. Repetir todo . .1. ¿Count = 0? Sí No Decrementar contador . Leds = Leds . Leds = W movf Leds. . tal como lo había dicho. ¿Leds > 0? ¿Hay leds que prender? goto fin . C . W . tmp = 00000000 movwf Leds ..**************************************************************************** . Input: W = Numero a formatear . Cargar cnt1 movlw d'2' movwf cnt2 . .**************************************************************************** . .. aun a costa de la falta de optimización. Saltar 5 instr atrás return end . Cargar cnt0 movlw d'135' movwf cnt1 . saltear si 0 goto $ .. Modo Descendente Count = Count. cnt1. Count = Top . cnt--. No bsf STATUS.

hay que hacer una resta y luego testear el bit Carry para evaluar su significado. sino que se darán pequeños rebotes en el contacto que generarán ondas irregulares. En la práctica 4-3 implementamos un sencillo filtro pasa-bajas para evadir estos rebotes. . Práctica 8-2 Filtros antirrebote Cuando apretamos un pulsador o movemos un switch. En el segundo. . El circuito Seguimos con los 8 leds de puerto B. . . El tiempo se puede ajustar modificando los valores de carga de los contadores cnt2. Modo Ascendente Top a W W = Count . En el primero. C Incrementar . . En el anterior capítulo hay más información sobre esto. Como el circuito requerido puede resultar algo incómodo de armar. como las que conocimos en el capítulo previo. En el futuro abordaremos los Timers para implementar las temporizaciones precisas con cálculos más directos. la señal de tensión relacionada no cambiará su valor establemente. luego de responder al primer pulso ponemos un tiempo para que se estabilice la señal. ¡Qué! ¿Esperabas un algoritmo DSP? En cuanto al enunciado del programa: cada vez que presionemos un botón (pulsador) se prenderá un LED más del puerto B y con otro botón se hará lo mismo para apagar un LED más.W (Top) ¿Count < Top? Sí No Los comentarios lo dicen todo. De la multitud de mecanismos realizables empezaremos por implementar dos bastante simples. Si queremos comparar dos datos. Sí. una vez detectado el cambio de tensión esperaremos un lapso de tiempo hasta que la señal se estabilice y luego volveremos a testear la entrada.La subrutina delay_0.7s emplea tres bucles de conteo trenzados. (No te obsesiones con la precisión. a veces se prefiere añadir una rutina antirrebote en el programa.) Un punto que te puede llamar la atención es el grupo de líneas Un punto que te puede llamar la atención es el grupo de líneas ContarArriba movlw subwf btfss goto ReiniciarArriba Top Count. en ese orden. . y genera un delay de 700 ms aproximadamente. solo que esta vez le agregamos dos pulsadores en el puerto A. cnt1 y cnt0. W STATUS. Desafortunadamente estos PICs no tienen instrucciones de comparación.

.

Dirección del primer GPR . 4 .El diagrama de flujo El código fuente . Prende otro LED #define Button1 PORTA.**************************************************************************** . .asm .inc> __config _XT_OSC & _WDT_OFF & _PWRTE_ON cblock endc 0x0C .**************************************************************************** processor PIC16F84A include <P16F84A. Purpose: Delays antirrebote . Processor: PIC16F84A . FileName: OnOffLeds.

WaitReleased1 btfss Button1 goto $ . F . btfss Button2 goto $ . goto CheckButton2 . SELect BANK of TRIS registers Entrada de Button 1 Entrada de Button 2 PORTB salida SELect BANK of PORT registers Apagar todos LEDs clrf PORTB . rrf PORTB. goto MLoop . .================================ CheckButton2 btfsc Button2 . Contador principal goto $ . Subrutina delay_30ms cblock d0. goto MLoop ¿Botón 1 pulsado? No. decfsz d1. btfsc Button1 . . Apaga otro LED . d1 endc delay_30ms movlw d'23' . TurnOnLed bsf STATUS. 3 org Start banksel bsf bsf clrf banksel TRISA Button1 Button2 PORTB PORTA 0x0000 .1 . . checkear Button 1 C entrará por la izquierda para apagar un led más Tiempo antirrebote Esperar botón libre . Vector de Reset . goto MLoop . return end . F . f .**************************************************************************** .1 . 30000/1280 = 23 movwf d1 . call delay_30ms . checkear Button2 Sí.================================ CheckButton1 btfsc Button1 . . WaitReleased2 call delay_30ms . C .3 . TurnOffLed bcf STATUS. Contador secundario goto $ + 2 . rlf PORTB. MLoop .#define Button2 PORTA. f . goto MLoop . decfsz d0. C . tiempo antirrebote ¿Aún pulsado? No C entrará por la derecha para prender un led más Esperar botón libre ¿Botón 2 pulsado? No.

con la diferencia de que al regresar de la subrutina cargará en W el valor de la constante que tiene al lado. Si te estás preguntando porque tantas instrucciones retlw.Descripción del programa Las dos rutinas que atienden a los dos botones son similares.. ¿Cómo así? El curso de ejecución del programa se rige por el contador de programa PC. En lo sucesivo utilizaremos cualquier forma según convenga. es porque cada vez que llamemos a esta subrutina se ejecuta una y solo una de ellas. prefiero referenciar a los elementos de una tabla utilizando un índice entre corchetes después de su nombre. Finalmente. F 0x0D 0x0A 0x01 0xE0 . Una tabla implementada a manera de subrutina tiene el siguiente aspecto: Table addwf retlw retlw retlw retlw PCL. F suma el valor de W al PC. F se dará un salto a retlw 0x01 (como lo indica el comentario) y se retornará con 0x01 en W. Como la instrucción addwf PCL. luego de apagar el LED. . En el caso más sencillo solo será necesario emplear el registro PCL para diseñar tablas de hasta 256 elementos. el programa saltará tantas posiciones como valga de W. que contiene los 8 bits de menor peso del PC y PCLATH (Program Counter Latch High). Las tablas tienen un amplísimo campo de aplicación. que representa los 5 bits de mayor peso. Una forma de confeccionaar tablas en los PICmicros es mediante la instrucción retlw. Table[1]. . Por ejemplo. Elemento Elemento Elemento Elemento accedido accedido accedido accedido con con con con W W W W = = = = 0 1 2 3 Retlw (RETurn with Literal in W) es una instrucción de retorno similar a return. si llamamos a la subrutina Table de arriba habiendo cargado 2 en W. Así que podemos provocar desvíos y saltos a diversos puntos del código alterando el valor de estos registros. . Table[2]. al igual que las matrices algebraicas. y así. pero para familiarizarnos con ellas volveremos a recurrir a los siempre ilustrativos secuenciadores. En un caso puse el tiempo antirrebote antes de prender el LED y en el segundo. . te sugiero apreciar esta operación en un simulador. para la tabla Table el primer elemento sería Table[0]. No olvides que en el mundo digital siempre se empieza desde el 0. Práctica 8-3 Las Tablas o Matrices Se llama tabla de búsqueda a un conjunto de datos grabados en locaciones contiguas de la memoria de programa del microcontrolador. con la ejecución de addwf PCL. el tercer elemento. De cierta forma este programa simplemente cuenta desde 0 hasta 15 cíclicamente.. el segundo elemento. como MPLAB SIM o Proteus. Se utiliza una tabla como patrón del efecto para canjear cada uno de dichos valores por una constante que representa un ítem del secuencial. ¿Alguna duda? Por si acaso fuera así. El PC está representado por dos registros especiales: PCL (Program Counter Low). Así.

Bank 1 Todo PORTB. .asm . . W LedsTable PORTB .**************************************************************************** processor PIC16F84A #include <P16F84A. Processor: PIC16F84A . El primer GPR está aquí 0x0000 .El circuito Se puede usar el mismo circuito de la práctica anterior.**************************************************************************** . Vector de Reset . W = index . Sacar elemento número W de LedsTable en W . Valor retornado (W) a PORTB TRISB PORTB PORTB index .inc> __config _XT_OSC & _WDT_OFF & _PWRTE_ON cblock index endc org Start banksel clrf banksel clrf MLoop movf call movwf index. . El código fuente . FileName: TableSeq. Purpose: Tablas de búsqueda . salida Bank 0 Empezar desde 0 0x0C .

. f . Cargar cnt1 movlw d'52' . Pausa de 65 ms . return . F retlw b'00000000' . Cargar cnt2 decfsz cnt1. offset = 12 retlw b'11100000' . offset = 0 retlw b'00000001' . offset = 9 retlw b'11111100' . offset = 3 retlw b'00001111' . f . Input: W = Índice del elemento de la tabla LedsTable addwf PCL. offset = 2 retlw b'00000111' . 65000/1280 = 50 movwf cnt2 . .3 . offset = 13 retlw b'11000000' . offset = 8 retlw b'11111110' . cblock cnt1.**************************************************************************** . W STATUS. f MLoop . offset = 15 . Subrutina delay_65ms. goto $ . . offset = 14 retlw b'10000000' . goto $ + 2 . Subroutine: LedsTable . cnt2 endc delay_65ms movlw d'49' . end . offset = 11 retlw b'11110000' . . offset = 5 retlw b'00111111' . C Incrementar index MLoop index. offset = 10 retlw b'11111000' .call movlw subwf btfss goto Resetear clrf goto Incrementar incf goto delay_65ms d'15' index. decfsz cnt2. offset = 1 retlw b'00000011' . Genera 65 milisegundos. Incrementar index . Valor de ajuste movwf cnt1 . . offset = 7 retlw b'11111111' .W (15) ¿index < 15? sí no Empezar desde 0 otra vez .**************************************************************************** . W = 15 W = index . offset = 6 retlw b'01111111' . offset = 4 retlw b'00011111' .

El circuito El mismo circuito. el segundo LED. . se dirigen hacia su encuentro y después de cruzarse por el centro van hacia los costados a perderse nuevamente. brilla menos aún. un poquito menos y el tercero. Dicho en otras palabras. no se trata más que de un conteo con index desde 0 hasta 15. No olvides simular este programa si es que te quedan algunas inquietudes. Se trata de un secuencial de 8 canales donde aparecen tres LEDs encendidos por cada lado. los 16 elementos (de ahí el conteo hasta 15) de la tabla LedsTable serán enviados al puerto B ordenada y cíclicamente. esta vez nos encontramos con un reto algo mayor. El chiste de todo esto es que el primer LED alumbra a su plenitud. como la colita de un cometa. al llegar a este valor final se resetea el conteo para reiniciar todo de nuevo. Práctica 8-4 Un secuencial de verdad Si no te agradó mucho el anterior programa.Descripción del programa Como había dicho. El valor de index se envía a la subrutina LedsTable para sacar un elemento del secuencial.

El diagrama de flujo .

Bank 0 0x0C . la operación . . Estas 9 líneas realizan . Purpose: Tablas de búsqueda . Estas 4 líneas preguntan si ¿Index < 6? (Cada bloque tiene 6 ítems) Si es así. . Obtener dato de patrón . Processor: PIC16F84A . F d'111' . Block = 0 . Index = 0 Count . . W Pattern PORTB delay_130us Index. F Item.asm . . F d'6' Index. Ponerlo en PORTB . Count = Count + 1 .**************************************************************************** processor PIC16F84A include <P16F84A. F Item. saltar a Loop2 Index . W Item Item. . Vector de Reset . . Index = Index + 1 . . Bank 1 . W Item. Count = 0 Block . . FileName: eSeq. Estas 4 líneas preguntan si . F Index. F Item. F Item. W STATUS.**************************************************************************** . . . . W = Pattern[Item]. F Item. . Item = 6×Block + Index .El código fuente . W = Item .inc> __config _XT_OSC & _WDT_OFF & _PWRTE_ON cblock Block Item Index Count endc org Start banksel TRISB clrf PORTB banksel PORTB MLoop clrf Loop0 clrf Loop1 clrf Loop2 movf movwf addwf addwf addwf addwf addwf movf addwf movf call movwf call incf movlw subwf btfss goto incf movlw Block. . Número de bloque actual Índice absoluto del ítem actual Índice relativo dentro de cada bloque Contador ordinario 0x0000 . . C Loop2 Count. PORTB salida .

Repetir todo otra vez . addwf . . F 0 b'10000001' b'10000001' b'10000001' b'10000001' b'10000001' b'10000001' 1 b'11000011' Pattern . . led1 111111111111111111111111111111. . Block retlw retlw retlw retlw retlw retlw . Index Index Index Index Index Index = = = = = = 0 1 2 3 4 5 . . Subrutina que contiene el patrón de efecto del secuencial .. f goto $ . . 1 0 0 . . . .20 segundos aprox. Block = Block + 1 . Entrada: W = Índice del elemento de la tabla . Prendido 2 de 6 partes . Si es así. 1 0 0 . * Cada bloque tiene 6 items Cada bloque se repite 111 veces Hay una pausa de 150 µs entre los ítems Hay 12 bloques en total Así que cada barrido dura 6 * 111 * 150 * 12 = 1. C Loop1 Block.**************************************************************************** cblock del0. W STATUS.subwf veces) btfss goto incf movlw subwf btfss goto goto Count. F d'12' Block. W STATUS. led2 100100100100100100100100100100. 1 0 0 . . Block retlw PCL. C Loop0 MLoop . Index = 0 ... ¿Count < 111? (Cada bloque se repite 111 . Estas 4 líneas preguntan si ¿Block < 12? (Porque tenemos 12 bloques) Si es así.1 nop return . . led1 led2 led3 . 1 0 0 . 1 1 1 . Prendido 1 de 6 partes .**************************************************************************** . . 1 1 0 . . saltar a Loop0 . .. del1 endc delay_130us movlw d'41' movwf del0 decfsz del0. . Prendido 6 de 6 partes .. led3 100000100000100000100000100000. saltar a Loop1 ..

. . Block retlw retlw retlw retlw retlw retlw . Block retlw retlw retlw retlw retlw retlw .. Block retlw retlw retlw b'01000010' b'01000010' b'11000011' b'01000010' b'01000010' 2 b'11100111' b'00100100' b'00100100' b'01100110' b'00100100' b'00100100' 3 b'01111110' b'00011000' b'00011000' b'00111100' b'00011000' b'00011000' 4 b'00111100' b'00011000' b'00011000' b'00011000' b'00011000' b'00011000' 5 b'00011000' b'00011000' b'00011000' b'00011000' b'00011000' b'00011000' 6 b'00111100' b'00100100' b'00100100' b'00111100' b'00100100' b'00100100' 7 b'01111110' b'01000010' b'01000010' b'01100110' b'01000010' b'01000010' 8 b'11100111' b'10000001' b'10000001' b'11000011' b'10000001' b'10000001' 9 b'11000011' b'00000000' b'00000000' . Block retlw retlw retlw retlw retlw retlw . Block retlw retlw retlw retlw retlw retlw . Block retlw retlw retlw retlw retlw retlw .... Index = 1 . . . Block retlw retlw retlw retlw retlw retlw .retlw retlw retlw retlw retlw . Block retlw retlw retlw retlw retlw retlw .

F Item. las dos formas más habituales se conocen como modulaciones PWM y PRM. PRM es la sigla de Modulación por Frecuencia de Pulsos. en inglés. . W Item STATUS. C Item. . . Block retlw retlw retlw retlw retlw retlw .retlw retlw retlw . Estas 10 líneas realizan . W Item. F Block. F Item. . a la segunda de las cuales se apega la onda generada por el patrón de efecto de nuestro secuencial. . F . . Block retlw retlw retlw retlw retlw retlw end movf movwf bcf rlf rlf movf addwf addwf movf addwf b'10000001' b'00000000' b'00000000' 10 b'10000001' b'00000000' b'00000000' b'00000000' b'00000000' b'00000000' 11 b'00000000' b'00000000' b'00000000' b'00000000' b'00000000' b'00000000' Block. Item = 6×Block + Index . Descripción del programa ¿Cómo conseguimos variar la intensidad de brillo de un LED? Regulando la cantidad de corriente que fluye por él. la operación . W Item. . Y ¿cómo variamos su corriente sin emplear un potenciómetro o algo por el estilo? Bueno. F Index.

Así un LED aparezca prendido la sexta parte del tiempo que otro. los bits 1 y 6 se prenden en dos de seis partes. Finalmente. y los bits 0 y 7 solo se prenden la sexta parte del tiempo. nuestros ojos no lo podrán percibir así. no significa que vaya a brillar 6 veces menos. este hecho puede ser aprovechable por otras interesantes aplicaciones. en la posición 2 se ejecutará el bloque 2 por 111 veces cíclicamente.Es el valor promedio de la corriente lo que cuenta para el brillo del LED. De hecho.com/index.htm. Block retlw retlw retlw retlw retlw retlw 2 b'11100111' b'00100100' b'00100100' b'01100110' b'00100100' b'00100100' Dado que los LEDs que parpadean dan miles de centelleos por segundo. solo veremos una disminución en su luminosidad. quitando los comentarios vemos que toda Pattern es una tabla de datos. No obstante. . Eso de los bloques es una división lógica hecha en el software utilizando índices relativos. Como se ve. Práctica 8-5 El Display 7 segmentos Un display 7 segmentos no es más que una matriz de 7 diodos LEDs dispuestos de forma que encendiéndolos apropiadamente se pueden formar los números del 0 al 9 y algunas letras del . Por ejemplo. debo mencionar que este programa fue inspirado en el led flasher 2 de Seiichi Inoue y lo puedes ubicar en el sitio web http://hobby_elec. Por otro lado. en este lapso los bits 2 y 5 siempre estarán activos. tampoco hay una proporción directa entre estos dos parámetros.piclist. Cada uno de los bloques de la tabla Pattern es una posición en el desplazamiento de los LEDs.

Como ves arriba. Algunos displays también cuentan con un led en la parte inferior llamado dp (decimal point). Este programa es una adaptación del dado electrónico de David Hobday incluido en los ejemplos de su compilador BoostC http://www. pensado para representar un punto decimal.com. Se ha incluido una pausa antirrebote para evitar múltiples jugadas con una sola pulsada. . Yendo a la práctica. Después. se comienza a mostrar pausadamente el valor incrementado de la jugada. El que vamos a emplear en esta práctica pertenece al segundo grupo y se muestra abajo. Se dividen en dos grupos: de ánodo común y de cátodo común.SourceBoost. mientras se va generando un número aleatorio basado en la duración del pulso.alfabeto. Funciona así: al presionar el botón de la jugada se muestra un tenue 6. cada LED del display está identificado por una letra entre a y g. Yo lo traduje al lenguaje ensamblador y cambié los LEDs comunes por el display 7-seg. Display 7-seg de cátodo común. al liberarse el botón. Para marcar este hecho se encenderá un led que bien podría ser el dp del display 7-seg. hasta que se detiene en un valor fijo.

El circuito .

El diagrama de flujo .

Prender LED indicador A jugar otra vez .asm . Pausa antirrebote de 30 ms .El código fuente . . . ¿i<=30? Sí No. Inicializar control de pausa Detener poco a poco Incrementar tiempo de pausa W = i Pausas variables W = i W = 100 . C Stopping PORTB. Purpose: Display 7-segmentos (Dado electrónico) . Sí clrf Stopping: incf movf call call movf sublw btfsc goto bsf goto i i. . . .inc> __config _XT_OSC & _WDT_OFF & _PWRTE_ON cblock 0x0C DiceVal i endc #define Button org Start: banksel clrf bsf banksel DisplayOff: clrf WaitPressed: btfsc goto movlw call btfsc goto TRISA PORTB Button PORTA PORTB Button $ . FileName: EDice. . .**************************************************************************** . Genera número pseudo-aleatorio call DiceCount . tal vez fue un rebote . salida Pin de botón. Bank 1 Todo PORTB.W ¿C = 1?. entrada Bank 0 PORTA. Esperar botón pulsado . . . F i. . Contar dado mientras el botón está pulsado .**************************************************************************** . Llamar DiceCount btfss Button . Esperar botón libre . Vector de Reset . . Processor: PIC16F84A . .**************************************************************************** processor PIC16F84A include <P16F84A. Un contador . Sí WaitReleased: . ¿Aún pulsado? . W delay_x10ms DiceCount i. 7 WaitPressed . puerto del botón .1 d'3' delay_x10ms Button DisplayOff . . Apagar Display . ¿Aún pulsado? goto WaitReleased . 4 0x000 . W d'30' STATUS. No. Valor actual del dado . Valor definitivo.

7-segment code of 5 retlw b'01111101' . del2 endc delay_x10ms movwf del2 . Mostrarlo en el display return . W . 7-segment code of 1 retlw b'01011011' . F retlw b'00111111' . ¿DiceVal > 6? goto Display . Subroutine: DiceCount . x=don't care . Purpose: Incrementa y muestra el valor de DiceVal .**************************************************************************** BinTo7Seg addwf PCL. 7-segment code of 3 retlw b'01100110' . f .**************************************************************************** . F goto ldel return end . Incrementar valor movf DiceVal. Convertir a 7 segmentos movwf PORTB . Purpose: Convierte un número a formato 7 segmentos . Purpose: Genera decenas milisegundos según el valor de entrada . W . 7-segment code of 4 retlw b'01101101' . Subroutine: delay_x10ms . C . 7-segment code of 6 .**************************************************************************** . 7-segment code of 2 retlw b'01001111' . Input: W = Número a convertir . W = DiceVal sublw d'6' . Subroutine: BinTo7Seg . W = DiceVal call BinTo7Seg . No movlw d'1' .. del1. Input: W = decenas de ms a producir . ¿C = 0?. f goto $ . DiceVal = 1 Display: .**************************************************************************** cblock del0. Output: W = Número en formato 7 segmentos "xgfedcba". Sí.. movwf DiceVal . f goto $ + 2 decfsz del1.. del2 = W ldel movlw d'8' movwf del1 movlw d'207' movwf del0 decfsz del0.**************************************************************************** DiceCount: incf DiceVal. W = 6 .3 decfsz del2.W btfsc STATUS. 7-segment code of 0 retlw b'00000110' . Mostrar en el display 7 segmentos del dado movf DiceVal.

y g con 6). Ya que el bit 7 quedaba libre. haremos un contador que se incremente cada vez que se pulse un botón y se decremente cuando se pulse otro. otro para aquello... Esta tabla contiene los códigos 7-seg de los números del 1 al 6. Actualmente. Inclusive a un mínimo de ellos se le puede dotar de múltiples funciones para el completo control de algún proceso. podemos encontrar este paradigma en casi cualquier artefacto digital. . b con 1. Un botón para esto. Pero aquí no termina la cosa. ¿Qué pasaría si alguien presionara un botón sin antes haber soltado el anterior? Eso queda a criterio del diseñador y su aplicación. Ante el inconveniente de disponer de un único display-7seg. el display está conectado a los 7 primeros pines de PORTB del PIC (a con 0.Descripción del programa Tal vez un punto que no está explícito en el flowchart es la subrutina BinTo7Seg. según la razón establecida (velocidad de repetición).velocidad de repetición. se iniciará un incremento/decremento continuo. nuestro contador avanzará cíclicamente de 0 a F (en hexadecimal) o al revés.. decidí dejarlo en cero para utilizarlo luego como el LED que señaliza el fin de la jugada. El algoritmo que presento es mi preferido y tiene la ventaja de ser fácilmente acoplado a cualquier otro programa. De acuerdo con el circuito.. Como solo tenemos la intención aprender. gran parte de la interface de usuario de los aparatos electrónicos implicaba la presencia de algún tipo de potenciómetros y switches. De hecho. Si un botón permanece apretado por más de cierto tiempo (retrazo de repetición). En este programa yo decidí dejar sin efecto al segundo botón mientras el primero esté pulsado. El programa implementa el concepto que tantas veces leemos como retrazo de repetición . Un buen diseñador siempre debe pensar que el usuario final es bastante traviezo y torpe. Está de más decir que los rebotes deben ser filtrados. en los tiempos analógicos. una característica de los sistemas digitales es su manipulación basada en botones. Práctica 8-6 Retrazo de repetición y velocidad de repetición Antes.

Este flowchart corresponde a la subrutina que procesa el estado del botón A según el estado del botón B.El circuito El flowchart Para que un código sea reutilizable es preferible empaquetarlo en forma de una subrutina. . ProcessA es la subrutina que en nuestro caso incrementa el contador.

El código fuente .**************************************************************************** .velocidad de repetición . FileName: DrepRrep.inc> __config _XT_OSC & _WDT_OFF & _PWRTE_ON . Purpose: Retrazo de repetición .**************************************************************************** processor PIC16F84A #include <P16F84A.asm . Processor: PIC16F84A .

En otro caso PA se resetea. frecuencia de 1/(50×PA). . Es decir PA sólo se incrementa si botón A está pulsado (ButtonA=0) y . No . W STATUS. Proceso A . . Yes . Bank 1 PORTB salida Entrada de botón Entrada de botón Bank 0 .cblock Count PA PB endc #define ButtonA #define ButtonB org Start banksel clrf bsf bsf banksel clrf clrf clrf MLoop call call call call goto 0x0C . 4 PORTA. ¿PA = 7? . Botón decrementador . Cuenta el tiempo que ButtonA está pulsado . Z TstPA2 ProcessA exitA (. Inicializar PB . Cuando PA alcance el valor 7 se llamará la subrutina ProcessA y . . F . botones A y B. .**************************************************************************** CheckButtonA btfsc ButtonA .240) PA.7 + . No . F STATUS. . W .7 PA. Botón incrementador . luego de llegar a 240 ProcessA será llamada continuamente con una . ¿PB = 0? . Yes . Inicializar PA . . x = 3ms . . La subrutina cuenta los periodos (PA) que botón A está pulsado solo. Vector de Reset . Retrazo de repetición de 240x . . Overview: Debería ser llamada periódicamente para revisar el estado de los . . . ¿ButtonA = 0? goto RstPA . Visualizar contador . Yes. botón B está libre (PB=0). 3 0x0000 TRISB PORTB ButtonA ButtonB PORTB PA PB Count delay_3ms CheckButtonA CheckButtonB Display MLoop .**************************************************************************** . . Z RstPA PA. No movf btfss goto IncPA TstPA1 incf movlw subwf btfss goto call goto TstPA2 movlw subwf PB. Cuenta el tiempo que ButtonB está pulsado PORTA. Inicializar contador . Antirrebote de 7x .

. La subrutina cuenta los periodos (PB) que botón B está pulsado solo. Overview: Muestra el valor del nibble bajo de Count en el display 7-seg. Velocidad de repetición de 1/50x subwf PB. ¿PA >= (7+240)? . . Resetear PB exitB return . .7 . Mostrarlo en el display return . W btfss STATUS.50 PA. luego de llegar a 240 ProcessB será llamada continuamente con una .240) . ¿PA = 0? goto RstPB . No movf PA. Z . W btfss STATUS. Z . Tomar sólo nibble bajo call BinTo7Seg . W . No .**************************************************************************** CheckButtonB btfsc ButtonB . Cuando PB alcance el valor 7 se llamará la subrutina ProcessB y .**************************************************************************** . Retrazo de repetición de 240x subwf PB.**************************************************************************** Display movf Count. Proceso B goto exitB TstPB2 movlw (. Es decir PB sólo se incrementa si botón B está pulsado (ButtonB=0) y . F . No IncPB incf PB.7 + .btfss goto call movlw subwf goto RstPA exitA clrf return STATUS. botón A está libre (PA=0).**************************************************************************** . En otro caso PB se resetea. . Antirrebote de 7x subwf PB. No call ProcessB . ¿PB >= (7+240)? goto exitB . Proceso de A . ¿PB = 7? goto TstPB2 . F . F goto exitB RstPB clrf PB . Procesos A y B . ¿ButtonB = 0? goto RstPB . .50 . No call ProcessB . Convertir a 7 segmentos movwf PORTB . . Proceso de B movlw . Yes. Velocidad de repetición de 1/50x . Yes TstPB1 movlw .**************************************************************************** . C . . F exitA PA . C exitA ProcessA . Overview: Debería ser llamada periódicamente para revisar el estado de . Resetear PA . los botones A y B. W = Count andlw 0x0F . frecuencia de 1/(50×PB). Yes btfss STATUS.

**************************************************************************** cblock del0.**************************************************************************** ProcessA incf Count.. F goto $ . Notes: Están calculadas para un XTAL de 4 MHz.**************************************************************************** . 7-segment code of 8 retlw b'01101111' .**************************************************************************** . 7-segment code of D retlw b'01111001' . F return . 7-segment code of F . Genera 3 ms + 5 µs movlw d'3' .6 return end . delay_ms . 7-segment code of 4 retlw b'01101101' . del1 endc delay_3ms .2 decfsz del0. Output: W = Número en formato 7-seg code . 7-segment code of 3 retlw b'01100110' . x = don't care . 7-segment code of 6 retlw b'00000111' . . F goto $ . 7-segment code of B retlw b'00111001' . 7-segment code of A retlw b'01111100' . F retlw b'00111111' . Overview: Producen los tiempos indicados. . Genera W ms + 4 µs movwf del0 movlw d'249' movwf del1 nop decfsz del1. 7-segment code of 5 retlw b'01111101' . 7-segment code of 2 retlw b'01001111' . Output format: b'xgfedcba' .**************************************************************************** BinTo7Seg addwf PCL. Todas incluyen call y return. 7-segment code of 0 retlw b'00000110' . Input: W = Número a connvertir . F return ProcessB decf Count. 7-segment code of E retlw b'01110001' . Subroutines: demoras . Overview: Convierte un número en formato 7-seg . 7-segment code of 1 retlw b'01011011' . 7-segment code of 7 retlw b'01111111' . 7-segment code of 9 retlw b'01110111' . 7-segment code of C retlw b'01011110' .

Tras ser detectada la señal negativa (botón pulsado). te sugiero que revises el flowchart y el código fuente del programa. es que permite atender a los botones sin necesidad de que el programa se trabe esperando a que se pulsen o liberen.Descripción del programa Quedaba pendiente aclarar que las dos subrutinas CheckButtonA y CheckButtonB son gemelas y su funcionalidad la puedes leer en sus respectivos encabezados. entre 0 y FF (en hexadecimal). . Según mi buble principal. x = 3ms . El delay de 3 ms. Visualizar contador Si sigues preguntándote de dónde rayos salen esos cálculos. Allí dice que se llaman periódicamente.. Eso servirá para filtrar los pequenos pulsos de rebote. En su lugar.. Para terminar. bla. MLoop call call call call goto delay_3ms CheckButtonA CheckButtonB Display MLoop . el programa siguirá testeando el pin continuamente durante 7x = 21 ms. Una característica notable de este mecanismo. tomando solo el nibble bajo se consigue el mismo resultado. bla. lo que deriva en un retrazo de repetición de 240x = 720 ms y una velocidad de repetición de 1/50x = 6.666 incrementos/decrementos por segundo. bla. que en este programa es tiempo muerto. habrás notado que no puse ninguna acotación al contador en el rango 0 ± F como lo pedía el enunciado. Solo se reconocerá como válido el pulso que permaneció estable en este lapso. el periodo indicado es x = 3 ms. podría ser sustituida en otros diseños por tareas prácticas. he dejado que avance en toda su amplitud. comparable solo con las interrupciones. Después de todo. De paso aprenderás a modificar esos parámetros.