Está en la página 1de 32

Características Especiales de la

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.

8.2. EL MODO DE REPOSO O DE BAJO CONSUMO

8.2.1. Funcionamiento del Modo de Reposo o de Bajo Consumo

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.

8.2.2. La instrucción SLEEP

Se introduce el microcontrolador en este modo especial de funcionamiento cuando se ejecuta la


instrucción SLEEP, esta forma de trabajo se caracteriza por su bajo consumo y pareciera que el
PIC se ha “congelado”.

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.

Al entrar el microcontrolador en el modo de reposo, si estaba funcionando el WDT se borra pero


sigue trabajando.

8.2.3. ¿Cómo "despertar" al PIC?

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

Power Down Mode

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.

En la práctica, el LED D1 conectado a la línea RB2 será intermitente indicando la normal


ejecución del programa.

Oprimiendo SW2, el programa ejecutará la instrucción SLEEP, colocando al PIC en Power


Down Mode. El LED D1 permanecerá encendido o apagado, según el instante en el que SW2 es
oprimido.

Para sacar al PIC del Power Down Mode, bastará oprimir SW1 para generar una interrupción y
retornar a la ejecución del programa.

El circuito a ser realizado se presenta a continuación:

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"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;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

;Jump to main body of program.

goto Start

;**********************************************************************
; Interrupt vector
; Start point for every interrupt handler
;**********************************************************************

ORG 04H

;**********************************************************************
; Interrupt handler
;**********************************************************************

bcf INTCON,INTF ;Reset INTF flag


retfie ;Return to the main body

;**********************************************************************
; Main body

5
;**********************************************************************
Start:
bsf STATUS,RP0 ;Swap to data bank 1

;I/O lines definition on port B (0=output, 1=input)

bsf TRISB,SWITCH1 ;Switch 1


bsf TRISB,SWITCH2 ;Switch 2
bcf TRISB,LED1 ;Led 1

;Set to 0 the INTEDG bit on OPTION register


;to have an interrupt on the falling edge of RB0/INT

bcf OPTION_REG,INTEDG
bcf STATUS,RP0 ;Swap to data bank 0

bsf INTCON,GIE ;Enables interrupts


bsf INTCON,INTE ;Enables RB0/INT interrupt

bcf PORTB,LED1 ;Turn off LED1

;**********************************************************************
; Main loop
;**********************************************************************
MainLoop

btfss PORTB,SWITCH2 ;If switch2 is down enter in


sleep ;Power Down Mode

call Delay ;Software delay

;If LED1 in on then turn it off and viceversa

btfss PORTB,LED1 ;Led on ?


goto TurnOnLed1 ;No, turn it on
goto TurnOffLed1 ;Yes, turn it off

;Turn LED1 on

TurnOnLed1
bsf PORTB,LED1
goto MainLoop

;Turn LED1 off

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

En primer lugar configuramos los registros OPTION_REG e INTCON.

Registro OPTION_REG

#RBPU INTEDG T0SC T0SE PSA PS2 PS1 PS0


0
Bit 7 Bit 0

INTEDG = 0, la interrupción RB0/INT se realiza con el flanco descendente

Registro INTCON

GIE PEIE T0IE INTE RBIE T0IF INTF RBIF


1 1
Bit 7 Bit 0

GIE = 1, habilita las interrupciones globalmente


INTE = 1, habilita la interrupción RB0/INT

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.

Subrutuna de interrupción. Pone en 0 la bandera INTF.

Subrutuna Delay. Establece por software un tiempo de más o menos 1 seg.

8.3. EL WATCH DOG TIMER (WDT)

8.3.1. El Watch Dog Timer (WDT)

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:

CLRWDT (CLeaR Watch Dog Timer)

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.

8.3.2. Asignación del preescaler al WDT

En la figura se muestra un esquema con los bloques principales que constituyen el perro guardián
o Timer Watch Dog:

Nota: PSA y PS2:PS0 son bits del registro OPTION_REG.

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

Los bits sombreados no son usados por el Watch Dog

Para habilitar el WDT se debe, en la fase de programación, habilitar el flag WDTE de la


palabra de configuración. La modalidad de activación de este flag, depende del programador
usado.

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:

BSF OPTION_REG, PSA

