Documentos de Académico
Documentos de Profesional
Documentos de Cultura
CPU
1
8.1. Introducción
Estos dispositivos están organizados para aumentar al máximo sus características de fiabilidad
del sistema, minimizar el costo de elementos externos, modo de trabajo en bajo consumo así
como protección de código. Los modos de trabajo de que dispone son los siguientes:
Selección de oscilador
Reset:
o Power-on Reset (POP)
o Power-Timer (PWRT)
o El oscilador Salida-a el Cronómetro (OST)
o Castaño-fuera Restablezca (BOR)
Interrupciones
Watchdog
SLEEP
Localizador ID
Programación serie en circuito
Programación en bajo voltaje
Degugger en circuito
Estos dispositivos tienen un Timer Watchdog, que puede ser habilitado a través de los bits de
configuración. Dispone de un oscilador RC interno muy fiable. Dispone de dos temporizadores
de Power-up. Uno asociado al oscilador Start-up (OST), pensado para funcionar desde que se
genera un reset hasta que el oscilador de cristal sea estable. El otro es un temporizador asociado
al Power-up Timer (PWRT) que proporciona un retardo de 72 ms (nominal) solo con un Power-
up.
Está diseñado para esperar que el resto de los periféricos del circuito se estabilicen en su
funcionamiento.
El modo SLEEP está diseño para tener un consumo muy bajo de la fuente de alimentación. El
usuario puede salir del modo SLEEP a través de un reset externo, un desbordamiento del
Watchdog o al generarse una interrupción. También dispone de varias opciones del oscilador
para diversas aplicaciones. El oscilador RC ahorra costes al sistema. Mientras que la opción LP
ahorra consumo. La configuración se realiza en el Registro de Configuración.
Alguna características especiales de la CPU ya fueron estudiadas anteriormente, por lo tanto las
características que se van analizar en este capítulo son: SLEEP y Watchdog. Además, se va a
estudiar la lectura y escritura de las memorias EEPROM y flash.
También llamado por el inglés Power Down Mode o Sleep Mode, es un estado particular de
funcionamiento del PiCmicro utilizado, para reducir el consumo de corriente en los momentos en
que el PICmicro no realiza ninguna tarea o está a la espera de un suceso externo.
2
Si se toma como ejemplo un control remoto para TV, se ve que la mayor parte del tiempo el
PICmicro permanece a la espera de la presión de alguna tecla. Apenas oprimida, efectúa una
breve transmisión y queda nuevamente a la espera de la presión de otra tecla.
El tiempo de uso efectivo de la CPU del PICmicro está, por lo tanto, limitado a unos pocos
milisegundos necesarios para efectuar la transmisión, mientras que por varias horas no efectúa
ninguna tarea particular.
Para no consumir inútilmente la energía de las baterías, es posible apagar varios de los circuitos
del PICmicro y reencenderlos sólo en correspondencia con algún suceso externo.
La instrucción SLEEP es utilizada para colocar el PICmicro en Power Down Mode y reducir, en
consecuencia, la corriente absorbida, que pasará de unos 2 mA (a 5 volt y el clock en 4MHz) a
unos 2uA, o sea, unas 1000 veces menos.
Para entrar en Power Down Mode basta insertar la siguiente instrucción en cualquier parte del
programa:
SLEEP
Cualquier instrucción siguiente a SLEEP no será efectuada por el PIC el cual finalizará en este
punto la ejecución, apagará los circuitos internos, excepto aquellos necesarios para mantener el
estado de los puertos de I/O y aquellos que lo sacarán de esa condición, los cuales se hablarán
enseguida. Es decir, las líneas de E/S digitales que se utilizaban mantienen su estado, las que no
se empleaban reducen al mínimo su consumo, se detienen los temporizadores y tampoco opera el
conversor A/D.
Los circuitos conectados a los puertos de salida, deben ser diseñados de modo de limitar el
consumo en la condición de POWER DOWN. Otra recomendación de Microchip es conectar al
positivo ( Vdd ) o al negativo ( Vss ) todas las líneas en alta impedancia no usadas.
Para despertar al PIC y pasar a ejecutar la instrucción direccionada por PC+1 existen varias
causas.
1. Reset del PIC activando la patita externa MCLR (llevando a cero el pin MCLR)
2. Desbordamiento del WDT que sigue trabajando en reposo (Timeout del watch dog timer
si está habilitado)
3
3. Generación de interrupción por la activación del pin RB0/INT o por cambio de estado en
los 4 pines de más peso del puerto B
4. Interrupción originada por alguno de los nuevos periféricos como:
a. Lectura o escritura en la puerto paralelo (PSP)
b. Interrupción del TMR1 en modo contador asíncrono
c. Interrupción del módulo CCP en modo captura
d. Disparo especial del TMR1 funcionando en el modo asíncrono con reloj externo.
e. Interrupción en el módulo de comunicación SSP (bit Start/Stop)
f. Transmisión o recepción del MSSP en modo esclavo (SP1/I2C)
g. Transmisión o recepción del USART (en modo esclavo)
h. Fin de la conversión A/D cuando la fuente de su reloj es RC interna
i. Fin de la operación de escritura sobre la EEPROM.
En los dos primeros casos, el PIC es reseteado y la ejecución es retomada en la locación 0x00
de memoria.
En los dos últimos casos, el PIC se comporta como en el caso de una interrupción normal,
siguiendo primeramente, el Interrupt handler retomando la ejecución después de la
instrucción SLEEP. Para que el PIC sea despertado por una interrupción debe ser habilitado
los bits de los registros INTCON, PIE1 y PIE2.
De esto es fácil deducir que los periféricos que emplean la señal del reloj principal del sistema,
no pueden despertar a la CPU del estado de reposo.
Ejemplo 1
El siguiente ejemplo establece el uso del Power Down Mode y la forma de "despertarlo", la
modalidad usada para despertarlo es con la interrupción por flanco descendente aplicado al pin
RB0/INT utilizando un pulsador.
Para sacar al PIC del Power Down Mode, bastará oprimir SW1 para generar una interrupción y
retornar a la ejecución del programa.
4
El programa utilizado es PDM.ASM y se muestra a continuación:
;**************************************************
; Pic by example
;
; PDM.ASM
; Power Down Mode example
;
;**************************************************
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"
;Define variables
SWITCH1 EQU 0
SWITCH2 EQU 1
LED1 EQU 2
;Define variables
CBLOCK 0x20
;Contador de 16 bit usado en la subrutina delay
Count
Count1
ENDC
;Reset Vector
ORG 00H
goto Start
;**********************************************************************
; Interrupt vector
; Start point for every interrupt handler
;**********************************************************************
ORG 04H
;**********************************************************************
; Interrupt handler
;**********************************************************************
;**********************************************************************
; Main body
5
;**********************************************************************
Start:
bsf STATUS,RP0 ;Swap to data bank 1
bcf OPTION_REG,INTEDG
bcf STATUS,RP0 ;Swap to data bank 0
;**********************************************************************
; Main loop
;**********************************************************************
MainLoop
;Turn LED1 on
TurnOnLed1
bsf PORTB,LED1
goto MainLoop
TurnOffLed1
bcf PORTB,LED1
goto MainLoop
;**********************************************************************
; Software delay
;**********************************************************************
Delay
clrf Count
clrf Count1
DelayLoop
decfsz Count,1
goto DelayLoop
decfsz Count1,1
goto DelayLoop
return
END
6
Análisis del código
Registro OPTION_REG
Registro INTCON
Programa Principal. Se prende y apaga el LED1 con un tiempo de la subrutina Delay, hasta que
se pulse SWITCH2 que produce la ejecución de la instrucción SLEEP, poniendo al PIC en
estado de reposo, y el LED1 se mantendrá prendido o apagado dependiendo en el momento que
se pulsó SWITCH2. Para sacar al PIC del estado de reposo se pulsa el SWITCH1 produciendo
una interrupción RB0/INT, para continuar el programa desde la siguiente instrucción del SLEEP.
El Watch Dog Timer (que podría traducirse como temporizador perro guardián) tiene la
capacidad de mejorar la confiabilidad de los circuitos basados en el PIC.
El Watch Dog Timer es, en la práctica, un oscilador interno al PIC, pero completamente
independiente del resto de la circuitería, cuya función es eliminar eventuales bloqueos de la
CPU del PIC y resetearlo para que retome la ejecución normal del programa.
Para poder eliminar un eventual bloqueo de la CPU durante la ejecución del programa principal,
se inserta en el programa la siguiente instrucción especial:
7
La cual pone en cero a intervalos regulares el WDT no permitiéndole llegar al final de su
temporización. Si la CPU no realiza esta instrucción antes del término de la temporización,
entonces, se asume que el programa se ha bloqueado por algún motivo y se efectúa el reset de la
CPU.
El temporizador del Watchdog tiene su propio reloj de funcionamiento por lo que no depende
del oscilador principal del sistema, esto permite que, cuando el WDT está‚ habilitado y el
temporizador se desborde por no borrarlo a tiempo (aún estando en reposo), pueda ocasionar
una detención en el flujo de ejecución de instrucciones del programa (Watchdog Timer Reset).
Es evidente que esto no puede considerarse una interrupción como las que pueden ocasionar el
temporizador TMR0, por ejemplo, pero hay que tenerlo en cuenta ya que al igual que otras
interrupciones también puede despertar al microcontrolador del estado de reposo SLEEP y que
con una simple lectura de los bits correspondientes se sabe quién lo despertó.
Cuando se envía al microcontrolador a SLEEP existen varias formas de despertarlo, uno de los
eventos si al programar el microcontrolador se habilita el WDT, el despertar tendrá lugar si se
excede del tiempo prefijado sin borrarlo. Es decir, el WDT de los PIC16F87x es un contador que
funciona con los impulsos de su propio oscilador y que provoca un reset cuando se desborda en
funcionamiento normal. Si el desbordamiento se produce cuando el microcontrolador se halla en
estado de reposo, se despierta y sigue su comportamiento normal.
En la figura se muestra un esquema con los bloques principales que constituyen el perro guardián
o Timer Watch Dog:
Las instrucciones CLRWDT y SLEEP borran o ponen a cero el valor del contaje del WDT y el
postdivisor. Si se ejecuta la instrucción CLRWDT y el predivisor de frecuencia esta asignado al
perro guardián, se borra, pero no cambia su configuración.
8
Registros asociados al Watch Dog
Dirección Nombre Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
(1)
2007 Bit’s de config BODEN(1) CP1 CP0 PWETE(1) WDTE FOSC1 FOSC0
81h,181h OPTION_REG #RBPU INTEDG T0SC T0SE PSA PS2 PS1 PS0
Actuando sobre el bit PSA del registro OPTION_REG es posible asignar el preescaler al WDT
para obtener tiempos de intervenciones mayores. El bit PSA va seteado a uno con la instrucción:
Según los valores de los bits PS0, PS1 y PS2 del OPTION_REG se puede obtener distintos
intervalos de retardo. La elección correcta deberá ser hecha teniendo en cuenta el máximo
retardo que se logre obtener en el programa tras la ejecución de dos instrucciones CLRWDT
sucesivas.
En la tabla siguiente se muestra los retardos obtenibles para un reloj de 4 Mhz, según los valores
de PS0, PS1 y PS2:
Como se puede ver en la tabla, el periodo mínimo alcanzado en el cual la CPU es reseteada es de
unos 18 ms (depende de le temperatura y de la tensión de alimentación). Es posible, sin
embargo, asignar el preescaler al WDT a fin de obtener retardos mayores hasta unos 2,3
segundos.
9
Ejemplo 2
Para el uso del WDT se utiliza el mismo circuito del ejemplo anterior y tomando como base el
código anterior PDM.ASM, porque en la práctica no difiere mucho.
Apenas se presiona SW2 la CPU entrará en un loop ( StopLoop ) sin ejecutar CLRWDT.
Transcurridos unos 2,3 segundos, el WDT efectuará el reset de la CPU y el LED comenzará a
destellar nuevamente.
Pero si se reprograma el PIC con la opción WDTE no habilitada, al presionar SW2 el LED se
apaga y permanece en esa condición.
;**************************************************
; Pic by example
;
; WDT.ASM
; Watch Dog Timer example
;
;**************************************************
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"
;Define variables
SWITCH1 EQU 0
SWITCH2 EQU 1
LED1 EQU 2
;Define variables
CBLOCK 0x20
;Contador de 16 bit usado en la subrutina delay
Count
Count1
ENDC
;Reset Vector
ORG 00H
goto Start
;**********************************************************************
; Interrupt vector
; Start point for every interrupt handler
;**********************************************************************
10
ORG 04H
;**********************************************************************
; Interrupt handler
;**********************************************************************
;**********************************************************************
; Main body
;**********************************************************************
Start:
bsf STATUS,RP0 ;Swap to data bank 1
bcf OPTION_REG,INTEDG
bsf OPTION_REG,PSA
bsf OPTION_REG,PS0
bsf OPTION_REG,PS1
bsf OPTION_REG,PS2
;**********************************************************************
; Main loop
;**********************************************************************
MainLoop
;Turn LED1 on
TurnOnLed1
bsf PORTB,LED1
11
goto MainLoop
TurnOffLed1
bcf PORTB,LED1
goto MainLoop
;**********************************************************************
; Software delay
;**********************************************************************
Delay
clrf Count
clrf Count1
DelayLoop
decfsz Count,1
goto DelayLoop
decfsz Count1,1
goto DelayLoop
return
END
Registro OPTION_REG
Registro
Registro INTCON
12
Poner el PRESCALER en el registro OPTION_REG al divisor requerido, por ejemplo
1:128 con PS0 = 1, PS1 = 1 y PS2 = 1.
Se cambia la sentencia SLEEP con las siguientes sentencias
StopLoop
goto StopLoop ;Stops CPU
Cuando se pulsa SWITCHE2 se va a un lazo infinito, lo que conlleva a que el timer WDT
termine con la cuenta del tiempo programado (2,304 seg), y se resetea el Micro
comenzando a ejecutarse el programa.
Ejemplo 3
Según el valor del interruptor RB0, se encenderán o apagarán los leds del puerto C pero, antes de
volver a mirar el valor de dicho interruptor, se introducirá al microcontrolador en estado de
reposo, del cual despertara al desbordarse el perro guardián, iniciándose de nuevo el proceso.
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"
;Reset Vector
13
org 0x000 ;Inicio del programa en la posición cero
;de memoria
nop ;Libre (uso del debugger)
nop
goto _inicio
org 0x005
_inicio
bsf STATUS,RP0 ;Ir banco 1
bcf STATUS,RP1
14
o Es posible ampliar el área de la memoria de datos no volátil EEPROM con posiciones
libres de la memoria FLASH.
Para leer y escribir ambos bloques de memoria no volátil se dispone de los siguientes seis
registros especiales de función (SFR):
EECON1
EECON2
EEDATA
EEDATH
EEADR
EEADRH
Para direccionar los 128 posiciones de memoria EEPROM (de datos) del PIC16F873 y 16F874 o
las 256 posiciones numeradas de 0x00 a 0xFF (de 0 a 255) del PIC16F876 y 16F877 se tiene una
palabra de 8 bits, por ello para escribir o leer en la memoria EEPROM solo hace falta; el registro
EEADR para direccionar la posición y el registro EEDATA para colocar el dato leído o escrito.
Pero, para poder escribir o leer datos en la memoria FLASH (programa) que puede tener hasta
8K palabras de 14 bits, hacen falta dos registros para direccionar las posiciones de memoria que
varían de 0 a 8K (0x000 a 0x3FFF), por ello se utiliza el registro EEADR que contiene los 8
valores menos significativos, concatenado con el registro EEADRH que contiene la parte alta de
la palabra de direccionamiento de memoria, así EEADRH:EEADR. De forma similar se utilizan
los registros EEDATA que contiene los 8 valores menos significativos, concatenado con el
registro EEADRH que contiene los 6 bits de mayor peso de las palabras de 14 bits, así
EEDATH:EEDATA. Los bits superiores que no se emplean se llenan con ceros.
Los valores escritos en la memoria de programa no tienen que ser necesariamente valores
correspondientes a instrucciones. Por tanto, los 14 bits almacenados pueden servir como
parámetros, números seriales, paquetes de 7 bits ASCII, etc. La ejecución de una posición de
la memoria de programa que contenga datos resulta en un código NOP.
15
8.4.1. Registros EECON1 (Dirección 18Ch) y EECON2
El registro EECON1 es el registro que contiene los bits de control del proceso de lectura o
escritura en la memoria EEPROM. A continuación se describen los bits de control del registro
EECON1
R/W-x U-0 U-0 U-0 R/W-x R/W-0 R/S-0 R/S-0
EEPGD --- --- --- WRERR WREN WR RD
Bit 7 Bit 0
bit 7: EEPGD: Programa / bit de selección de datos EEPROM
1= Acceso a la memoria de programa FLASH
0= Acceso a la memoria de datos EEPROM
(No se puede cambiar mientras la lectura o escritura está en proceso)
bit 6:4:No implementados: Se leen como “0”
bit 3: WRERR: Flag de error de escritura en la EEPROM
1 = El proceso de lectura se ha producido prematuramente
(se ha producido un Reset por MCLR o un WDT durante el proceso)
0 = Se ha producido el proceso de escritura con éxito
bit 2: WREN: bit de habilitación de escritura
1 = Permite inicializar el ciclo de escritura
0 = Inhibe la escritura
bit 1: WR: bit de inicio de escritura
1 = Cuando se le pone a 1 comienza el ciclo de escritura de la memoria no volátil. (El
bit se pone de nuevo a cero por hardware cuando la escritura se completa).
0 = Toma este valor cuando completa el ciclo de escritura de la memoria no volátil.
bit 0: RD: bit de inicio de lectura
1 = Cuando se le pone a 1 se inicia un ciclo de lectura. (El bit RD se pone a cero por
hardware).
0 = no ha comenzado el ciclo de lectura de la memoria no volátil.
El bit 4 EEIF del registro PIR2 es una bandera que se coloca a 1lógico cuando la operación de
escritura ha terminado, debe ser colocada a 0 por software. La bandera se puede usar cuando
deseamos trabajar con interrupciones.
Los pasos para efectuar una secuencia de lectura en la memoria EEPROM es la siguiente:
LEE_EEPROM
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;Cargar dirección del byte a leer en W
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
donde: ADDR_L y DATA_L son registros creados por el usuario; ADDR_L almacena la
dirección de la memoria EEPROM a ser leída y DATA_L almacenará el dato leído.
Al igual que en el caso anterior antes de realizar una operación se debe cargar la dirección de la
memoria en el registro EEADR y el dato debe ser cargado en el registro EEDATA.
ESCRIBE_EEPROM
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;EEADR = ADDR_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
17
movlw 0x55
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal
donde: ADDR_L y DATA_L son registros creados por el usuario; ADDR_L indica la dirección de la memoria
EEPROM en donde se va a grabar y DATA_L tiene el dato a grabar.
Luego que se inicia la escritura si se cambia el valor de WREN no detiene la grabación. El bit
WR solo debe ser colocado a 1-lógico después de que WREN ha sido puesto a 1-lógico. El bit
WR y el bit WREN no deben ser colocados a 1-lógico en la misma instrucción.
Cuando se completa un ciclo de escritura, el bit WR es colocado a 0 por hardware y el bit EEIF
se coloca a 1-lógico. El bit EEIF debe ser nuevamente colocado a 0-lógico por software.
Es similar al caso anterior para la lectura de la memoria EEPROM, con la diferencia que se debe
realizar la operación de la dirección de la memoria a ser leída en dos registros y el dato debe ser
leído en dos registros. El siguiente segmento de código muestra la secuencia de lectura a ser
operada:
LEE_FLASH
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;Cargar dirección baja a leer
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
18
bsf EECON1,EEPGD ;Selecciona la memoria FLASH
bsf EECON1,RD ;Habilita ciclo de lectura
Es similar al caso anterior para la escritura de la memoria EEPROM, con la diferencia que se
debe realizar la operación de la dirección de la memoria a ser escrita en dos registros y el dato a
ser escrito debe ser de dos registros. El siguiente segmento de código muestra la secuencia de
lectura a ser operada:
ESCRIBE_FLASH
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;EEADR = ADDR_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
19
movlw 0x55
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal
Hay condiciones en las cuales se desea proteger el dispositivo contra posibles grabaciones
indeseadas para ello existen mecanismos. Cuando se energiza el microcontrolador (Power-Up) el
bit WREN del registro EECON1 es puesto a 0-lógico, además hay un temporizador Power-Up
timer que demora 72 us antes de que se efectúe cualquier instrucción en el microcontrolador,
prohibiendo cualquier operación de escritura. El bit PWRTE de la palabra de configuración debe
ser puesto a 1-lógico al momento de grabar el PIC para que el temporizador Power-Up funcione.
Finalmente existe la secuencia de grabación y el bit WREN que controla el ciclo de escritura. En
el caso de la memoria FLASH se debe poner a 0 el bit WRT de la Palabra de Configuración, que
solo puede escribirse desde un grabador externo.
Dependiendo del valor del bit WRT y de los bits de Protección de código CP1 y CP0, de la
Palabra de Configuración, se consiguen diversas alternativas de protección contra escritura de la
memoria FLASH.
CONFIGURACIÓN DE BITS LECTURA ESCRITURA LECTURA ESCRITURA
POSICIONES DE FLASH
CP1 CP0 WRT INTERNA INTERNA ICSP ICSP
20
8.4.4. Registros asociados con la memoria de datos EEPROM/programa FLASH
Dirección Nombre Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 Valor en Valor en el
POR,BOR resto de
Reset
0Bh, 8Bh, INTCON GIE PEIE T0IE INTE RBIE T0IF INTF RBIF 0000 000x 0000 000u
10Bh,18Bh
10D h EEADR Redistro de direccionamiento de EEPROM xxxx xxxx uuuu uuuu
10F h EEADRH -- -- -- Parte alta de la direccionamiento de EEPROM xxxx xxxx uuuu uuuu
10C h EEDATA Registro de dato de la EEPROM xxxx xxxx uuuu uuuu
10E h EEDATH -- -- xxxx xxxx uuuu uuuu
18C h EECON1 EEPGD --- --- --- WRERR WREN WR RD x--- xxxx x--- u000
18D h EECON2 Registro2 de control EEPROM (registro físico no implementado)
8D h PIE2 -- (1) -- EEIE BCLIE --- --- CCP2IE -r-0 0—0 -r-0—0
0D h PIR2 -- (1) -- EEIF BCLIF --- --- CCP21F -r-0 0—0 -r-0 0--0
Ejercicio 4
Diseñar un programa que permita escribir (0x5A) y leer un byte en la posición 0x00 de la
memoria EEPROM, luego el dato leído se muestra en el puerto C.
Para la implementación del programa se usará dos rutinas: una que graba (ESCRIBE_EEPROM)
y una que lee (LEE_EEPROM). En ambos casos se define dos variables ADDR_L (0x20) y
DATA_L(0x21). Antes de llamar a la rutina ESCRIBE_EEPROM es necesario cargar la
dirección ADDR_L y el dato que deseamos grabar. Antes de invocar a la rutina LEE_EEPROM
se debe cargar la dirección a leer, al terminar la rutina se almacena el valor en DATA_L para que
sea usado desde el programa principal.
a) Diagrama de Flujo
21
b) Código del programa
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"
;Define variables
CBLOCK 0x20
ADDR_L
DATA_L
ENDC
;Reset Vector
_inicio
bsf STATUS,RP0 ;Ir banco 1
bcf STATUS,RP1
clrf TRISC ;PORTC salida
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
clrf PORTC ;Limpiar PORTC
clrf ADDR_L ;ADDR_L = 0x00
clrf DATA_L ;Limpiar registro de datos
start
movlw 0x5A ;DATA_L = 0x5A
movwf DATA_L
call ESCRIBE_EEPROM ;Llamar rutina de grabación
clrf EEDATA ;Limpiar EEDATA
clrf DATA_L ;Limpiar registro de datos
call LEE_EEPROM ;Llamar rutina de lectura
_bucle
movf DATA_L,W ;W = DATA_L
movwf PORTC ;PORTC = W
goto _bucle ;Ir _bucle
;********************************************************
; Subrutina ESCRIBE_EEPROM
;********************************************************
ESCRIBE_EEPROM
bcf STATUS,RP0 ;Ir banco 0
22
bcf STATUS,RP1
movf ADDR_L,W ;EEADR = ADDR_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
movlw 55h
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal
;********************************************************
; Subrutina LEE_EEPROM
;********************************************************
LEE_EEPROM
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;Cargar dirección a leer
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
end
23
Ejercicio 5
Ejercicio que permita escribir y leer una “palabra de memoria” en la memoria FLASH
Diseñar un programa similar al anterior que permita escribir (0x015A) y leer una palabra de
memoria en la posición 0x0100 de la memoria FLASH, luego el dato leído se muestra en los
puertos B y C.
Para la implementación del programa se usará dos rutinas: una que graba (ESCRIBE_FLASH) y
una que lee (LEE_ FLASH). En ambos casos se define las variables ADDR_L (0x20), ADDR_H
(0x21), DATA_L(0x22) y DATA_H(0x23). Antes de llamar a la rutina ESCRIBE_FLASH es
necesario cargar la dirección ADDR_H:ADDR_L y el dato que deseamos grabar. Antes de
invocar a la rutina LEE_FLASH se debe cargar la dirección a leer, al terminar la rutina se
almacena el valor en DATA_LH:DATA_L para que sea usado desde el programa principal.
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"
;Define variables
CBLOCK 0x20
ADDR_L
ADDR_H
DATA_L
DATA_H
ENDC
;Reset Vector
_inicio
bsf STATUS,RP0 ;Ir banco 1
bcf STATUS,RP1
clrf TRISB ;PORTB salida
clrf TRISC ;PORTC salida
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
clrf PORTB ;Limpiar PORTB
clrf PORTC ;Limpiar PORTC
clrf ADDR_L ;ADDR_L = 0x00
clrf ADDR_H ;ADDR_H = 0x00
clrf DATA_L ;Limpiar registro de datos parte baja
clrf DATA_H ;Limpiar registro de datos parte alta
start
;Ver el tamaño del programa para almacenar los datos en una dirección
;que no afecte al programa
movlw 0x00 ;ADDR_L = 0x00
movwf ADDR_L
movlw 0x01 ;ADDR_H = 0x01
movwf ADDR_H
movlw 0x5A ;DATA_L = 0x5A
movwf DATA_L
movlw 0x01 ;DATA_H = 0x01
movwf DATA_H
24
call ESCRIBE_FLASH ;Llamar rutina de grabación
clrf EEDATA ;Limpiar EEDATA de FLASH
clrf EEDATH ;Limpiar EEDATh de FLASH
clrf DATA_L ;Limpiar registro de datos parte baja
clrf DATA_H ;Limpiar registro de datos parte alta
call LEE_FLASH ;Llamar rutina de lectura
_bucle
movf DATA_L,W ;W = DATA_L
movwf PORTC ;PORTC = W
movf DATA_H,W ;W = DATA_H
movwf PORTB ;PORTB = W
goto _bucle ;Ir _bucle
;********************************************************
; Subrutina ESCRIBE_FLASH
;********************************************************
ESCRIBE_FLASH
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;EEADR = ADDR_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
movlw 55h
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal
end
Ejercicio 6
Elaborar un programa que lea la posición 0x00 de la memoria EEPROM, muestre el dato en el
PORTC, incremente el valor del dato en una unidad, grabe el nuevo valor en la posición 0x00 de
la memoria EEPROM. Demostrar que quitando la alimentación al microcontrolador se tiene el
dato almacenado.
a) Diagrama de Flujo
26
Nota. Los diagramas de flujo de las rutinas ESCRIBE_EEPROM y LEE_EEPROM son los
mismos que los que se mostraron en el programa anterior
;Define variables
CBLOCK 0x20
ADDR_L
DATA_L
ENDC
;Reset Vector
;********************************************************
; Subrutina ESCRIBE_EEPROM
27
;********************************************************
ESCRIBE_EEPROM
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;EEADR = ADDR_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
movlw 55h
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal
;********************************************************
; Subrutina LEE_EEPROM
;********************************************************
LEE_EEPROM
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;Cargar dirección a leer
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
end
28
Ejercicio 7
El primero graba 255 en la posición 0x00 de la EEPROM, luego 254 en la posición 0x01,
y así sucesivamente hasta 0 en la posición 0xFF.
El segundo programa lee el canal AN0 del ADC y según esa lectura obtiene una
dirección de 8 bits que usa para extraer un dato de la EEPROM y lo muestra en el
PORTC.
a) Diagrama de Flujo
Primer Programa
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"
;Define variables
CBLOCK 0x20
ADDR_L
DATA_L
ENDC
;Reset Vector
_graba
movfw ADDR_L
movwf DATA_L
comf DATA_L,F
call ESCRIBE_EEPROM ;Llamar rutina de grabación
incf ADDR_L,F
movfw ADDR_L
btfss STATUS,Z
goto _graba
_bucle
goto _bucle
;********************************************************
; Subrutina ESCRIBE_EEPROM
;********************************************************
ESCRIBE_EEPROM
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;EEADR = ADDR_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
movlw 55h
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal
end
30
Segundo Programa
list p=16f877 ;Comando que indica el Pic usado
include "p16f877.inc" ;Etiquetas genéricas para el Pic16F877
_leer
call _adc
call LEE_EEPROM
movf DATA_L
movwf PORTC
goto _leer
_adc
bsf ADCON0,GO ;Start A/D conversion
_espera
btfsc ADCON0,GO ;ADCON0 es 0? (la conversión esta
;completa?)
goto _espera ;No, ir _espera
movf ADRESH,W ;Si, W=ADRESH
movwf ADDR_L ;Muestra el resultado en PORTC
return
;********************************************************
; Subrutina LEE_EEPROM
;********************************************************
LEE_EEPROM
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movf ADDR_L,W ;Cargar dirección a leer
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADR
end
32