En caso contrario, el preescaler será asignado al TIMER 0. Obviamente, asignando el preescaler


al WDT, no será posible usarlo con el TIMER 0 y viceversa.

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:

PS2 PS1 PS0 Divisor Retardo


0 0 0 1 18ms
0 0 1 2 36ms
0 1 0 4 72ms
0 1 1 8 144ms
1 0 0 16 288ms
1 0 1 32 576ms
1 1 0 64 1.152s
1 1 1 128 2.304s

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

Uso del WDT

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.

Al ejecutar el programa se verá destellar el LED 1, mientras tanto, se ejecuta continuamente la


instrucción CLRWDT para evitar que la CPU sea reseteada ( recordar de programar el PIC con
la opción WDTE habilitada ).

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.

El programa utilizado es WDT.ASM y se muestra a continuación:

;**************************************************
; Pic by example
;
; WDT.ASM
; Watch Dog Timer example
;
;**************************************************
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;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

;Jump to main body of program.

goto Start

;**********************************************************************
; Interrupt vector
; Start point for every interrupt handler
;**********************************************************************

10
ORG 04H

;**********************************************************************
; Interrupt handler
;**********************************************************************

bcf INTCON,INTF ;Reset INTF flag


retfie ;Return to the main body

;**********************************************************************
; Main body
;**********************************************************************
Start:
bsf STATUS,RP0 ;Swap to data bank 1

;I/O lines definition on port B (0=output, 1=input)

bsf TRISB,SWITCH1 ;Switch 1


bsf TRISB,SWITCH2 ;Switch 2
bcf TRISB,LED1 ;Led 1

;Set to 0 the INTEDG bit on OPTION register


;to have an interrupt on the falling edge of RB0/INT

bcf OPTION_REG,INTEDG

;Assign the PRESCALER to Watch dog timer

bsf OPTION_REG,PSA

;Set the PRESCALER to 1:128

bsf OPTION_REG,PS0
bsf OPTION_REG,PS1
bsf OPTION_REG,PS2

bcf STATUS,RP0 ;Swap to data bank 0

bsf INTCON,GIE ;Enables interrupts


bsf INTCON,INTE ;Enables RB0/INT interrupt

bcf PORTB,LED1 ;Turn off LED1

;**********************************************************************
; Main loop
;**********************************************************************
MainLoop

btfss PORTB,SWITCH2 ;If switch2 is down enter in


StopLoop
goto StopLoop ;Stops CPU

clrwdt ;Clear wtahc dog timer

call Delay ;Software delay

;If LED1 in on then turn it off and viceversa

btfss PORTB,LED1 ;Led on ?


goto TurnOnLed1 ;No, turn it on
goto TurnOffLed1 ;Yes, turn it off

;Turn LED1 on

TurnOnLed1
bsf PORTB,LED1
11
goto MainLoop

;Turn LED1 off

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

Análisis del código

En primer lugar configuramos los registros OPTION_REG, INTCON y .

Registro OPTION_REG

Registro

#RBPU INTEDG T0SC T0SE PSA PS2 PS1 PS0


0 1 1 1 1
Bit 7 Bit 0

INTEDG = 0, la interrupción RB0/INT se realiza con el flanco descendente


PSA = 1, habilita el timer WDT
Divisor 1:128
PS2 = 1
PS1 = 1
PS0 = 1

Registro INTCON

El registro INTCON se analizó en el ejemplo anterior.

Programa Principal. El LED1 se prende y apaga igual al ejercicio anterior.

Cambio que se realizan respecto al ejercicio anterior:

 Activar el bit WDTE de la palabra de control, con _WDT_ON.


 Asignar el PRESCALER a timer Watch dog en el registro OPTION_REG, con PSA = 1.

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

clrwdt ;Clear wtahc dog timer

Cuando no se pulsa SWITCHE2 se ejecuta la sentencia clrwdt, inicalizando a 0 el timer


WDT, lo que mantiene el LED1 repetidamente prendido o apagado.

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.

Subrutinas. Igual al ejercicio anterio.

Ejemplo 3

Ejemplo práctico del uso del WDT

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"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;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

clrf TRISA ;PORTA salida


clrf TRISB ;PORTB salida
clrf TRISC ;PORTC salida
clrf TRISD ;PORTD salida
clrf TRISE ;PORTE salida

bsf TRISB,0 ;RB0 como entrada

movlw b'11001111' ;Preescaler = 128


movwf OPTION_REG ;Asociado al perro guardian

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
clrf PORTC ;PORTC = 0
; clrwdt
_bucle
btfsc PORTB,0 ;RB0 = 0 ?
goto _apagar ;no
goto _encender ;si
_apagar
clrf PORTC ;limpiar puertoC
goto _reposo ;ir _reposo
_encender
movlw 0xff
movwf PORTC ;poner a 1 todo el puertoC
goto _reposo ;ir _reposo
_reposo
clrwdt ;refresca el perro guardián
sleep ;entrar en bajo consumo
goto _bucle ;ir a _bucle

end ;Fin de programa

8.4. LECTURA Y ESCRITURA DE LAS MEMORIAS EEPROM Y FLASH

8.4.1. Memoria de Datos EEPROM y Memoria FLASH de Programa

En esta familia de microcontroladores tanto la memoria EEPROM de datos como la memoria de


programa FLASH, pueden ser leídas y escritas durante la ejecución de un programa, sin
necesidad de utilizar un programador exterior y a la tensión nominal del microcontrolador VDD.
Es decir, un programa dinámicamente puede generar información que se puede grabar en la
memoria FLASH.

Por lo tanto podemos utilizar esta propiedad para que:

o La propia aplicación se puede programar según las condiciones externas

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.

1. La memoria EEPROM (de datos) es una memoria no volátil que se graba


eléctricamente, es útil porque permite almacenar valores o parámetros que sean
persistentes en la ejecución de la aplicación. Esto es, que no se borren frente a la
pérdida de energía o reset.

2. En la memoria FLASH (programa) se puede leer o escribir palabras. El acceso a la


memoria de programa se hace para el cálculo del checksum y la calibración de tablas de
almacenamiento. La escritura de un byte o de una palabra borra automáticamente la
localización seleccionada y coloca en ella el dato elegido. La escritura de la memoria de
programa cesa todas las operaciones hasta que es completada. La memoria de programa
no puede ser accedida durante un ciclo de escritura por tanto ninguna instrucción puede
ser ejecutada. Durante la operación de escritura el oscilador continúa trabajando los
demás módulos. Si sucede una interrupción esta queda en espera (en una suerte de cola)
hasta que el ciclo de escritura de la memoria de programa haya terminado. Terminado el
ciclo, ejecuta una instrucción mas (que se cargo antes de que empiece la grabación) y
luego salta al vector de interrupción.

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 registro EECON2, no está implementado físicamente y sólo se utiliza para la operación de


escritura, si se lee se encuentra que está lleno de ceros, de igual forma que se hace con el PIC
16F84, es decir, antes de iniciar la escritura de un dato en la memoria, se debe escribir en el
registro EECON2 primero el dato 55h y posteriormente el dato AAh.

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.

8.4.2. Subrutinas para la escritura y lectura de la memoria no volátil

1. Subrutina para la lectura de la memoria EEPROM de datos

Los pasos para efectuar una secuencia de lectura en la memoria EEPROM es la siguiente:

 Cargar el registro EEADR con la dirección que nos interesa acceder.


 Colocar a 0 lógico el bit EEPGD del registro EECON1 para indicar que se va a trabajar
en la memoria de datos (EEPROM).
16
 Colocar a 1lógico el bit RD del registro EECON1.
 En el siguiente ciclo de instrucción se puede obtener el contenido en el registro
EEDATA.

El siguiente segmento de código muestra un ciclo de lectura:

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

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;Selecciona la memoria EEPROM
bsf EECON1,RD ;Habilita ciclo de lectura

bcf STATUS,RP0 ;Ir banco 2


bsf STATUS,RP1
movf EEDATA,W ;W = EEDATA (leer dato de EEPROM)

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movwf DATA_L ;DATA_L = W (almacena dato de EEPROM)
return ;Retorno

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.

2. Subrutina para la escritura de la memoria EEPROM de datos

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.

El registro EEDATA contendrá el valor que se va escribir al ejecutar la operación de escritura. El


siguiente segmento de código muestra la secuencia de grabación a ser operada:

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

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf DATA_L,W ;EEDATA = DATA_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEDATA

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;selecciona la memoria EEPROM
bsf EECON1,WREN ;Habilitar escritura
bcf INTCON,GIE ;Deshabilita interrupciones

17
movlw 0x55
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal

bsf EECON1,WR ;Habilita el bit de escritura


bsf INTCON,GIE ;Habilita interrupciones
_bucle1
btfsc EECON1,WR ;Espera el final de grabación
goto _bucle1 ;Si no termina la grabación: Ir _bucle
bcf PIR2,EEIF ;Si termina Borra bandera de interrupción
bcf EECON1,WREN ;Deshabilitar escritura

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
return ;Retorno

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.

El fabricante indica que no se llevará a cabo el ciclo de grabación si en forma previa no se


escribe 0x55 y 0xAA en el registro EECON2, después se puede habilitar el ciclo de escritura con
el bit WR. Esta secuencia se debe realizar cada vez que se almacena un dato en la EEPROM.
Este mecanismo tiene por objetivo evitar la escritura accidental de datos indeseados. El bit
WREN debe ser puesto a 0-lógico todo el tiempo excepto cuando se actualice datos en la
EEPROM. El bit WREN no se coloca a 0 por hardware.

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.

3. Subrutina para la lectura de la memoria FLASH de código

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

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf ADDR_H,W ;Cargar dirección alta a leer
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADRH

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1

18
bsf EECON1,EEPGD ;Selecciona la memoria FLASH
bsf EECON1,RD ;Habilita ciclo de lectura

bcf STATUS,RP0 ;Ir banco 2


bsf STATUS,RP1
movf EEDATA,W ;W = EEDATA (leer dato de FLASH)
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movwf DATA_L ;DATA_L = W (almacena dato de FLASH)

bcf STATUS,RP0 ;Ir banco 2


bsf STATUS,RP1
movf EEDATH,W ;W = EEDATH (leer dato de FLASH)
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movwf DATA_H ;DATA_L = W (almacena dato de FLASH)
return ;Retorno

donde: ADDR_H:ADDR_L y DATA_H:DATA_L son registros creados por el usuario; ADDR_H:ADDR_L


almacenan la dirección de la memoria EEPROM a ser leída y DATA_H:DATA_L van almacenar el dato leído.

4. Subrutina para la escritura de la memoria FLASH de código

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

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf ADDR_H,W ;EEADRH = ADDR_H
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADRH

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf DATA_L,W ;EEDATA = DATA_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEDATA

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf DATA_H,W ;EEDATH = DATA_H
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEDATH

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bsf EECON1,EEPGD ;selecciona la memoria FLASH
bsf EECON1,WREN ;Habilitar escritura
bcf INTCON,GIE ;Deshabilita interrupciones

19
movlw 0x55
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal

bsf EECON1,WR ;Habilita el bit de escritura


bsf INTCON,GIE ;Habilita interrupciones
_bucle1
btfsc EECON1,WR ;Espera el final de grabación
goto _bucle1 ;Si no termina la grabación: Ir _bucle
bcf PIR2,EEIF ;Si termina Borra bandera de interrupción
bcf EECON1,WREN ;Deshabilitar escritura

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
return ;Retorno

donde: ADDR_H:ADDR_L y DATA_H:DATA_L son registros creados por el usuario;


ADDR_H:ADDR_L indican la dirección de la memoria EEPROM en donde se va a grabar y
DATO_H:DATO_L tienen el dato a grabar.

La operación de escritura de esta memoria dura aproximadamente 2 ms. El fabricante


recomienda la verificación de los datos escritos en las memorias EEPROM y FLASH para
comprobar que su grabación se ha realizado correctamente.

8.4.3. Protección contra escrituras indeseadas

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

0 0 X Toda la memoria de Programa SI NO NO NO


0 1 0 Áreas no Protegidas SI NO SI NO
0 1 0 Área Protegida SI NO NO NO
0 1 1 Área no Protegida SI SI SI NO
0 1 1 Área Protegida SI NO NO NO
1 0 0 Áreas no Protegidas SI NO SI NO
1 0 0 Área Protegida SI NO NO NO
1 0 1 Áreas no Protegidas SI SI SI NO
1 0 1 Áreas Protegidas SI NO NO NO
1 1 0 Toda la Memoria de Programa SI NO SI SI

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

Leyenda: x = Indeterminado, u = Permanece Invariable, r = reservado; -- No implementado se


lee como 0; No se usan los bits sombreados durante el acceso a FLASH/EEPROM.

Nota. Área de memoria reservada; estos bits se mantienen siempre a cero

Ejercicio 4

Ejercicio que permita escribir y leer un byte en la memoria EEPROM

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"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;Define variables
CBLOCK 0x20
ADDR_L
DATA_L
ENDC

;Reset Vector

org 0x000 ;Inicio del programa en la posición cero


;de memoria

_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

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf DATA_L,W ;EEDATA = DATA_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEDATA

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;selecciona la memoria EEPROM
bsf EECON1,WREN ;Habilitar escritura
bcf INTCON,GIE ;Deshabilita interrupciones

movlw 55h
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal

bsf EECON1,WR ;Habilita el bit de escritura


bsf INTCON,GIE ;Habilita interrupciones
_bucle1
btfsc EECON1,WR ;Espera el final de grabación
goto _bucle1 ;Si no termina la grabación: Ir _bucle
bcf PIR2,EEIF ;Si termina Borra bandera de interrupción
bcf EECON1,WREN ;Deshabilitar escritura

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
return ;Retorno

;********************************************************
; 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

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;Selecciona la memoria EEPROM
bsf EECON1,RD ;Habilita ciclo de lectura

bcf STATUS,RP0 ;Ir banco 2


bsf STATUS,RP1
movf EEDATA,W ;W = EEDATA (leer dato de EEPROM)

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movwf DATA_L ;DATA_L = W (almacena dato de EEPROM)
return ;Retorno

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"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;Define variables
CBLOCK 0x20
ADDR_L
ADDR_H
DATA_L
DATA_H
ENDC

;Reset Vector

org 0x000 ;Inicio del programa en la posición cero


;de memoria
;Libre (uso del debugger)

_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

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf ADDR_H,W ;EEADRH = ADDR_H
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADRH

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf DATA_L,W ;EEDATA = DATA_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEDATA

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf DATA_H,W ;EEDATH = DATA_H
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEDATH

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;selecciona la memoria EEPROM
bsf EECON1,WREN ;Habilitar escritura
bcf INTCON,GIE ;Deshabilita interrupciones

movlw 55h
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal

bsf EECON1,WR ;Habilita el bit de escritura


bsf INTCON,GIE ;Habilita interrupciones
_bucle1
btfsc EECON1,WR ;Espera el final de grabación
goto _bucle1 ;Si no termina la grabación: Ir _bucle
bcf PIR2,EEIF ;Si termina Borra bandera de interrupción
bcf EECON1,WREN ;Deshabilitar escritura

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
return ;Retorno
25
;********************************************************
; Subrutina LEE_FLASH
;********************************************************
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

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf ADDR_H,W ;Cargar dirección alta a leer
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEADRH

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;Selecciona la memoria FLASH
bsf EECON1,RD ;Habilita ciclo de lectura

bcf STATUS,RP0 ;Ir banco 2


bsf STATUS,RP1
movf EEDATA,W ;W = EEDATA (leer dato de FLASH)
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movwf DATA_L ;DATA_L = W (almacena dato de FLASH)

bcf STATUS,RP0 ;Ir banco 2


bsf STATUS,RP1
movf EEDATH,W ;W = EEDATH (leer dato de FLASH)
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movwf DATA_H ;DATA_L = W (almacena dato de FLASH)
return ;Retorno

end

Ejercicio 6

Ejercicio que permita leer y escribir un dato en la memoria EEPROM

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

b) Código del programa


PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;Define variables
CBLOCK 0x20
ADDR_L
DATA_L
ENDC

;Reset Vector

org 0x00 ;Inicio del programa en la posición cero


;de memoria
nop
nop
nop
_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
start
clrf ADDR_L ;ADDR_L = 0x00 (Variables en banco2)
call LEE_EEPROM ;Llamar rutina de lectura (Regresa en el
:banco0)
movfw DATA_L ;W = DATA_L
movwf PORTC ;PORTC = W
incf DATA_L,F ;incremento del dato leído de la EEPROM
call ESCRIBE_EEPROM ;Llamar rutina de grabación
_bucle
goto _bucle ;Ir _bucle

;********************************************************
; 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

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf DATA_L,W ;EEDATA = DATA_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEDATA

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;selecciona la memoria EEPROM
bsf EECON1,WREN ;Habilitar escritura
bcf INTCON,GIE ;Deshabilita interrupciones

movlw 55h
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal

bsf EECON1,WR ;Habilita el bit de escritura


bsf INTCON,GIE ;Habilita interrupciones
_bucle1
btfsc EECON1,WR ;Espera el final de grabación
goto _bucle1 ;Si no termina la grabación: Ir _bucle
bcf PIR2,EEIF ;Si termina Borra bandera de interrupción
bcf EECON1,WREN ;Deshabilitar escritura

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
return ;Retorno

;********************************************************
; 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

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;Selecciona la memoria EEPROM
bsf EECON1,RD ;Habilita ciclo de lectura

bcf STATUS,RP0 ;Ir banco 2


bsf STATUS,RP1
movf EEDATA,W ;W = EEDATA (leer dato de EEPROM)

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movwf DATA_L ;DATA_L = W (almacena dato de EEPROM)
return ;Retorno

end

28
Ejercicio 7

Diseñar dos programas: graba y lee

Diseñe dos programas:

 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

Nota: Los diagrama de flujos de las rutinas LEER_EEPROM y ESCRIBIR_EEPROM ya fueron


explicados. Escribir EEPROM sigue la lógica de los programas expuestos con anterioridad.

b) Código del programa

Primer Programa
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;Define variables
CBLOCK 0x20
ADDR_L
DATA_L
ENDC

;Reset Vector

org 0x000 ;Inicio del programa en la posición


;cero de memoria
29
nop
nop
nop
_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

_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

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
movf DATA_L,W ;EEDATA = DATA_L
bcf STATUS,RP0 ;Ir banco 2
bsf STATUS,RP1
movwf EEDATA

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;selecciona la memoria EEPROM
bsf EECON1,WREN ;Habilitar escritura
bcf INTCON,GIE ;Deshabilita interrupciones

movlw 55h
movwf EECON2 ;Escribe 55 hexadecimal
movlw 0xAA
movwf EECON2 ;Escribe AA hexadecimal

bsf EECON1,WR ;Habilita el bit de escritura


bsf INTCON,GIE ;Habilita interrupciones
_bucle1
btfsc EECON1,WR ;Espera el final de grabación
goto _bucle1 ;Si no termina la grabación: Ir _bucle
bcf PIR2,EEIF ;Si termina Borra bandera de interrupción
bcf EECON1,WREN ;Deshabilitar escritura

bcf STATUS,RP0 ;Ir banco 0


bcf STATUS,RP1
return ;Retorno

end

30
Segundo Programa
list p=16f877 ;Comando que indica el Pic usado
include "p16f877.inc" ;Etiquetas genéricas para el Pic16F877

ADDR_L equ 0x20


DATA_L equ 0x21

org 0x000 ;Inicio del programa en la posición cero


;de memoria
nop ;Libre (uso del debugger)
nop
nop
_inicio
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movlw b'01000001' ;A/D conversion Fosc/8
movwf ADCON0
bsf STATUS,RP0 ;Ir banco 1
bcf STATUS,RP1
clrf TRISA ;PORTA salida
clrf TRISC ;PORTC salida
movlw b'00001110' ;A/D Port AN0/RA0
movwf ADCON1
bsf TRISA,0
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
clrf PORTC ;Limpiar PORTC
clrf ADDR_L ;ADDR_L = 0x00

_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

bsf STATUS,RP0 ;Ir banco 3


bsf STATUS,RP1
bcf EECON1,EEPGD ;Selecciona la memoria EEPROM
bsf EECON1,RD ;Habilita ciclo de lectura

bcf STATUS,RP0 ;Ir banco 2


bsf STATUS,RP1
movf EEDATA,W ;W = EEDATA (leer dato de EEPROM)
31
bcf STATUS,RP0 ;Ir banco 0
bcf STATUS,RP1
movwf DATA_L ;DATA_L = W (almacena dato de EEPROM)
return ;Retorno

end

32

También podría gustarte