Está en la página 1de 70

Los Puertos Paralelos de

Entrada/Salida

1
3. Los Puertos Paralelos de Entrada/Salida
Los integrados PIC16F874 y PIC16F877 poseen 5 puertos de entrada/salida denominados
PORTA, PORTB, PORTC, PORTD y PORTE, mientras que el PIC16F873 y PIC16F876 poseen 3.
Estos puertos son totalmente programables, es decir, sus pines pueden ser configurados
para trabajar como entradas o como salidas a selección del programador.
3.1. El Puerto A (PORTA)

3.1.1. Registros asociados al Puerto A

El puerto A posee 6 pines bidireccionales. Los 3 registros asociados a este puerto son:

1. Registro PORTA (05H), que es el registro de estado del Puerto A. Cada uno de los 6 bits
menos significativos (RA5,...,RA0) de este registro están asociados al p i n físico
correspondiente del puerto. Al hacer una lectura este registro se lee el estado de todas los pines
del puerto. Todas las escrituras al registro son operaciones del tipo “lee- modifica-escribe”, es
decir, toda escritura al puerto implica que el estado de los pines es leído, luego es modificado y
posteriormente se escribe al latch de datos del puerto.

2. Registro TRISA (85H). Cada bit de este registro configura la dirección en que fluye la
información del pin correspondiente del puerto A, así, para k=0,1,...,5:

Bit k de TRISA = 1 configura el pin RAk del puerto A como Entrada


Bit k de TRISA = 0 configura el pin RAk del puerto A como Salida

Todos los pines del puerto A poseen diodos de protección conectados a Vdd (contra altos
voltajes de entrada) y a Vss (contra voltajes negativos) además, manejan niveles de entrada tipo
TTL y como salidas se comportan como drivers tipo CMOS. Excepto el pin RA4, e l cual
como entrada posee un Disparador Schmitt trigger y como salida es de Drenaje abierto, además
RA4 sólo posee un diodo de protección conectado a Vss.

3. El Registro ADCON1 (9FH). Los pines RA0, RA1, RA2, RA3 y RA5 están
multiplexados con las entradas analógicas AN0,...,AN4, respectivamente; de manera que
antes de utilizarlos se debe configurar si serán usadas como entradas analógicas o como
entradas/salidas digitales. Para seleccionar la segunda opción (entradas/salidas digitales) se
debe colocar en el nible menos significativa de este registro un 01102 (es decir, un 06h).

2
Registro ADCON1 (dirección 9Fh)

A continuación se indica el registro ADCON1:

U-0 U-0 R/W-0 U-0 R/W-0 R/W-0 R/W-0 R/W-0


ADFM --- --- --- PCFG3 PCFG2 PCFG1 PCFG0
Bit 7 Bit 0

bit 3-0: PCFG3:PCFG0: bits de configuración de los canales de entrada del convertidor A/D.
Se utilizan para configurar las patillas como E/S digital o como entrada analógica.

En las siguientes dos figuras se muestra el detalle de implementación interna de los pines del
puerto A, mostrando la diferencia entre los pines RA4 y los demás pines del puerto A:

Pines RA0, RA1, RA2, RA3 y RA5 Pines RA4

3.1.2. Estado de lectura y escritura de un pin de I/O

El PIC puede ser implementado en diversas topologías de estado de escritura para el pin de I/O,
teniendo en cuenta que los pines de los puertos E/S tienen igual comportamiento o ligeramente difiere
uno del otro. Conociendo el funcionamiento de diversos estados de escritura del puerto, se puede
aprovechar de mejor manera las características y optimizar el proyecto.

1. Estado de lectura y escritura de los pines RA0, RA1, RA2, RA3 y RA5

El esquema para estos pines muestra el estado de escritura extraído del “data sheet del Microchip”.
Por ejemplo, se toma el pin RA0 para analizar el funcionamiento del estado de salida cuando el
mismo funciona como entrada o como salida:
3
a. Funcionamiento como Entrada

Para configurar el pin RA0 como entrada, se debe poner a 1 el bit 0 del registro TRISA:

bsf TRISA, 0

Esta instrucción determina una conmutación a 1 del estado lógico del Flip Flop del latch D
indicado en el circuito con el nombre TRIS latch. Para otro pin de I/O existe uno de estos Flip
Flop y el estado lógico en que se mantiene depende estrictamente del estado lógico del bit
relativo al registro TRIS (en otras palabras, el mejor diseño de todos los bits del registro TRIS
es físicamente realizable con un latch TRIS).

La salida Q del latch TRIS es conectada a la entrada de una compuerta lógica de tipo OR, esto
significa que independientemente del valor presente en la otra entrada, la salida de la compuerta
OR siempre estará en uno si una de sus entradas están en uno. En esta condición, el transistor P
no conduce manteniendo el pin RA0 desconectado del positivo de la alimentación.

Del mismo modo la salida negativa Q del latch TRIS es conectada a la entrada de una
compuerta AND, en donde, la salida de ésta estará siempre en 0 mientras una de sus entradas
valga 0. En esta condición el transistor N no conduce manteniendo el pin RA0 desconectado de
la tierra. El estado lógico del pin RA0 dependerá exclusivamente del circuito externo al cual
estará conectado. Aplicando 0 o 5 volts al pin RA0, será posible leer el estado presente del
circuito externo en la entrada del bloque representado por “TTL input buffer” y el latch de
entrada.

b. Funcionamiento como Salida

Para configurar el pin RA0 como salida, se debe colocar 0 en el bit 0 del registro TRISA con la
instrucción:

bcf TRISA, 0

Esta instrucción determina la conmutación a cero de la salida Q del latch TRIS (a 1 si la salida
Q es negativa). En este estado el valor de la salida de las compuertas OR y AND dependen
exclusivamente del estado de salida del Q negado del latch de datos. Como para el latch TRIS,
el latch de datos depende del estado de un bit de un registro, particularmente del registro
PORTA. Su salida negativa será enviada a la entrada de las dos compuertas lógicas OR y AND
que está directamente sobre la base del transistor P y N. Si ponemos en 0 el bit 0 del registro
PORTA con la instrucción:

bcf PORTA, 0

Se obtendrá la conducción del transistor N y por tanto irá a 0 el pin RA0. Si se coloca en 1 el bit
0 con la instrucción:

bsf PORTA, 0

Se obtendrá la conducción del transistor P y por tanto irá a +5 V el pin RA0. Con esta condición
será siempre posible revisar el valor enviado sobre el pin a través del circuito de entrada.
4
2. Estado de Salida del pin RA4

Se analiza el funcionamiento del estado de salida del pin RA4 que es diferente de las otros pines de
I/O, en cuanto a la distribución del pin en el PIC16C84 con el TOCKI, lo cual se analizará luego.

Se describe el esquema de bloques del estado de salida extraído del data sheet. La lógica de
conmutación es substancialmente idéntica al grupo de pines RA0 a RA3 con ausencia de la
compuerta OR y del transistor P, o de todos los circuitos que permiten una conexión a positivo del
pin RA4. Esto significa en términos prácticos que cuando un pin RA4 está programado para salida,
podrá asumir un nivel que dependerá del circuito externo, pues en realidad no está conectada al
positivo y sí desconectada. Este tipo de circuito de salida se llama de “colector abierto” y es útil
para aplicaciones en que es necesario compartir una misma ligación con más pines de salida,
porque se tiene la necesidad de colocar en alta impedancia un pin de salida y pudiendo así
reprogramarla como pin de entrada.

Si se quiere asegurar que el pin RA4 sea 1 se debe conectar externamente un resistor de PULL-UP,
ya sea un resistor conectado al positivo de alimentación.

3.2. El Puerto B (PORTB)

3.2.1. Registros asociados al Puerto B

El puerto B es un puerto digital de 8 bits, todas sus pines son bidireccionales y trabaja en forma
similar al puerto A. Tiene tres registros asociados: el registro de datos PORTB, el registro de
dirección de los datos TRISB y el registro OPTION_REG.

1. Registro PORTB (06H, 106H). Los ocho bits que contiene reflejan directamente el estado de
los ocho pines del puerto B: RB0,...,RB7.

2. Registro TRISB (86H, 186H).- En forma similar a TRISA, al poner un 0 en un bit de TRISB
se configura el pin RB correspondiente como salida y al poner un 1 en un bit de TRISB se
configura el pin RB correspondiente como entrada.

3. Registro OPTION_REG (81H, 181H). El bit 7 de este registro, denominado RBPU es usado
para conectar/desconectar una resistencia “pull-up” conectada a cada pin RB. Poniendo un 0 en
este bit todas las resistencias se conectan. Para desconectar las resistencias “pull-up” se
debe poner este bit en 1, también se desconectan automáticamente cuando e l pin
correspondiente es configurado como salida. Un Reset desconecta todas las resistencias.

REGISTRO OPTION u OPTION_REG (Dirección 81h, 181h)

A continuación se indica el registro OPTION_REG, que puede ser leído o escrito y que contiene
varios bits de control para configurar la asignación del preescaler al TMR0 o al WDT, la
interrupción externa, el TMR0 y las resistencias de pull-up del PORTB.

R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1


#RBPU INTED T0CS T0SE PSA PS2 PS1 PS0
Bit 7 Bit 0

5
Bit 7: #RBPU: Resistencia de Pull-up en el PORTB
1=Resistencia de Pull-up desactivada
0= Resistencia de Pull-up activada

Pines RB4, ... ,RB7. Estos cuatro pines del puerto B tienen la capacidad de generar una solicitud de
interrupción a la CPU cuando están configuradas como entradas. El estado de estos pines es
comparado con el último estado que tenían durante la última lectura a PORTB, guardado en un
latch. Los bits que indican que hay una diferencia entre estos valores por cada pin están
conectados a una puerta OR cuya salida activa el bit RBIF del registro INTCON solicitando
con esto una interrupción. Esta interrupción es especialmente útil para despertar al dispositivo de
su estado de SLEEP cuando alguno de los cuatro pines es activado, por ejemplo, en respuesta a la
presión de una tecla.

Esta característica de solicitud de interrupción cuando se detecta un cambio junto con las
resistencias “pull-up” configurables para estos cuatro pines, los hacen ideales para el manejo de
teclados en dispositivos portátiles que requieren “dormirse” durante largos ratos para economizar
baterías y “despertarse” cuando una tecla es presionada.

En las siguientes figuras se muestra el alambrado interno de los pines del puerto B:

Pines RB0,...RB3 Pines RB4,...,RB7

3.2.2. Estado de salida de los pines del puerto B

1. Estado de salida de los pines RB0, RB1, RB2 y RB3

Para este grupo de pines permanece sustancialmente invariante la lógica de conmutación, estas
disponen de un circuito WEAK PULL-UP que se activa cuando el pin es programado como
entrada.
6
La entrada del evento, como se explicó anteriormente, el pin viene completamente desligado del
PIC. Un estado del pin depende entonces exclusivamente del circuito externo. Sí un circuito es del
tipo colector abierto o simplemente está constituido de un simple switche que cuando es
presionado, conecta a tierra la línea de I/O, es necesario poner una resistencia del PULL-UP desde
el positivo para tomar cuando el switche se suelte y volver al nivel a una condición lógica activa
sobre un pin de entrada. El circuito de WEAK PULL-UP evita el uso del resistor PULL-UP e
inhabilita que sea activado sobre el bit RBPU (0 habilita, 1 deshabilita) del registro OPTION.

La figura anterior representa un esquema de salida extraído del data sheet del Microchip. El pin
RB0 solo presenta una característica especial, cuando está configurado como pin de entrada, puede
generar, en correspondencia un cambio de estado lógico, una interrupción ya sea una interrupción
inmediata del programa en ejecución o una llamada a una subrutina especial denominada interrupt
handler, lo que se indicará más adelante.

2. Estado de salida de los pines RB4, RB5, RB6 Y RB7

Un circuito de conmutación de este grupo es muy similar al grupo de RB0 a RB3. Este pin dispone
también de un circuito de “weak pull-up”. También tienen con respecto a los pines anteriores la
ventaja de poder revelar variaciones de estado sobre cualquier pin y generar una interrupción, la
misma que se hablará más adelante.

La figura representa un esquema de salida extraído del data sheet del Microchip. Donde, el bit
RBIF corresponde al registro INTCON.

3.3. El puerto C (PORTC)

3.3.1. Registros asociados al puerto C

El puerto C consta de 8 pines bidireccionales. Trabaja en forma similar a los dos puertos
anteriores y tiene asociados los registros:

1. Registro PORTC (07H).- Es el registro de datos cuyos 8 bits RC7,RC6,...,RC0 reflejan


directamente el valor lógico de los pines físicos del puerto C.

2. Registro TRISC (87H).- Registro de control de dirección de los pines del puerto C.
Poniendo un 1 en un bit del registro TRISC se configura los pines correspondiente como entrada
y poniendo un 0 se configura la línea correspondiente como salida.

Los pines del puerto C se encuentran multiplexados con varios pines controlados por otros
periféricos, cuando se habilita el pin del periférico respectivo puede ser ignorada la configuración
de TRISC, de hecho, algunos periféricos configuran e l p i n como salida mientras que otros la
configuran como entrada.

Cada entrada del puerto C posee un buffer con disparador trigger. Además, cuando se selecciona la
función I2C, los pines PORTC<4,3> pueden ser configurados con niveles I2C o con niveles SMBus
mediante el bit CKE del registro SSPSTAT<6>, como se muestra en las figuras siguientes.

En las siguientes figuras se muestra el alambrado interno de los pines del puerto B.
7
Pines 7, 6, 5, 2, 1 y 0 del puerto C Pines 4 y 3 del puerto C
3.3.2. Periféricos que están multiplexados con los p i n e s del puerto C

En la siguiente tabla se resumen los p i n e s del puerto C y los de los periféricos que están
multiplexados con ellos.

Nombre Función multiplexada


RC0/T1OSO/T1CKI Salida oscilatoria del Timer1/reloj de entrada del Timer 1
Entrada oscilatoria del Timer1/entrada de captura2 o salida de
RC1/T1OSI/CCP2 comparación2 o salida PWM2
RC2/CCP1 Entrada de captura1 o salida de comparación1 o salida PWM1
RC3/SCK/SCL Reloj para los modos de comunicación serie síncrona SPI e I2C
RC4/SDI/SDA Dato de entrada (en modo SPI)/ Dato de entrada-salida (modo I2C)
RC5/SDO Dato de salida (en modo SPI)
RC6/TX/CK Pin de transmisión asíncrona de la USART/reloj síncrono
RC7/RX/DT Pin de recepción asíncrona de la USART/dato síncrono

3.4. Los Puertos D y E

Estos dos puertos no se encuentran disponibles en los circuitos PIC16F873 y PIC16F876.


El puerto D es un puerto de 8 pines configurables como entradas o salidas mediante el registro
TRISD (88H) y cuyos pines pueden ser accedidos mediante el registro PORTD (08H). Cuando se
configuran como entradas éstas poseen un disparador Schmitt trigger.
El Puerto D puede configurarse para trabajar simultáneamente con sus 8 bits como un puerto
8
esclavo (Parallel Slave Port) de comunicación paralela bidireccional con pines de protocolo
proporcionados por los tres pines del Puerto E, para ello se deberá activar el bit PSPMODE
(TRISE<4>).
El Puerto E sólo posee 3 pines configurables como entradas o salidas mediante el los 3 bits menos
significativos del registro TRISE (89H). Sus pines pueden ser accedidos mediante los 3 bits menos
significativos del registro PORTE (09H). Los pines del puerto E están compartidos con el
convertidor analógico/digital, por ello, antes de usarlas deberán ser configuradas como
entradas/salidas digitales o analógicas, según se desee en forma similar a como se hizo con el
puerto A, usando el registro de configuración ADCON1 (9FH).

3.5. Ejemplo 31

Entradas digitales. En este ejemplo se configura el pin RA5 del puerto A como salida conectado a
un LED, el cual se controla de acuerdo al estado de los pines RA0 y RA1 configurados como
entradas.

Hardware necesario. Como prácticamente en todos los programas para PIC, si no se conecta el
hardware adecuado no se puede ver ningún efecto al ejecutar el programa. En la siguiente
figura se muestra la conexión de los dos botones y el LED necesarios para probar el programa,
además claro está de la circuitería de reloj necesaria.

En este programa el rebote generado por los switches no es problema, porque funciona correctamente
haya o no haya rebote.

9
1. Diagrama de Flujo:

INICIO

NO
RA0=1

SI

RA5←0

NO
RA1=1

SI

RA5←1

2. Código en Assembler:

;*****************************************************************************
;* Ejemplo31.asm
;* Este programa Enciende un LED conectado a RA5 cuando se presiona un botón
;* conectado a RA1 y lo apaga cuando se presiona un botón conectado a RA0
;*****************************************************************************
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877A.INC"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;Reset Vector
org 0x00 ;Inicia en el vector de reset

;Inicialización del puerto A:


BCF STATUS,RP0 ;
BSF STATUS,RP0 ;Selecciona Banco 1
MOVLW 0x06 ;Configura todas los pines de A
MOVWF ADCON1 ;como digitales
MOVLW 0x1F ;configura todas los pines de A como entradas
MOVWF TRISA ;y RA5 como salida

;Una vez inicializado el puerto, se procede leer los pines RA0 y RA1
BCF STATUS,RP0 ;regresa al banco 0
chRA0 BTFSS PORTA,0 ;checa si RA0=1
10
GOTO chRA1 ;si RA=0 salta a checar RA1
BCF PORTA,5 ;si RA0=1 apaga el LED
chRA1 BTFSS PORTA,1 ;checa si RA1=1
GOTO chRA0 ;si RA1=0 salta a checar RA0
BSF PORTA,5 ;si RA1=1 enciende el LED
GOTO chRA0

END

3.6. El efecto rebote y su eliminación

En el momento de presionar un botón pulsador o cualquier conmutador electromecánico es inevitable


que se produzca un pequeño arco eléctrico durante el breve instante en que las placas del contacto
se aproximan o se alejan de sus puntos de conexión, ya que las distancias microscópicas entre los
puntos a diferente potencial son suficientes para romper por un instante la resistividad del aire.
Este fenómeno se conoce como rebote (bounce) y se ilustra en la siguiente figura:

3.6.1. Duración del rebote

La experiencia empírica indica que el periodo transitorio de un rebote depende entre otros factores,
de la calidad de los switches y de la rapidez de su accionamiento, pero a lo más puede durar unos
20 milisegundos, En la siguiente figura se muestra un rebote real en los contactos de un
relevador capturado en la pantalla de un osciloscopio digital Fluke 123. (Obsérvese que el rebote
mostrado duró solamente 3 milisegundos).

11
3.6.2. Limpiado del rebote (debouncing)

El rebote generado por un switch no siempre es un problema, porque en algunas aplicaciones


puede funcionar correctamente haya o no haya rebote (como en el ejemplo 1). Sin embargo, ¿Qué
pasaría si se desea controlar el encendido y apagado del LED con un solo botón?. En este caso
sí se debe contemplar el limpiado del rebote. (Siempre que a una sola tecla se le asigna dos o más
funciones diferentes en un programa se deben considerar el efecto del rebote).

El rebote de un switch puede durar a lo más unos 20 milisegundos, por lo que para el limpiado del
rebote se debe realizar una subrutina con una pausa de 20 mseg, la cual se desarrolla a continuación.

I. Subrutina de pausa con un ciclo

La subrutina pau20ms simplemente realiza una pausa de 20 milisegundos. Se tiene dos formas de
implementación de esta subrutina (suponiendo un reloj de 100 Khz):

a. Usando memoria de datos


1. Diagrama de Flujo:

12
pau20ms

W←N
cont←W

cont←cont-1

NO
Z=1

SI

FIN

2. Código en Assembler:
;* Subrutina de pausa que dura 20 milisegundos (usando memoria de datos)
;* (Supone un reloj de 100 Khz)
;**************************************************************************
;Define constantes
N EQU 0xA5
cont EQU 0x20
; inicia subrutina
pau20ms MOVLW N ;Carga dato que controla la duración, W=N
MOVWF cont ;inicializa contador con el dato, cont=N
rep DECFSZ cont,1 ;Decrementa contador y escapa si cero, cont=cont-1
GOTO rep ;si no es cero, repite
RETURN ;regresa de esta subrutina

b. Sin usar memoria de datos

;* Subrutina de pausa que dura 20 milisegundos (sin usar memoria de datos)


;* (Supone un reloj de 100 Khz)
;**************************************************************************
;Define dato que controla la duración
N EQU ???
;inicia subrutina
pau20ms MOVLW N ;Carga dato que controla la duración
rep ADDLW 0xFF ;Le suma –1
BTFSS STATUS,2 ;Si es cero escapa
GOTO rep ;si no es cero, repite
RETURN ;regresa de esta subrutina

Cálculo del valor de N

Para lograr que la duración de la subrutina presentada sea de 20 milisegundos es


necesario elegir adecuadamente el valor de la constante N. Para ello se ejemplifica el cálculo
13
en la primera de las dos versiones (a): A continuación se repite el código fuente de la
subrutina incluyendo entre paréntesis el número de ciclos que dura cada instrucción:

pau20ms MOVLW N ;(1)


MOVWF cont ;(1)
rep DECFSZ cont,1 ;(1 si no escapa, 2 si escapa)
GOTO rep ;(2)
RETURN ;(2)

De manera que el número de ciclos de instrucción Tsub consumidos por la subrutina,


incluyendo los 2 ciclos de la llamada (CALL) serán:

Tsub = [2+1+1+(N-1)*(1+2)+2+2] ciclos

Lo cual se puede expresar en segundos como:

Tsub = (3N+5) Tcy


Donde Tcy es la duración en segundos de un ciclo de instrucción. Despejando N tenemos:

N = (Tsub/Tcy – 5)/3

Por ejemplo, suponiendo Tsub = 20 mseg y que se está usando un reloj de 100 Khz y (fcy =
fosc/4 = (100x103 Hz)/4 =25x103 Hz entonces Tcy = 1/(25x103) = 40x10-6 seg = 40 µseg):
N = (20 mseg /40µseg – 5)/3
N = 165 = A5H

Observación. La máxima duración que se puede lograr con esta subrutina y con la frecuencia del
reloj de 100 Khz supuesta (Tcy = 40 µseg), es cuando el ciclo se ejecute 256 veces, es decir con
un valor N=256, lo cual en el programa se logra con el valor inicial N=0:

Tsub = (3N+5) Tcy


Tsub = (3 x 256 + 5) x 40 x 10-6seg
Tsub = 30.92 mseg

Así, si la frecuencia del reloj es mayor, por ejemplo, en el modo de oscilador interno se
tienen 4 Mhz, que da un Tcy=1µseg, esta subrutina no podrá proporcionar nunca los 20 mseg,
por lo que habrá que realizar una pausa más larga anidando dos ciclos.

II. Subrutina de pausa con dos ciclos anidados

La pausa pau20ms es demasiado corta, de manera que para frecuencias altas de reloj no puede
alcanzar ni siquiera los 20 mseg. A continuación se muestra una pausa que puede dar tiempos
mucho más largos usando ciclos anidados. A continuación se va realizar una subrutina d20ms de
20 mseg con un reloj de 4 Mhz:

1. Diagrama de Flujo:

14
d20ms

W←N
cont1←W

W←M
cont2←W

cont2←cont2-1

NO
Z=1

SI

cont1←cont1-1

NO
Z=1

SI

FIN

2. Código en Assembler:
;* Subrutina de pausa para frecuencias medias
;* (Adecuada para un reloj de 4 Mhz)
;**************************************************************************
;Define constantes para 20 milisegundos
N EQU 0x1A
M EQU 0x00
cont1 EQU 0x20
cont2 EQU 0x21
;inicia subrutina
d20ms MOVLW N ;(1) Carga dato N
MOVWF cont1 ;(1) inicializa contador1 ciclo externo
rep1 MOVLW M ;(1) Carga dato M
MOVWF cont2 ;(1) inicializa contador2 ciclo interno
rep2 DECFSZ cont2,1 ;(1,2)Decrementa contador2 y escapa si cero
GOTO rep2 ;(2) si no es cero, repite ciclo interno
DECFSZ cont1,1 ;(1,2)Decrementa contador1 y escapa si cero
GOTO rep1 ;(2) si no es cero repite ciclo externo
RETURN ;(2) regresa de esta subrutina

15
La duración de esta subrutina en ciclos de reloj será

Tsub = 2+1+1+N*[1+1+(M-1)*(1+2)+2+1+2]+1-2+2+2 ciclos

Lo cual se puede simplificar como sigue:

Tsub = [N*(3M+4)+7] Tcy


La máxima duración de esta subrutina se obtiene para N=M=256, lo cual en el programa se logra
con los valores iniciales de N=M=0; entonces Tsub máximo = 197639 Tcy. Para una
frecuencia de 4 Mhz (Tcy = 1 µseg), entonces Tsub máximo =0,197639 seg.

Si deseamos Tsub = 20 mseg, haciendo M=256, despejando N,

N = (Tsub/Tcy –
7)/(3M+4)

Sustituyendo valores obtenemos N= 25.89 ~ 1Ah. (debido a la aproximación la subrutina


realmente dura 20.08 mseg.

Observación. Hasta aquí se están realizando pausas de duración controlada por software, sin
embargo, lo más adecuado para controlar tiempo de manera más exacta es usar los timers
que se verán más adelante.

III. Subrutina de pausa con ciclos anidados para tiempos largos

Para realizar la pausa de un segundo, d1seg, se puede hacer uso de la subrutina d20ms con su
máxima duración Tsub = 0,197639 seg, teniendo los valores N=M=256 (con los valores
iniciales de N=M=0 en el programa). Para ello se implementa un tercer ciclo externo que repita
esta pausa p veces.

1. Diagrama de Flujo:

16
d1seg

W←p
cont3←W

d20ms

cont3←cont3-1

NO
Z=1

SI

FIN

2. Código en Assembler:

;* Subrutina de pausa de 1 segundo


;**********************************************************************
p EQU 0x05
cont3 EQU 0x22
d1seg MOVLW p ;(1)carga duración del ciclo
MOVWF cont3 ;(1)inicializa contador3
ciclo CALL d20ms ;(197639)pausa de 0.197639 seg con Fos=4Mh y M=N=256
DECFSZ cont3,1 ;(1,2)Decrementa y escapa si cero
GOTO ciclo ;(2)si no es cero repite
RETURN ;(2)si es cero retorna

La duración de esta subrutina incluyendo la llamada será

Tsub = [2+1+1+(p)*(197639+1+2)+1-2+2] ciclos


Es decir,
Tsub = [197642*p+5] Tcy

La máxima duración de esta subrutina (para p =256), suponiendo un reloj de 4 Mhz (Tcy = 1
µseg), se tiene un Tsubmáximo=50,596357 seg. Sin embargo, si se desea un Tsub=1seg. Se
obtiene:
p = (Tsub/Tcy – 5)/197642 = 5,059628 ~ 5

17
3.7. Ejemplo 2

En este ejemplo se usa sólo el botón conectado al pin RA0 para controlar el encendido y
apagado del LED en el pin RA5, cuando se presiona el botón conectado a RA0 se enciende el LED
conectado en el pin RA5 y lo apaga cuando se presiona nuevamente el mismo botón y así
sucesivamente. Se incluye el limpiado del rebote con un reloj de 4Mhz.

Hardware necesario. En la siguiente figura se muestra la conexión del botón y el LED necesarios
para probar el programa.

1. Diagrama de Flujo:

Diagrama de flujo del Programa Principal:

INICIO

N←0x1A
M←0x00

NO
RA0=1

SI

d20ms

NO
RA0=1

SI

W←0x20
RA5←W XOR RA5

18
El diagrama de flujo de la Subrutina d20ms se realiza en el subtema “Limpiado del rebote”.

2. Código en Assembler:

;**************************************************************************
;* Ejemplo32.asm
;* Este programa Enciende un LED conectado a RA5 cuando se presiona un botón
;* conectado a RA0 y lo apaga cuando se presiona nuevamente el mismo botón y
;* así sucesivamente.
;**************************************************************************
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877A.INC"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;Define constantes
N EQU 0x1A
M EQU 0x0

;Define variables
CBLOCK 0x20
cont1
cont2
ENDC

;Reset Vector
org 0x00 ;Inicia en el vector de reset

;Inicialización del puerto A:


CLRF STATUS ;Selecciona Banco 0
BSF STATUS,RP0 ;Selecciona Banco 1
MOVLW 0x06 ;Configura todas las patitas de A
MOVWF ADCON1 ;como digitales
MOVLW 0x1F ;configura todas las patitas de A como entradas
MOVWF TRISA ;y RA5 como salida
BCF STATUS,RP0 ;regresa al banco 0

checa BTFSS PORTA,0 ;checa si RA0=1


GOTO checa ;si RA=0 checa de nuevo
CALL d20ms ;si RA0=1 espera 20 miliseg. A que pase el rebote
chec1 BTFSS PORTA,0 ;checa nuevamente si RA0=1
GOTO chec1 ;si RA1=0 falsa alarma, vuelve a checar
MOVLW 0x20 ;si RA0=1 señal válida; carga máscara en W
XORWF PORTA,1 ;conmuta estado del LED
GOTO checa ;repite

;* Subrutina de pausa para frecuencias medias


;* (Adecuada para un reloj de 4 Mhz)
;**************************************************************************
19
;Define constantes para 20 milisegundos
;inicia subrutina
d20ms MOVLW N ;(1) Carga dato N
MOVWF cont1 ;(1) inicializa contador1 ciclo externo
rep1 MOVLW M ;(1) Carga dato M
MOVWF cont2 ;(1) inicializa contador2 ciclo interno
rep2 DECFSZ cont2,1 ;(1,2)Decrementa contador2 y escapa si cero
GOTO rep2 ;(2) si no es cero, repite ciclo interno
DECFSZ cont1,1 ;(1,2)Decrementa contador1 y escapa si cero
GOTO rep1 ;(2) si no es cero repite ciclo externo
RETURN ;(2) regresa de esta subrutina

END

3.8. Ejemplo 3

Luces secuenciales. En este ejemplo se realiza el encendido secuencial de 8 LEDs conectados a


RB0,...,RB7, es decir, se enciende RB0 durante un segundo, luego RB1 y así sucesivamente hasta
llegar a RB7 para recomenzar después con RB0. (en un segundo dado sólo un LED está prendido).

Hardware necesario. Sólo se requieren los ocho LEDs conectados a los pines RBP0,...,RBP7,
como se muestra en la siguiente figura.

1. Diagrama de Flujo:

Diagrama de flujo del Programa Principal:

20
INICIO

N←0x00
M←0x00
p←0x05
C←0
PORTB←0
RB0←1

d1seg

PORTB←rota izq PORTB

SI
C=0

NO

RB0←1

C←0

Los diagramas de flujo de la Subrutinas d1seg y d20ms se realiza en el subtema “Limpiado del
rebote”.

2. Código en Assembler:

;**********************************************************************
;* Ejemplo33.asm
;* Este programa Enciende en secuencia (uno a la vez) 8 LEDs conectados
;* a los pines del puerto B
;**********************************************************************
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877A.INC"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;Define constantes Para 200 milisegundos


N EQU 0x0
M EQU 0x0

21
p EQU 0x05

;Define variables
CBLOCK 0x20
;Define variables para pausa de 200 mseg
cont1
cont2
;Define variable para pausa de 1 segundo
cont3
ENDC

;Reset Vector
org 0x00 ;Inicia en el vector de reset

;Inicialización del puerto B:


CLRF STATUS ;Selecciona Banco 0
BSF STATUS,RP0 ;Selecciona Banco 1
CLRF TRISB ;Configura todas los pines de B como salidas

;Una vez inicializado el puerto, se procede a controlar los LEDs


BSF STATUS,RP0 ;Regresa al banco 0
BCF STATUS,C ;Limpia acarreo
CLRF PORTB ;Apaga todos los LEDs
BSF PORTB,0 ;enciende el LED RB0
ciclo CALL d1seg ;pausa de un segundo
RLF PORTB,1 ;Recorre posición encendida a la izquierda
BTFSC STATUS,C
BSF PORTB,0
BCF STATUS,C
GOTO ciclo ;repite

;* Subrutina de pausa para tiempos largos


;* (Adecuada para un reloj de 4 Mhz)
;**************************************************************************
;inicia subrutina
d20ms MOVLW N ;(1) Carga dato N
MOVWF cont1 ;(1) inicializa contador1 ciclo externo
rep1 MOVLW M ;(1) Carga dato M
MOVWF cont2 ;(1) inicializa contador2 ciclo interno
rep2 DECFSZ cont2,1 ;(1,2)Decrementa contador2 y escapa si cero
GOTO rep2 ;(2) si no es cero, repite ciclo interno
DECFSZ cont1,1 ;(1,2)Decrementa contador1 y escapa si cero
GOTO rep1 ;(2) si no es cero repite ciclo externo
RETURN ;(2) regresa de esta subrutina

;* Subrutina de pausa de 1 segundo


;**********************************************************************
;inicia rutina
d1seg MOVLW p ;(1)carga duración del ciclo
MOVWF cont3 ;(1)inicializa contador3
ciclo1 CALL d20ms ;(196868)pausa de 0.197639 seg
DECFSZ cont3,1 ;(1,2)Decrementa y escapa si cero
GOTO ciclo1 ;(2)si no es cero repite
RETURN ;(2)si es cero retorna

END

22
3.9. Ejemplo 4

Conexión de un teclado matricial

El presente ejemplo realiza el ingreso de datos numéricos hexadecimales mediante un teclado de tipo
telefónico, controlando las 3 columnas con las salidas RB1, RB2 y RB3 y las 4 filas con las entradas
RB4, RB5, RB6 y RB7 con resistencias pull-up internas para evitar resistencias externas. Luego
mostrar el dato ingresado en el puerto C.

Una de las funciones más comunes de un microcontrolador es la aceptación de datos numéricos o


alfanuméricos suministrados por un operador mediante un teclado.

La mayoría de los teclados están organizados en forma matricial como un conjunto de switches en
las intersecciones de varios renglones y columnas conductoras como se muestra en la siguiente
figura que ejemplifica un teclado de tipo telefónico.

En la figura también se ilustra una conexión típica con tres pines de entrada del puerto B que
controlan las columnas y cuatro pines de salida del mismo puerto que recogen la información de los
renglones.

Observación. Es recomendable colocar un diodo de protección en cada pin de salida del


puerto para evitar que al activar más de una columna a la vez se produzca un corto circuito.

Procedimiento de detección de teclas y codificación. Para la detección y asignación de


código según la tecla presionada se procede como sigue:

1. Detección. Se ponen en bajo todos las filas (cuidando que haya diodos de protección) y se
leen las columnas.

2. Si hay alguna columna activa se limpia el rebote, si es tecla válida se pasa al paso 3, si
no es tecla válida se asigna un código de “ninguna tecla presionada”.

3. Codificación. El puerto activa una fila a la vez colocando un cero lógico en el pin
correspondiente a la fila a activar.
23
4. Por cada fila activa se lee la información de columnas y dependiendo de la fila y la
columna activada (en bajo) se asigna el código a la tecla de la intersección.
1. Diagrama de Flujo:

Diagrama de flujo del Programa Principal:

INICIO

N←0x1A
M←0x00

initB

W←0xFF
masq←W

detect

masq←W-masq

NO
Z=1

SI

codif

PORTC←W

Diagrama de flujo de la Subrutina initB:

24
initB

STATUS←0
PORTB←0

TRISB←0xF0
RBPU←0

FIN

Diagrama de flujo de la Subrutina detec:

INICIO

PORTB←0 A

NO NO
RB7=1 RB7=1

SI SI

NO NO
RB6=1 RB6=1

SI SI

NO NO
RB5=1 RB5=1

SI SI

NO NO
RB4=1 RB4=1

SI SI

W←0x00 d20ms W←0x00 W←0xFF

FIN A FIN FIN

Diagrama de flujo de la Subrutina codif:


25
INICIO A B

W←0xF7 W←0xFB W←0xFD


PORTB←W PORTB←W PORTB←W

NO NO NO
RB7=1 RB7=1 RB7=1

SI W←’1’ SI W←’2’ SI W←’3’

NO NO NO
RB6=1 RB6=1 RB6=1

SI SI SI
W←’4’ W←’5’ W←’6’

NO NO NO
RB5=1 RB5=1 RB5=1

SI W←’7’ SI W←’8’ SI W←’9’

NO NO NO
RB4=1 RB4=1 RB4=1

SI W←’*’ SI W←’0’ SI W←’#’

A B W←0x00

FIN FIN FIN

El diagrama de flujo de la Subrutina d20ms se realiza en el subtema “Limpiado del rebote”.

2. Código en Assembler:
;*********************************************************************
;* Ejemplo34.asm
;* Ingreso de datos numéricos hexadecimales mediante un teclado de
;* tipo telefónico, controlando las 3 columnas con las salidas RB1,RB2 y RB3
;* y las 4 filas con las entradas RB4,RB5,RB6 y RB7 con resistencias pull-up
;* internas para evitar resistencias externas.
;* Luego mostrar el dato ingresado en el puerto C.
;*********************************************************************
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877A.INC"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;Define constantes para 20 milisegundos


N EQU 0x1A
M EQU 0x0

26
;Define variables
CBLOCK 0x20
;Define variables para pausa de 20 mseg
cont1
cont2
;Define variable para mascara 0xFF (tecla pulsada)
masq
ENDC

;Reset Vector
org 0x00 ;Inicia en el vector de reset

;Configura el puerto C como salida


CLRF STATUS ;Selecciona Banco 0
BSF STATUS,RP0 ;Selecciona Banco 1
MOVLW 0x00 ;configura puerto C como salidas
MOVWF TRISC ;
BCF STATUS,RP0 ;regresa al Banco 0

;Programa principal
CALL initB ;Llama a la subtutina initB
repite MOVLW 0xFF ;Almacena en W la mascara 0xFF
MOVWF masq ;Almacena en masq la mascara 0xFF
CALL detec ;Llama a la subtutina detect
SUBWF masq,1 ;
BTFSS STATUS,Z ;Resultado en W=0
GOTO repite ;Repite el proceso
CALL codif ;Llama a la subtutina codif
MOVWF PORTC ;Mueve la tecla presionada al puerto C
GOTO repite ;Repite el proceso

;Esta subrutina realiza la Inicialización del puerto B:


;*********************************************************************

initB CLRF STATUS ;Selecciona Banco 0


CLRF PORTB ;Inicializa latches de datos de PORTB
BSF STATUS,RP0 ;Selecciona Banco 1
MOVLW 0xF0 ;configura RB7,...,RB4 como entradas
MOVWF TRISB ;y RB3,RB2,RB1 como salidas
BCF OPTION_REG,7 ;Conecta todas las resistencias Pull-Up
BCF STATUS,RP0 ;regresa al Banco 0
RETURN

;Esta subrutina realiza la Detección de tecla presionada: regresa W=0 si no hay


tecla
;presionada y W=0xFF si hay alguna tecla presionada
;*********************************************************************

detec CLRF PORTB ;activa las cuatro filas


BTFSS PORTB,7 ;lee renglón 1,2,3
GOTO rebo ;si tecla presionada limpia rebote
BTFSS PORTB,6 ;lee renglón 4,5,6
GOTO rebo ;si tecla presionada limpia rebote
BTFSS PORTB,5 ;lee renglón 7,8,9
GOTO rebo ;si tecla presionada limpia rebote
BTFSS PORTB,4 ;lee renglón *,0,#
GOTO rebo ;si tecla presionada limpia rebote
RETLW 0x0 ;no hubo tecla presionada retorna con w=0

rebo CALL d20ms ;pausa de 20 milisegundos

27
BTFSS PORTB,7 ;lee renglón 1,2,3
RETLW 0xFF ;tecla presionada, retorna con w=0xFF
BTFSS PORTB,6 ;lee renglón 4,5,6
RETLW 0xFF ;tecla presionada, retorna con w=0xFF
BTFSS PORTB,5 ;lee renglón 7,8,9
RETLW 0xFF ;tecla presionada, retorna con w=0xFF
BTFSS PORTB,4 ;lee renglón *,0,#
RETLW 0xFF ;tecla presionada, retorna con w=0xFF
RETLW 0x0 ;falsa alarma retorna con w=0

;* Esta subrutina realiza la codificación del teclado tipo telefónico


;* retornando en W el código ASCII de la tecla presionada,
;* regresa W=0 si no hubo tecla presionada
;*********************************************************************

; Se hace un barrido por columnas


codif ; Primera columna
MOVLW 0xF7 ;Activa la primera columna
MOVWF PORTB ;y activa la columna 1,4,7,*
BTFSS PORTB,7 ;Es la tecla 1?
RETLW '1' ;retorna código del ?1?
BTFSS PORTB,6 ;Es la tecla 4?
RETLW '4' ;retorna código del ?4?
BTFSS PORTB,5 ;Es la tecla 7?
RETLW '7' ;retorna código del ?7?
BTFSS PORTB,4 ;Es la tecla *?
RETLW '*' ;Retorna código del ?*?

; Segunda columna
MOVLW 0xFB ;Activa la segunda columna
MOVWF PORTB ;y activa la columna 2,5,8,0
BTFSS PORTB,7 ;Es la tecla 2?
RETLW '2' ;retorna código del ?2?
BTFSS PORTB,6 ;Es la tecla 5?
RETLW '5' ;retorna código del ?5?
BTFSS PORTB,5 ;Es la tecla 8?
RETLW '8' ;retorna código del ?8?
BTFSS PORTB,4 ;Es la tecla 0?
RETLW '0' ;Retorna código del ?0?

; Tercera columna
MOVLW 0xFD ;Activa la tercera columna
MOVWF PORTB ;y activa la columna 3,6,9,#
BTFSS PORTB,7 ;Es la tecla 3?
RETLW '3' ;retorna código del ?3?
BTFSS PORTB,6 ;Es la tecla 6?
RETLW '6' ;retorna código del ?6?
BTFSS PORTB,5 ;Es la tecla 9?
RETLW '9' ;retorna código del ?9?
BTFSS PORTB,4 ;Es la tecla #?
RETLW '#' ;Retorna código del ?#?
RETLW 0x00 ;falsa alarma, no hay tecla presionada

;* Subrutina de pausa para frecuencias medias


;* (Adecuada para un reloj de 4 Mhz)
;**************************************************************************
;inicia subrutina
d20ms MOVLW N ;(1) Carga dato N
MOVWF cont1 ;(1) inicializa contador1 ciclo externo
rep1 MOVLW M ;(1) Carga dato M

28
MOVWF cont2 ;(1) inicializa contador2 ciclo interno
rep2 DECFSZ cont2,1 ;(1,2)Decrementa contador2 y escapa si cero
GOTO rep2 ;(2) si no es cero, repite ciclo interno
DECFSZ cont1,1 ;(1,2)Decrementa contador1 y escapa si cero
GOTO rep1 ;(2) si no es cero repite ciclo externo
RETURN ;(2) regresa de esta subrutina

END

Observación: La repetición de código se puede evitar utilizando direccionamiento indirecto dentro


de un ciclo, sin embargo, esto sólo representa un ahorro de código y de memoria para teclados de
mayor complejidad.

3.10. Ejemplo 5

Manejo de un Display de 7 segmentos

Un display de siete segmentos es un arreglo de 7 LEDs conectados como se muestra en la siguiente


figura:

Para este ejemplo se supone un display de ánodo común conectado al puerto C como se muestra a
continuación:

El programa visualiza dígitos numéricos hexadecimales en el display, de acuerdo a la codificación


mostrada en la siguiente tabla.

Dato a b c d e f g Código
29
0 0 0 0 0 0 0 1 01
1 1 0 0 1 1 1 1 4F
2 0 0 1 0 0 1 0 12
3 0 0 0 0 1 1 0 06
4 1 0 0 1 1 0 0 4C
5 0 1 0 0 1 0 0 24
6 0 1 0 0 0 0 0 20
7 0 0 0 1 1 1 1 0F
8 0 0 0 0 0 0 0 00
9 0 0 0 0 1 0 0 04
A 0 0 0 1 0 0 0 08
b 1 1 0 0 0 0 0 60
C 0 1 1 0 0 0 1 31
d 1 0 0 0 0 1 0 42
E 0 1 1 0 0 0 0 30
F 0 1 1 1 0 0 0 38

1. Diagrama de Flujo:

Diagrama de flujo del Programa Principal:

30
INICIO

N←0x1A
M←0x00
PORTB←0x01
cont←0

NO
RC7=1

SI

d20ms

NO
RC7=1

SI

cont←cont+1

codigo

POTC←W

NO
RC7=0

SI

El diagrama de flujo de la Subrutina d20ms se presenta en el subtema “Limpiado del rebote”

Diagrama de flujo de la Subrutina código:

31
codigo

W←0x0F
W←W AND cont
PCL←W + PCL

Selecciona un
valor de acuerdo
a cont:
0: W←0x01
1: W←0x4F
2: W←0x12
3: W←0x06
4: W←0x4C
5: W←0x24
6: W←0x20
7: W←0x0F
8: W←0x00
9: W←0x04
10: W←0x08
11: W←0x60
12: W←0x31
13: W←0x42
14: W←0x30
15: W←0x38

FIN

2. Código en Assembler:

;************************************************************************
;* Ejemplo35.asm
;* Este programa despliega un dígito hexadecimal en un display de ánodo
;* común conectado al puerto C. El dígito se incrementa en 1 cada vez que
;* se presiona un botón conectado al MSB del mismo puerto C
;************************************************************************
PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877A.INC"

;Setup of PIC configuration flags


__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;Define constantes para 20 milisegundos


N EQU 0x1A
M EQU 0x0

;Define variables
CBLOCK 0x20
;Define variables contador
cont

32
;Define variables para pausa de 20 mseg
cont1
cont2
;Define variable para pausa de 1 segundo
cont3
ENDC

;Reset Vector
org 0x00 ;Inicia en el vector de reset

inic BSF STATUS,RP0 ;banco 1


MOVLW 0x80 ;Todos los bits del puerto C como salidas
MOVWF TRISC ;y el MSB como entrada
BCF STATUS,RP0 ;regresa al Banco 0

MOVLW 0x01 ;
MOVWF PORTC ;Despliega un cero. Codificado
CLRF cont ;Inicializa contador en cero
tecla BTFSS PORTC,7 ;checa botón si se ha presionado
GOTO tecla ;Si no se ha presionado espera
CALL d20ms ;pausa de 20 milisegundos
BTFSS PORTC,7 ;checa nuevamente el botón si esta presionado
GOTO tecla ;tecla falsa, espera de nuevo
INCF cont,1 ;tecla válida, incrementa contador
CALL codigo ;obtiene código para desplegar el contador
MOVWF PORTC ;despliega contador
suelta BTFSC PORTC,7 ;checa de nuevo el botón si se ha soltado
GOTO suelta ;si sigue presionado espera
GOTO tecla ;si ya se soltó espera nueva presión.

;* Subrutina para determinar el codigo hexadecimal


;*
;**************************************************************************

codigo
MOVLW 0x0F ;carga máscara
ANDWF cont,0 ;enmascara el contador y lo deja en W
ADDWF PCL,1 ;Salta W instrucciones adelante
RETLW 0x01 ;código del 0
RETLW 0x4F ;código del 1
RETLW 0x12 ;código del 2
RETLW 0x06 ;código del 3
RETLW 0x4C ;código del 4
RETLW 0x24 ;código del 5
RETLW 0x20 ;código del 6
RETLW 0x0F ;código del 7
RETLW 0x00 ;código del 8
RETLW 0x04 ;código del 9

33
RETLW 0x08 ;código de la A
RETLW 0x60 ;código de la b
RETLW 0x31 ;código de la C
RETLW 0x42 ;código de la d
RETLW 0x30 ;código de la E
RETLW 0x38 ;código de la F

;* Subrutina de pausa para tiempos largos


;* (Adecuada para un reloj de 4 Mhz)
;**************************************************************************

;inicia subrutina
d20ms MOVLW N ;(1) Carga dato N
MOVWF cont1 ;(1) inicializa contador1 ciclo externo
rep1 MOVLW M ;(1) Carga dato M
MOVWF cont2 ;(1) inicializa contador2 ciclo interno
rep2 DECFSZ cont2,1 ;(1,2)Decrementa contador2 y escapa si cero
GOTO rep2 ;(2) si no es cero, repite ciclo interno
DECFSZ cont1,1 ;(1,2)Decrementa contador1 y escapa si cero
GOTO rep1 ;(2) si no es cero repite ciclo externo
RETURN ;(2) regresa de esta subrutina

END
Observación: Una manera más cómoda de escribir la lista de instrucciones RETLW al final del
programa anterior puede lograrse usando la directiva DT (Define Table) del ensamblador, la cual
nos permite definir una tabla de datos que será sustituida por una lista de instrucciones RETLW; así,
la lista anterior puede quedar como sigue:

DT 0x01,0x4F,0x12,0x06,0x4C,0x24,0x3F,0x0F
DT 0x00,0x04,0x08,0x60,0x31,0x42,0x30,0x38

3.11. Pantalla LCD 16×2 con PIC (HD44780)

http://www.geekfactory.mx/tutoriales/tutoriales-pic/pantalla-lcd-16x2-con-pic-libreria/

por Jesus Ruben Santa Anna Zamudio | Ago 2, 2014 | Tutoriales PIC | 3 Comments

11.1. Descripción:

Cuando los simples indicadores luminosos con led ya no son suficientes, habitualmente el siguiente
paso para todo programador es ir por una pantalla lcd 16×2. Se encuentra que existen módulos LCD
que facilitan la interfaz con este periférico. El estándar en la industria para estos módulos con
“controlador a bordo” es el chipset HD44780 (y otros más compatibles desarrollados a partir de este),
para los cuales se encuentra soporte en prácticamente cualquier plataforma: Arduino, PIC, AVR,
MSP430, etc.

La pantalla de cristal líquido o LCD (Liquid Crystal Display) es un dispositivo microControlado de


visualización gráfico para la presentación de caracteres, símbolos o incluso dibujos (en algunos
modelos), es este caso dispone de 2 filas y de 16 caracteres cada una y cada carácter dispone de una
34
matriz de 5x7 puntos (pixels), aunque los hay de otro número de filas y caracteres. Este dispositivo está
gobernado internamente por un microcontrolador y regula todos los parámetros de presentación, este
modelo es el más comúnmente usado y esta información se basará en el manejo de este u otro LCD
compatible.

En la Figura 1 se muestra un LCD 2x16 que está compuesto por 2 líneas de 16 caracteres, con
iluminación azul y letras color blanco.

Figura 1. LCD 2x16: está compuesto por 2 líneas de 16 caracteres

11.2. Gestión de un LCD

Se realizará la interface del PIC16F877 con un LCD (Liquid Crystal Display) compuesto por dos líneas
de 16 caracteres cada una. Los LCD más comunes, disponen de una interface ideada por Hitachi y
adoptada como estándar por otros fabricantes. Esta interface hace que el LCD pueda ser conectado al
micro a través de un bus de 4 u 8 líneas más tres líneas de control y las de alimentación.
En la Tabla 1 están descriptas las funciones de cada línea disponible del LCD. Las descripciones en
negrita, indican las líneas efectivamente utilizadas en la aplicación de 4 bits de datos.

Tabla 1. Funciones de cada línea disponible del LCD


Pin Nombre Función
1 GND Ground. GND (Tierra 0V)
2 VDD Power Supply. Alimentación (+5 v)
3 LCD Liquid Crystal Driving Voltage. Ajuste del contraste (Se aplicada una tensión variable entre 0 y 5V)
4 RS# Register Select. Selección DATO/COMANDO (Es una línea de control que se le indica al display si se está
enviando en el bus de datos un comando (RS=0) o un dato (RS=1))
5 R/W# Read, Write. Lectura o escritura en LCD (Es otra línea de control que LE se indica al display si se está enviando
un dato (R/W=0) o leyendo un dato del display (R/W=1))
6 E Enable. Habilitación (Esta línea de control sirve para habilitar el display para que reciba un dato o
instrucciones a través del bus (E=1))
7 DB0 Data Bus Line 0 - Bit menos significativo. Sobre estas líneas viajan los datos entre el micro y el display
8 DB1 Data Bus Line 1
9 DB2 Data Bus Line 2
10 DB3 Data Bus Line 3
11 DB4 Data Bus Line 4
12 DB5 Data Bus Line 5
13 DB6 Data Bus Line 6
14 DB7 Data Bus Line 7. Bit más significativo
15 LED+ Ánodo de LED backlight
16 LED- Cátodo de LED backlight
Nota: # significa negado
35
Según la operación que se desee realizar en el módulo LCD, los pines de control E, RS#, R/W# deben
tener un estado determinado. Además, debe tener en el bus de datos un código que indique un caracter
para mostrar en la pantalla o una instrucción de control para el display.

Para reducir al máximo las conexiones entre el micro y el LCD, se usará la modalidad de interconexión
de datos a 4 bit, usando sólo las líneas DB4, DB5, DB6, DB7. Las líneas DB0, DB1, DB2 y DB3 no
serán usadas y serán conectadas a tierra.

Tampoco la línea R/W# será utilizada por lo que será puesta a tierra. De este modo, quedará
seleccionado el modo escritura. En la práctica, sólo se podrá enviar datos al LCD pero no recibirlos.

a) La línea Register Select (RS) y Enable (E) del LCD

Para poder visualizar una frase en el LCD, el PIC debe enviar una serie de comandos a través del
bus de datos (líneas DB4 a DB7). Para hacer esto, son utilizadas dos líneas de control con las que
se comunica al LCD la operación de transferencia que realizará el bus.

Las dos líneas de control son Register Select (pin 4) y Enable (pin 6) del LCD, las cuales se
describen a continuación:

 Con la línea Register Select, el PIC indica al display que el dato presente en el bus es un
comando ( RS=0 ) o un dato a ser visualizado ( RS=1 ). A través de los comandos, el PIC puede
indicar al LCD el tipo de operación a realizar, como por ejemplo "limpiar pantalla". Con los
datos, el PIC puede enviar directamente los caracteres ASCII a ser visualizados.

 La línea Enable habilita al LCD para leer el comando o el dato enviado en el bus por el PIC. El
PIC debe ocuparse de haber enviado en el bus de datos el comando o el dato correcto antes de
poner a 1 la señal Enable.

b) Mutiplexado sobre el bus de datos

Puesto que los comandos son datos de 8 bits ¿Cómo es posible enviarlos al LCD si el bus de datos
dispone sólo de 4 líneas?

En la práctica se usa la operación de multiplexado, es decir, cada bit es descompuesto en dos


grupos de 4 bit y transmitidos por el bus de datos en secuencia. Son enviados primero los 4 bit
menos significativos seguidos de los 4 bit más significativos.

En el ejemplo a desarrollar, todas las operaciones de transmisión de datos y comandos hacia el


LCD son seguidas de una serie de subrutinas, simplificando de esa manera la complejidad del
programa.

Los módulos LCD responden a un conjunto especial de instrucciones, estas deben ser enviadas por el
microcontrolador o sistema de control al display, según la operación que se requiera. Se muestran en la
Tabla 2 el conjunto de instrucciones del módulo LCD.

36
Tabla 2. Conjunto de instrucciones del módulo LCD
Código de Operación Tiempo de
Instrucción Descripción
RS R/W B7 B6 B5 B4 B3 B2 B1 B0 Ejecución
Borra el contenido de la pantalla y
Clear display 0 0 0 0 0 0 0 0 0 1 retorna el cursor a la posición “home” 1.52 ms
(dirección 0).
Retorna el cursor a la posición
“Home”. Retorna también el área de
Cursor home 0 0 0 0 0 0 0 0 1 * visión a la posición inicial. El 1.52 ms
contenido de la DDRAM permanece
intacto.
Incrementar/Decrementar dirección
(I/D); Habilitar corrimiento de
Entry mode set 0 0 0 0 0 0 0 1 I/D S 37 μs
pantalla (S). Estas operaciones se
realizan al leer o escribir datos
Enciende o apaga el display (D),
Display on/off
0 0 0 0 0 0 1 D C B Cursor encendido / apagado (C), 37 μs
control
destello del cursor (blink) (B).
Selecciona entre desplazamiento de
pantalla o de cursor (S/C), selecciona
Cursor/display
0 0 0 0 0 1 S/C R/L * * la dirección de desplazamiento (R/L). 37 μs
shift
El contenido de la DDRAM
permanece intacto.
Configurar en ancho de bus (4 u 8
Function set 0 0 0 0 1 DL N F * * bits) (DL), Número de líneas de 37 μs
display (N), y tipo de fuente (F).
Escribe la dirección de CGRAM. Los
Set CGRAM Dirección generador de datos a almacenar en CGRAM pueden
0 0 0 1 37 μs
address RAM ser enviados después de esta
instrucción
Escribe la dirección de DDRAM. Los
Set DDRAM datos a almacenar en DDRAM pueden
0 0 1 Dirección de datos RAM 37 μs
address ser enviados después de esta
instrucción
Leer bandera “Ocupado” (Bussy Flag)
indicando si el controlador está
Read busy flag realizando alguna operación interna o
&address 0 1 BF CGRAM/DDRAM address está listo para aceptar datos/comandos. 0 μs
counter Lee la dirección CGRAM o DDRAM
(dependiendo de la instrucción
previa).
Write CGRAM Escribe datos a las memorias CGRAM
1 0 Escritura de Dato 37 μs
orDDRAM o DDRAM.
Read from Lee datos desde las memorias
1 1 Lectura de Dato 37 μs
CG/DDRAM CGRAM o DDRAM.

NOTA: nótese que el pin RS# debe tomar el valor 0 (cero) cuando lo que se va a enviar es una
instrucción de control y debe tomar el valor 1 (uno) cuando lo que se va a enviar es un dato.

En la Tabla 3 se muestra los nombres y significado de bits en instrucciones.

37
Tabla 3. Significado de las abreviaturas
SIGNIFICADO DE LAS ABREVIATURAS
(Nombres y significado de bits en instrucciones)
I/D – 0 = disminuir el contador de dirección, 1 = incrementar el contador de dirección;

S – 0 = Sin corrimiento de display, 1 = Con corrimiento de display;

D – 0 = Display Apagado, 1 = Display Encendido;

C – 0 = Cursor Apagado, 1 = Cursor Encendido;

B – 0 = Parpadeo cursor Apagado, 1 = Parpadeo Cursor Encendido;

S/C – 0 = Mover Cursor, 1 = Corrimiento de pantalla;

R/L – 0 = Mover/Correr a la izquierda, 1 = Mover/Correr a la derecha;

DL – 0 = Interfaz de 4 bits, 1 = Interfaz de 8 bits;

N – 0 = 1 línea, 1 = 2 líneas;

F – 0 = 5×8 puntos, 1 = 5×10 puntos;

BF – 0 = puede aceptar instrucción, 1 = operación interna en progreso.

Nota: Cuando se va a cargar la dirección de la DDRAM donde se va a escribir el próximo caracter,


nótese que el D7 siempre es 1. Por lo tanto cuando se apunta a una dirección de memoria en el display
hay que considerar este 1 adicional. Se verá con más detalle al describir el mapa de memoria del
módulo LCD.

La interface entre el microcontrolador y el LCD se puede hacer con el bus de datos del PIC trabajando
a 4 u 8 bits. Las señales de control trabajan de la misma forma en cualquiera de los dos casos, la
diferencia se establece en el momento de iniciar el display, ya que existe una instrucción que permite
establecer dicha configuración. O sea se tiene que avisarle al LCD que se va a operar en 8 o a 4 bits.

Los caracteres que se envían al display se almacenan en la memoria RAM del módulo. Existen
posiciones de memoria RAM, cuyos datos son visibles en la pantalla y otras que no son visibles, estas
últimas se pueden utilizar para guardar caracteres que luego se desplazan a la zona visible.

Es importante anotar que solo se pueden mostrar caracteres ASCII de 7bits, por lo tanto algunos
caracteres especiales no se pueden ver (es aconsejable tener a mano una tabla de caracteres ASCII para
conocer los datos que son prohibidos). También se tiene la opción de mostrar caracteres especiales
creados por el programador y almacenarlos en la memoria RAM que posee el módulo.

11.3. Interfaz con Microcontrolador a 8 Bits

http://www.bolanosdj.com.ar/SOBRELCD/TEORIALCDV1.pdf

El HD44780 permite conectarse a través de un bus de 4 u 8 bits. El modo de 4 bits permite usar solo 4
pines para el bus de datos, dándonos un total de 6 o 7 pines requeridos para la interfaz con el LCD,

38
mientras que el modo de 8 bits requiere 10 u 11 pines. El pin RW es el único que puede omitirse si se
usa la pantalla en modo de solo escritura y deberá conectarse a tierra si no se usa. En este caso el
microcontrolador no puede leer desde el chip controlador de pantalla, pero en la mayoría de las
situaciones esto no es necesario.

11.3.1. Conexión básica de una pantalla lcd 16×2 con una interface de 8 bits

Las conexiones para el modo de 8 bits son algo más complicadas, ya que se requiere las 8 líneas de
datos activas. En este caso se utiliza los 8 bits del puerto B, aunque se puede usar cualquier
combinación de pines. RA0 ahora funciona como señal de selección de registro (RS) y RA1 como
señal de habilitación (E). En la Figura 2 se muestra las conexiones para el modo de 8 bits en un
PIC16F877.

Figura 2. Las conexiones para el modo de 8 bits en un PIC16F877

11.3.2. Configuración de las instrucciones con una interface de 8 bits

Esta forma de manejo es la más sencilla de programar, pero tiene la desventaja de utilizar 8 pines del
microcontrolador solo para el envío de datos y otros 2 pines para las señales de control.

En principio en la mayor parte de las aplicaciones se va requerir escribir en el LCD y rara vez leer en el
mismo, por lo tanto en este curso se dedicará exclusivamente a escribir en el LCD. Esto implica que el
pin de selección de lectura/escritura (R/W) se conectará siempre a tierra GND.

Si se utiliza el puerto B como bus de datos (manejará los pines D0 a D7 del LCD) y el puerto A se
encarga de manejar las señales de control (manejará los pines E y RS del LCD).

39
Se debe programar 2 subrutinas, una que se llama INSTRUC que será invocada cuando se quiera enviar
una instrucción al módulo LCD, por ejemplo limpiar pantalla, indicar una posición de memoria, indicar
si se utiliza interfaz de 8 o 4 bits etc. Y otra subrutina que se llama ESCRIB que será invocada cuando
se quiera escribir un dato para ser visualizado en el módulo LCD.

Se configurará entonces todo el puerto B como salida al igual que los pines RA0 y RA1 del puerto A,
estando asignado cada pin del puerto como se indica a continuación:

Pin PORTB RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0
Pin LCD D7 D6 D5 D4 D3 D2 D1 D0

Pin PORTA RA5 RA4 RA3 RA2 RA1 RA0


Pin LCD E RS

RS = 1  DATO (o sea se va a escribir)


RS = 0  CONTROL (o sea se va a enviar un instrucción al módulo LCD).

RECORDAR: Como solo se va a escribir en pantalla, el pin R/W vale siempre 0 (cero), así que se lo
conecta directamente a tierra.

Evidentemente se debe configurar el PORTB del PIC como salida, y en el PORTA los pines RA0 y
RA1 deben ser configurados como salidas. El resto de los pines del PORTA se puede configurar como
se desee. Recuerde que se está basando esta explicación para interface de 8 bits.

Lo primero que hay que hacer es mandar una serie de instrucciones al módulo LCD que constituyen la
configuración del mismo. Luego recién se podrá escribir el mensaje a presentar en pantalla.

11.3.3. Instrucciones muy importantes (son independientes a que se use interface de 4 u 8 bits)

ACTIVAR FUNCION (Instrucción)

Se informa el tipo de interfaz que se va a usar, 4 o 8 bits. La cantidad de líneas (1 o 2). La fuente de
caracter (5x8 dots o 5x10 dots).

RS R/W# D7 D6 D5 D4 D3 D2 D1 D0
0 0 0 0 0 DL N F - -

DL = 0 interfaz de 4 bits.
DL = 1 interfaz de 8 bits.

N=0 se va a usar 1 sola línea del display.


N=1 se van a usar 2 líneas del display.

F=0 fuente de caracter 5x8 dots.


F=1 fuente de caracter 5x10 dots.

Entonces si se envía al PORTB 30h

40
D7 D6 D5 D4 D3 D2 D1 D0
0 0 1 1 0 0 0 1

DL = 1 interfaz de 8 bits.
N=0 se va a usar 1 sola línea del display.
F=0 fuente de caracter 5x8 dots.

Ahora se debe llamar a la subrutina INSTRU (esta subrutina debe ser creada por el programador, se le
puede dar el nombre que se quiera), la cual va a enviar 30h al PORTB, hacer RS=0 o sea le dice al
LCD que lo que va a recibir es una instrucción (RA0= 0 del PORTA) y E=1 o sea habilita al LCD
(RB1= 0 del PORTA), luego vuelve a hacer E=0 deshabilita.

ACTIVAR DISPLAY (Instrucción)

Configura el estado ON/OFF de todo el display, el estado del cursor y el parpadeo del caracter en la
posición del cursor.

RS R/W# D7 D6 D5 D4 D3 D2 D1 D0
0 0 0 0 0 0 1 D C B

RS se maneja con PORTA


RW# se conecta a tierra

Donde:

D=1 enciende pantalla (activar)


D=0 apaga pantalla (desactivar)

C=1 activar cursor


C=0 desactivar cursor

B=1 parpadea caracter señalado por el cursor


B=0 no parpadea caracter.

Por ejemplo, si envió 0Ch al PORTB

D7 D6 D5 D4 D3 D2 D1 D0
0 0 0 0 1 1 0 0

Se está diciendo:

D=1 encienda la pantalla


C=0 desactivar cursor
B=0 no parpadeo del caracter

Ahora se debe llamar nuevamente a la subrutina INSTRU (esta subrutina debe ser creada por el
programador, se le puede dar el nombre que se quiera), la cual va a enviar 0Ch al PORTB, hacer RS=0
41
o sea le dice al LCD que lo que va a recibir es una instrucción (RA0=0 del PORTA) y E=1 o sea
habilita al LCD (RB1= 0 del PORTA), luego vuelve a hacer E=0 deshabilita.

BORRAR PANTALLA (Instrucción)

Limpia pantalla y retorna el cursor al inicio (dirección 00h del display).

RS R/W# D7 D6 D5 D4 D3 D2 D1 D0
0 0 0 0 0 0 0 0 0 1

Se invoca la subrutina INSTRUC.

SELECCIONAR MODO (Instrucción)

Selecciona el modo de desplazamiento del display y se desplaza o no el cursor.

RS R/W# D7 D6 D5 D4 D3 D2 D1 D0
0 0 0 0 0 0 0 1 ID S

S=0 display no se desplaza, dato fijo en pantalla.


S=1 display se desplaza.

ID = 1 incremento.
ID = 0 decremento.

En el ejemplo se envía al PORTB 06h.

D7 D6 D5 D4 D3 D2 D1 D0
0 0 0 0 0 1 1 0

S=0 dato fijo en pantalla

NOTAR que los unos enviados a D2 y D1 carecen de sentido porque no hay desplazamiento.

Se invoca la subrutina INSTRUC.

Hasta aquí se han enviado las instrucciones previas para inicializar el módulo LCD y dejarlo listo para
la escritura de datos en pantalla.

El envió de los datos se realiza por medio de los pines D0 a D7 del LCD, que se encuentran conectados
a los pines RB0 a RB7 del PORTB B del PIC. Por lo tanto los datos a escribir deben ser enviados a
través del PORTB B.

Un dato se envía por medio de su código ASCII, pero previamente se debe indicar al módulo la
posición de memoria en que deseo escribir el dato que se enviará a continuación. Esto se hace por
medio de la instrucción DDRAM.

42
DDRAM (Instrucción)

Le informa al módulo LCD la dirección de memoria en la cual se va almacenar el código del dato que
se le enviará a continuación.

RS R/W# D7 D6 D5 D4 D3 D2 D1 D0
0 0 1 Posición de memoria a escribir dato

Luego se invoca a subrutina INSTRUC, ya que es una instrucción para el LCD.

NOTA: el 1 en D7 debe ir siempre.

Se aclara algo para no confundirse. Supóngase que se desea escribir 'A' en la posición de memoria 00h
del LCD.

Como se tiene que asegurar el 1 en el D7 se debe enviar al PORTB (recordar que D0 ... D7 del LCD
está conectado a RB0 ... RB7 del PORTB) en lugar de 00h el 80h, esto es para asegurar el 1 en el D7
(el 1 en D7 hace 00000000 se convierta en 10000000 o sea 80h).

Así quedará en este caso:

D7 D6 D5 D4 D3 D2 D1 D0
1 0 0 0 0 0 0 0

Ahora que se le informa donde se va a escribir el dato, se debe enviarlo y aclararle que se está enviando
un dato, para lo cual se necesita una subrutina que justamente hará esto y que se la llamará ESCRIB.

La secuencia de instrucciones sería así:

MOVLW 80h ;carga en W la dirección de memoria del LCD donde se va


almacenar el dato
CALL INSTRUC ;subrutina que gestiona la instrucción con el LCD, en este caso
que tome lo que se está enviando como una dirección de
memoria
MOVLW 'A' ;este es el dato a presentar en pantalla
CALL ESCRIB ;subrutina que gestiona el ingreso del dato al LCD a la posición
de memoria antes enviada.

MAPA DE LA MEMORIA DEL MODULO LCD

En la Tabla 4. Se muestra el Mapa de la Memoria del Módulo LCD (2 líneas x 16 caracteres).

43
Tabla 4. Mapa de la Memoria del Módulo LCD (2 líneas x 16 caracteres)

00h 01h 02h 03h 04h 05h 06h 07h 08h 09h 0Ah 0Bh 0Ch 0Dh 0Eh 0Fh 10h --- 1Fh

40h 41h 42h 43h 44h 45h 46h 47h 48h 49h 4Ah 4B 4C 4D 4Eh 4Fh 50h --- 5Fh

Área no
Área visible visible - 16
posiciones

Como se había mencionado anteriormente, al enviar una dirección hay que asegurar el 1 en D7

Por ejemplo:

D7 D6 D5 D4 D3 D2 D1 D0
1 0 0 0 0 0 0 0

De este modo se podría pensar en un mapa de memoria equivalente del módulo LCD como muestra en
la Tabla 5, en el caso de solo utilizar la zona visible.

Tabla 5. Mapa de la Memoria del Módulo LCD de la zona visible

80h 81h 82h 83h 84h 85h 86h 87h 88h 89h 8Ah 8Bh 8Ch 8Dh 8Eh 8Fh

C0h C1h C2h C3h C4h C5h C6h C7h C8h C9h CAh CB CC CD CEh CFh

Ejemplo 6

A continuación se transcribe la solución completa al ejemplo de escribir en el display el mensaje


'HOLA MUNDO'. Se recomienda observar con detalle las subrutinas INSTRUC y ESCRIB que operan
con interfaz de 4 bit. Se utiliza PIC16F84.

El circuito con interface de 8 bits en Proteus se muestra en la Figura 3.

44
LCD1
LCD16x2

VDD
VSS

VEE

RW
RS

D0
D1
D2
D3
D4
D5
D6
D7
E
1
2
3

4
5
6

7
8
9
10
11
12
13
14
BOXER
13 33
OSC1/CLKIN RB0/INT
14 34
OSC2/CLKOUT RB1
X1 1 35
MCLR/Vpp/THV RB2
36
RB3/PGM
2 37
RA0/AN0 RB4
3 38
RA1/AN1 RB5
20Mhz R1 4
RA2/AN2/VREF- RB6/PGC
39
10k 5 40
RA3/AN3/VREF+ RB7/PGD
C2 C1 6
RA4/T0CKI
22p 22p 7 15
RA5/AN4/SS RC0/T1OSO/T1CKI
16
RC1/T1OSI/CCP2
8 17
RE0/AN5/RD RC2/CCP1 VDD
9 18
RE1/AN6/WR RC3/SCK/SCL
10 23
VDD RE2/AN7/CS RC4/SDI/SDA
24
RC5/SDO
25
RC6/TX/CK
26
RC7/RX/DT
19
RD0/PSP0
20
RD1/PSP1
21
RD2/PSP2
22
RD3/PSP3
27
RD4/PSP4
28
RD5/PSP5
29
RD6/PSP6
30
RD7/PSP7
PIC16F877

Figura 3. Circuito en Proteus con interface de 8 bits

;muestra en LCD - HOLA MUNDO en interfase de 8 bits

processor 16f877
include <p16f877.inc>

;Define variables
cblock 0x20
temp5
endc

org 00
inicio
bsf STATUS,5 ;se posiciona en banco1
clrf TRISB ;define el PORTB como salida
clrf TRISA ;define el PORTA como salida
movlw 06h ; carga w con literal 06h
movwf ADCON1; carga ADCON1 con w = 06h para entradas digitales
bcf STATUS,5

;-------inicializacion del LCD--------

;------------Una instruccion-----------
movlw 30h; carga w con literal 30h
;la intrucc asociada dice: interface de datos de 8 bits y se va a usar
;1 linea - corresponde a la intruccion ACTIVAR FUNCION
call instruc; enviaremos este valor al PORTB y por lo tanto al LCD

45
;------------otra instruccion-----------
movlw 06h; carga w con literal 06h
;la intrucc asociada dice: dato fijo en pantalla- corresponde a la intruccion
;SELECCIONAR MODO
call instruc; enviaremos este valor al PORTB y por lo tanto al LCD

;------------otra instruccion-----------
movlw 0Ch; carga w con literal 0Ch
;la intrucc asociada dice: encienda la pantalla y desactive el cursor-
;corresponde a la intruccion ENCENDER O APAGAR PANTALLA
call instruc; enviaremos este valor al PORTB y por lo tanto al LCD

;------------otra instruccion-----------
movlw 01h; carga w con literal 01h
;la intrucc asociada dice: BORRAR PANTALLA
call instruc; enviaremos este valor al portb y por lo tanto al LCD

;----fin inicializacion del LCD-----

;---escritura de datos---------------
movlw 80h; se indica posicion de memoria del LCD en que quiere
; escibir el dato
call instruc
movlw " "; este es dato - al estar entre comillas se le indica al
; compilador que el dato requerido es el valor ASCII del
; caracter
call escrib

;repito con el resto de los datos

movlw 81h;
call instruc
movlw "H";
call escrib

movlw 82h;
call instruc
movlw "O";
call escrib

movlw 83h;
call instruc
movlw "L";
call escrib

movlw 84h;
call instruc
movlw "A";
call escrib

movlw 85h;
call instruc
movlw " ";
call escrib

movlw 86h;
call instruc
movlw "M";
call escrib

46
movlw 87h;
call instruc
movlw "U";
call escrib

movlw 88h;
call instruc
movlw "N";
call escrib

movlw 89h;
call instruc
movlw "D";
call escrib

movlw 8Ah;
call instruc
movlw "O";
call escrib

movlw 8Bh;
call instruc
movlw ".";
call escrib

movlw 8Ch;
call instruc
movlw ".";
call escrib
;---fin escritura de datos-----------

;--------SUBRUTINAS-------------

;----------------------------------------
;Subrutina que manda una instruccion al LCD
instruc
bcf PORTA,0 ; hace 0 el bit 0 del PORTA o sea hace RS=0 - Le dice
; al LCD que es una intruccion lo que va a recibir
bsf PORTA,1 ; hace 1 el bit 1 del PORTA - O sea E=1 habilita LCD
movwf PORTB; carga contenido de w en PORTB
call retardo
bcf PORTA,1; hace 0 el bit 1 del PORTA - O sea E=0 habilita LCD
call retardo
return

;----------------------------------------
;Subrutina que manda un dato al LCD
escrib
bsf PORTA,0 ; hace 1 el bit 0 del PORTA o sea hace RS=1 - Le dice
; al LCD que es es un dato lo que va a recibir
bsf PORTA,1 ; hace 1 el bit 1 del PORTA - O sea E=1 habilita LCD
movwf PORTB; carga contenido de w en PORTB
call retardo
bcf PORTA,1; hace 0 el bit 1 del PORTA - O sea E=0 habilita LCD
call retardo
return

;----------------------------------------
;Subrutina retardo
retardo
47
movlw 0ffh; carga literal 0ffh en w
movwf temp5; mueve contenido de w a temp5
decr
decfsz temp5,1 ; decrementa temp5 y guarda resultado en temp5- salta
; la intruccion siguiente si temp es igual a cero
goto decr
return

;--------FIN SUBRUTINAS--------

end

Ejemplo 7 (Otra Aplicación con interface de 8 bits)

Realizar una aplicación que muestre el siguiente mensaje en el lcd 2x16, en forma repetida, con un
tiempo de muestreo de 1 seg:

Hello World! Primera columna


Im OpenBoxer Segunda columna

1. Diagrama de Conexiones

Figura 4. muestra las conexiones para el modo de 8 bits en un PIC16F877

Figura 4. Las conexiones para el modo de 8 bits en un PIC16F877

2. El código es el siguiente:
48
processor 16F877
include <P16F877.INC>

;Variables para DELAY


cblock 0x30
val1
val2
endc

org 0 ;Vector de RESET

;Configuración de puertos
clrf PORTB ;Limpia PORTB
clrf PORTD ;Limpia PORTD
bsf STATUS, RP0
bcf STATUS, RP1 ;Selecciona el banco 1
clrf TRISB ;Configura PORTB como salida
clrf TRISD ;Configura PORTD como salida
bcf STATUS,RP0 ;Regresa al banco 0

START_LCD:
call INICIA_LCD ;Configura el LCD
call M1 ;Muestra Mensaje
call LINEA2 ;Configura línea 2
call M2 ;Muestra Mensaje
goto START_LCD

;Mensaje a enviar
M1:
movlw 'H' ;Mueve 'H' a W
movwf PORTB ;Mueve lo que hay en W a PORTB
call ENVIA ;Imprime en LCD
movlw 'e'
movwf PORTB
call ENVIA
movlw 'l'
movwf PORTB
call ENVIA
movlw 'l'
movwf PORTB
call ENVIA
movlw 'o'
movwf PORTB
call ENVIA
movlw ' '
movwf PORTB
call ENVIA
movlw 'W'
movwf PORTB
call ENVIA
movlw 'o'
movwf PORTB
call ENVIA
movlw 'r'
movwf PORTB
call ENVIA
movlw 'l'
movwf PORTB
call ENVIA
movlw 'd'
49
movwf PORTB
call ENVIA
movlw '!'
movwf PORTB
call ENVIA
return
M2:
movlw 'I' ;Mueve 'I' a W
movwf PORTB ;Mueve lo que hay en W a PORTB
call ENVIA ;Imprime en LCD
movlw 'm'
movwf PORTB
call ENVIA
movlw ' '
movwf PORTB
call ENVIA
movlw 'O'
movwf PORTB
call ENVIA
movlw 'p'
movwf PORTB
call ENVIA
movlw 'e'
movwf PORTB
call ENVIA
movlw 'n'
movwf PORTB
call ENVIA
movlw 'B'
movwf PORTB
call ENVIA
movlw 'o'
movwf PORTB
call ENVIA
movlw 'x'
movwf PORTB
call ENVIA
movlw 'e'
movwf PORTB
call ENVIA
movlw 'r'
movwf PORTB
call ENVIA
return

;Subrutina para inicializar el lcd


INICIA_LCD:
bcf PORTD,0 ; RS=0 MODO INSTRUCCION
movlw 0x01 ; El comando 0x01 limpia la pantalla en el LCD
movwf PORTB
call COMANDO ; Se da de alta el comando
movlw 0x0C ; Selecciona la primera línea
movwf PORTB
call COMANDO ; Se da de alta el comando
movlw 0x3C ; Se configura el cursor
movwf PORTB
call COMANDO ; Se da de alta el comando
bsf PORTD, 0 ; RS=1 MODO DATO
return

50
;Subrutina para enviar comandos
COMANDO:
bsf PORTD,1 ; Pone ENABLE en 1
call DELAY ; Tiempo de espera
call DELAY
bcf PORTD, 1 ; ENABLE=0
call DELAY
return

;Subrutina para enviar un dato


ENVIA:
bsf PORTD,0 ; RS=1 MODO DATO
call COMANDO ; Se da de alta el comando
return

;Configuración Líneal 2 LCD


LINEA2:
bcf PORTD, 0 ; RS=0 MODO INSTRUCCION
movlw 0xc0 ; Selecciona línea 2 en el LCD
movwf PORTB
call COMANDO ; Se da de alta el comando
return

; Subrutina de retardo
DELAY:
movlw 0xFF
movwf val2
ciclo:
movlw 0xFF
movwf val1
ciclo2:
decfsz val1,1
goto ciclo2
decfsz val2,1
goto ciclo
return
END

11.4. Manejo del Lcd con Interfaz de 4 Bits

11.4.1. Conexión básica de una pantalla lcd 16×2 con una interface de 4 bits

Las conexiones para el modo de 4 bits en un PIC16F877 se muestran en la Figura 5. Se utilizan los
primeros 4 bits del puerto A (RA0-RA3) como bus de datos. RB0 como señal de habilitación (E) y
RB1 como señal de selección de registro (RS).

51
Figura 5. Las conexiones para el modo de 4 bits en un PIC16F877

11.4.2. Configuración de las instrucciones con una interface de 4 bits

Es el caso más utilizado para aprovechar los puertos del PIC. Dejando disponibles pines de los mismos
para otras funciones.

Por ejemplo si se usa el PORTB:

RB4 RB5 RB6 RB7 respectivamente conectados a D4 D5 D6 D7

RB0 RB1 a RS y E respectivamente

RW va a tierra

Las subrutinas INSTRUC y ESCRIB serán diferentes que para el caso de interfaz de 8 bits. Estas
enviaran primero los 4bit MSB y luego los 4 bits LSB.

Ejemplo 8

El programa presenta los siguientes mensajes en el LCD con interfaz de 4 bits:

(C) DJB_2009  Primera fila


E  Segunda fila

Repite los siguientes mensajes:

Limpia la pantalla
52
(C) DJB_2009  Primera fila

Limpia la pantalla
RECREO……  Primera fila

Limpia la pantalla
ALARMA……  Primera fila

El circuito con interface de 4 bits en Proteus se muestra en la Figura 6.

LCD1
LCD16x2

VDD
VSS

VEE

RW
RS

D0
D1
D2
D3
D4
D5
D6
D7
E
1
2
3

4
5
6

7
8
9
10
11
12
13
14
BOXER
13 33
OSC1/CLKIN RB0/INT
14 34
OSC2/CLKOUT RB1
X1 1 35
MCLR/Vpp/THV RB2
36
RB3/PGM
R1 2
RA0/AN0 RB4
37
10k 3 38
RA1/AN1 RB5
4 39
20Mhz RA2/AN2/VREF- RB6/PGC
5 40
RA3/AN3/VREF+ RB7/PGD
C2 C1 6
RA4/T0CKI
22p 22p 7 15
RA5/AN4/SS RC0/T1OSO/T1CKI
16
RC1/T1OSI/CCP2
8 17
VDD RE0/AN5/RD RC2/CCP1 VDD
9 18
RE1/AN6/WR RC3/SCK/SCL
10 23
RE2/AN7/CS RC4/SDI/SDA
24
RC5/SDO
25
RC6/TX/CK
26
RC7/RX/DT
19
RD0/PSP0
20
RD1/PSP1
21
RD2/PSP2
22
RD3/PSP3
27
RD4/PSP4
28
RD5/PSP5
29
RD6/PSP6
30
RD7/PSP7
PIC16F877

Figura 6. Circuito en Proteus con interface de 4 bits

;muestra en LCD -MENSAJES en interface de 4 bits, se usara 4 bits MSB del


;PORTB para envio de datos a 4bits - RB4 RB5 RB6 RB7 respectivamente
;conectados ;a D4 D5 D6 D7 y RB0 RB1 a RS y E ;respectivamente- RW va a tierra.
;---------------------------------------------------------------------

processor 16f877
include <p16f877.inc>

;Comienza listado de registros de proposito general empleados en este


;programa
;Define variables
cblock 0x20
;--------------registros de subrutina retardo-------------
temp5
;--------------registros de subrutina INSTRUC/ESCRIB---------
aux
;------------- registros de subrutina espera y larga--------
tempo1
tempo2
53
tempo3
endc
;--------Fin del listado de registros de proposito general empleados en este

;------INICIO programa principal-----

org 00
inicio
bsf STATUS,5 ;se posiciona en banco1
clrf TRISB ;define el PORTB como salida
movlw b'00001100'
movwf TRISA
movlw 06h ; carga w con literal 06h
movwf ADCON1; carga ADCON1 con w = 06h para entradas digitales

bcf STATUS,5; se posiciona en banco 0

call lcd; se llama subrutina de inicializacion del LCD

call copyright; escribe copyright


call largaespera

mostrar
btfsc PORTA,2 ; si en el pin 2 del PORTA hay un 0 entonces muestra
; mensaje1-alternado con mensaje3
goto frase2
call mensaje1
call espera
frase2
btfsc PORTA,3 ; si en el pin 3 del PORTA hay un 0 entonces muestra
; mensaje2-alternado con mensaje3
goto frase3
call mensaje2
call espera
frase3
call copyright ; si recibe un 0 en pines 2 y 3 de PORTA muestra
; mensaje3
call espera
goto mostrar

;---fin programa principal-----------

;--------SUBRUTINAS-------------

;-------Subrutina LCD ------------------------------------


;esta subrutina inicia la pantalla LCD
;esta subrutina no utiliza registros de proposito general
lcd
;------------una instruccion-----------
movlw 02h; carga w con literal 02h RB1=1 o sea E=1 habilito LCD
call instruc; enviaremos este valor al PORTB y por lo tanto al LCD

;------------otra instruccion-----------
movlw 28h; carga w con literal 28h
;la intrucc asociada dice: interfase de datos de 4 bits y se va a usar 2
;lineas - corresponde a la intruccion ACTIVAR FUNCION
call instruc; enviaremos este valor al PORTB y por lo tanto al LCD

;------------otra instruccion-----------
movlw 06h; carga w con literal 06h
54
;la intrucc asociada dice: dato fijo en pantalla- corresponde a la intruccion
;SELECCIONAR MODO
call instruc; enviaremos este valor al PORTB y por lo tanto al LCD

;------------otra instruccion-----------
movlw 0Ch; carga w con literal 0Ch
;la intrucc asociada dice: encienda la pantalla y desactive el
;cursor- corresponde a la intruccion ENCENDER O APAGAR PANTALLA
call instruc; enviaremos este valor al PORTB y por lo tanto al LCD

;------------otra instruccion-----------
movlw 01h; carga w con literal 01h
;la intrucc asociada dice: BORRAR PANTALLA
call instruc; enviaremos este valor al PORTB y por lo tanto al LCD

return
;-------Fin Subrutina LCD --------

;--------------------Subrutina INSTRC/ESCRIB-------------------------
;Subrutina que manda una instruccion o dato al LCD
;Esta subrutina utiliza el registro de proposito general aux
instruc
bcf PORTB,0; RB0=0 o sea RS=0 es una instruccion para LCD
goto dato2
escrib
bsf PORTB,0; RB0=1 o sea RS=1 es una dato para LCD

dato2
;lo que se va hacer es para no alterar el contenido de los 4 bit menos
;significativos del PORTB
movwf aux; guarda contenido de w en aux
movlw 0fh; carga 00001111 en w
andwf PORTB,1 ; AND entre w=00001111 y PORTB y guarda resultado en
; PORTB
;lo que se termino de hacer es para no alterar el contenido de los 4 bit
;menos significativos dell PORTB

movf aux,0; carga aux en w


andlw b'11110000'; AND entre literal 11110000 y w en el cual esta
; copiado aux (guarda el resultdado en w)
iorwf PORTB,1; OR entre w y PORTB - guarda resultado en PORTB
bsf PORTB,1; RB1=1 o sea E=1 habilito LCD
call retardo
bcf PORTB,1; RB1=E=0 deshabilita LCD
call retardo
;---terminó envio 4 bit MSB-------

;---comienza el envio de 4 bit LSB---


movlw 0fh; carga 00001111 en w
andwf PORTB,1 ; AND entre w=00001111 y PORTB - guarda resultado en
; PORTB
swapf aux,0; intercambia NIBLES de aux y lo guarda en w
andlw b'11110000'; AND entre literal 11110000 y w en el cual esta
; copiado aux con los NIBLES intercambiados
;(guarda el resultdado en w)
iorwf PORTB,1; OR entre w y PORTB - guarda resultado en PORTB
;lo que se termino de hacer es para no alterar el contenido de los 4 bit
;menos significativos dell PORTB

bsf PORTB,1; RB1=1 o sea E=1 habilito LCD


55
call retardo
call retardo
bcf PORTB,1; RB1=E=0 deshabilita LCD
call retardo
;---termino el envio de lo 4bit LSB---

return
;---------Fin subrutina que manda instruccion o dato al LCD------

;------------------Fin Subrutina INSTRC/ESCRIB------------------------


;----------------Subrutina retardo----------------------------------
;Esta subrutina utiliza el registro de proposito general temp5
retardo
movlw 0ffh; carga literal 0ffh en w
movwf temp5; mueve contenido de w a temp5
decr
decfsz temp5,1 ; decrementa temp5 y guarda resultado en temp5- salta
; la intruccion siguiente si temp5 es igual a cero
goto decr
movlw 0ffh; carga literal 0ffh en w
movwf temp5; mueve contenido de w a temp5
decr2
decfsz temp5,1 ; decrementa temp5 y guarda resultado en temp5- salta
; la intruccion siguiente si temp5 es igual a cero
goto decr2
return
;---------------- Fin Subrutina retardo----------------------------------

;----------Subrutina largaespera - -----------------------


largaespera
;Escribe una E en display para indicar larga espera
movlw 0CFh;
call instruc
movlw "E";
call escrib
;Termino de escribir la E

retardo4
incf tempo1,1
retardo5
incf tempo2,1
retardo6
incf tempo3,1
btfss tempo3,7
goto retardo6
clrf tempo3
btfss tempo2,7
goto retardo5
clrf tempo2
btfss tempo1,7
goto retardo4
clrf tempo1

;Llama reiteradas veces a subrutina espera -para incrementar espera


;Tanto como subrutina espera y largaespera utilizan los mismos registros de
;proposito general
call espera
call espera
; Termino de llamar reiteradas veces a subrutina espera

56
;Limpia la E en display que escribio al principio
movlw 0CFh;
call instruc
movlw " ";
call escrib
;Termino de limpiar la E

return
;-----Fin subrutina largaespera-----------------------

;----------Subrutina espera -( Retardo general)--------


;Esta subrutina utiliza los registros de proposito general- temp1-temp2-temp3
;Son los mismos que utiliza la subrutina largaespera
espera nop
retardo1
incf tempo1,1
retardo2
incf tempo2,1
retardo3
incf tempo3,1
btfss tempo3,6
goto retardo3
clrf tempo3
btfss tempo2,5
goto retardo2
clrf tempo2
btfss tempo1,4
goto retardo1
clrf tempo1
return
;-------------------Fin subrutina espera------------

;---Subrutina copyright-----------
copyright
movlw 01h
; call instruc; borrar pantalla

movlw 80h; se indica posicion de memoria del LCD en que quiere


; escibir el dato
call instruc
movlw "("; este es el primer caracter del mensaje - al estar entre
; comillas se le indica al compilador que el dato requerido
; es el valor ASCII del caracter
call escrib

;repito con el resto de los caracteres


movlw 81h;
call instruc
movlw "c";
call escrib

movlw 82h;
call instruc
movlw ")";
call escrib

movlw 83h;
call instruc
movlw " ";
call escrib
57
movlw 84h;
call instruc
movlw "D";
call escrib

movlw 85h;
call instruc
movlw "J";
call escrib

movlw 86h;
call instruc
movlw "B";
call escrib

movlw 87h;
call instruc
movlw "-";
call escrib

movlw 88h;
call instruc
movlw "2";
call escrib

movlw 89h;
call instruc
movlw "0";
call escrib

movlw 8Ah;
call instruc
movlw "0";
call escrib

movlw 8Bh;
call instruc
movlw "9";
call escrib

call espera
call espera
call espera
call espera

return
;---Fin subrutina copyright-----------------------------------------------

;---Subrutina mensaje1-----------
mensaje1
movlw 01h
; call instruc; borrar pantalla

movlw 80h; se indica posicion de memoria del LCD en que quiere


; escibir el dato
call instruc
movlw "R"; este es el primer caracter del mensaje - al estar entre
; comillas se le indica al compilador que el dato requerido
58
; es el valor ASCII del caracter
call escrib

;repito con el resto de los caracteres


movlw 81h;
call instruc
movlw "E";
call escrib

movlw 82h;
call instruc
movlw "C";
call escrib

movlw 83h;
call instruc
movlw "R";
call escrib

movlw 84h;
call instruc
movlw "E";
call escrib

movlw 85h;
call instruc
movlw "O";
call escrib

movlw 86h;
call instruc
movlw ".";
call escrib

movlw 87h;
call instruc
movlw ".";
call escrib

movlw 88h;
call instruc
movlw ".";
call escrib

movlw 89h;
call instruc
movlw ".";
call escrib

movlw 8Ah;
call instruc
movlw ".";
call escrib

movlw 8Bh;
call instruc
movlw ".";
call escrib

59
call espera
call espera
call espera
call espera

return
;---Fin subrutina mensaje1-----------------------------------------------

;---Subrutina mensaje2-----------
mensaje2
movlw 01h
; call instruc; borrar pantalla

movlw 80h; se indica posicion de memoria del LCD en que quiere


; escibir el dato
call instruc
movlw "A"; este es el primer caracter del mensaje - al estar entre
; comillas se le indica al compilador que el dato requerido
; es el valor ASCII del caracter
call escrib

;repito con el resto de los caracteres


movlw 81h;
call instruc
movlw "L";
call escrib

movlw 82h;
call instruc
movlw "A";
call escrib

movlw 83h;
call instruc
movlw "R";
call escrib

movlw 84h;
call instruc
movlw "M";
call escrib

movlw 85h;
call instruc
movlw "A";
call escrib

movlw 86h;
call instruc
movlw ".";
call escrib

movlw 87h;
call instruc
movlw ".";
call escrib

movlw 88h;
call instruc
movlw ".";
60
call escrib

movlw 89h;
call instruc
movlw ".";
call escrib

movlw 8Ah;
call instruc
movlw ".";
call escrib

movlw 8Bh;
call instruc
movlw ".";
call escrib

call espera
call espera
call espera
call espera

return
;---Fin subrutina mensaje2-----------------------------------------------

;--------FIN SUBRUTINAS-------------

end

Ejemplo 9 (Otra Aplicación con interface de 4 bits)

Ejemplo para la Gestión de un LCD.

Mostrar en el LCD la siguiente leyenda:

HELLO WORD!_

NOTA: Si no se logra ver nada en el LCD, tal vez, sea necesario regular el contraste del LCD a
través de R2 conectado al pin 3 del LCD.

El circuito a ser utilizado para comprender mejor las explicaciones de la Gestión de un LCD, se
presenta en la Figura 7:

61
Figura 7. Circuito de la Gestión de un LCD

1. Diagrama de Flujo:
Diagrama de flujo del Programa Principal:

62
INICIO

RS←2
E←3
DB4←4
DB5←5
DB6←6
DB7←7

LcdInit

W←10H

LcdLocate

W←’H’

LcdSendData

.
.

W←’!’

LcdSendData

Los diagramas de flujo de las subrutinas de este programa no se presentan, pero en la Tabla 5 se
describen brevemente las Subrutinas de gestión del LCD.

Tabla 5. Subrutinas de gestión del LCD


Subrutina Función
LcdInit Se ocupa de inicializar el display y de limpiar la pantalla. Debe ser llamada una sola vez y antes de cualquier otra
subrutina.

No requiere paso de parámetros.


LcdClear Limpia el contenido de pantalla y reposiciona el cursor en la primera fila y primera columna.

No requiere paso de parámetros.

63
LcdLocate Posiciona arbitrariamente el cursor dentro del área visible del display.

Necesita el valor de fila y columna para posicionar el cursor en el registro W. Los bits desde D0 a D3 contienen
el valor de columna (eje Y) y los bits desde D4 a D7 los valores de filas (eje X). La numeración de las filas parte
de cero hacia arriba, la de columna parte de cero hacia la derecha.
LcdSendData Envía el carácter ASCII al LCD a ser visualizado en la posición donde se encuentra el cursor.

Requiere cargar en el registro W el valor ASCII del carácter.


LcdSendCommand Envía un comando al LCD. Los comandos reconocidos por el LCD, están especificados en la hoja de datos del
mismo.

Requiere cargar en el registro W el valor de 8 bit del comando a enviar.


LcdSendByte Esta función, es usada internamente por las demás y se ocupa de efectuar el deslizamiento de los datos y comando
de 8 bit en el bus de datos de 4 bit.

2. Código en Assembler:

El código de este ejemplo se muestra a continuación:

;**************************************************
;
; LCD.ASM
;
;**************************************************

PROCESSOR 16F877
RADIX DEC
INCLUDE "P16F877.INC"

__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;LCD Control lines


LCD_RS equ 2 ;Register Select
LCD_E equ 3 ;Enable

;LCD data line bus


LCD_DB4 equ 4 ;LCD data line DB4
LCD_DB5 equ 5 ;LCD data line DB5
LCD_DB6 equ 6 ;LCD data line DB6
LCD_DB7 equ 7 ;LCD data line DB7

ORG 20H
tmpLcdRegister res 2
msDelayCounter res 2

Start
bsf STATUS,RP0 ;Swap to register bank 1

movlw 00000011B ; Set as output just the LCD's lines


movwf TRISB

bcf STATUS,RP0 ;Swap to register bank 0

;LCD inizialization
call LcdInit

;Locate LCD cursor on row 0, col 0


movlw 10H
64
call LcdLocate

;Shows "HELLO WORLD" string on LCD


movlw 'H'
call LcdSendData
movlw 'E'
call LcdSendData
movlw 'L'
call LcdSendData
movlw 'L'
call LcdSendData
movlw 'O'
call LcdSendData
movlw ' '
call LcdSendData
movlw 'W'
call LcdSendData
movlw 'O'
call LcdSendData
movlw 'R'
call LcdSendData
movlw 'L'
call LcdSendData
movlw 'D'
call LcdSendData
movlw ' '
call LcdSendData
movlw '!'
call LcdSendData

foreverLoop
goto foreverLoop

;**********************************************************************
; Delay subroutine
; W = Requested delay time in ms (clock = 4MHz)
;**********************************************************************

msDelay
movwf msDelayCounter+1
clrf msDelayCounter+0

; 1 ms (about) internal loop


msDelayLoop
nop
decfsz msDelayCounter+0,F
goto msDelayLoop
nop

decfsz msDelayCounter+1,F
goto msDelayLoop

return

;**********************************************************************
; Init LCD
; This subroutine must be called before each other lcd subroutine
;**********************************************************************

65
LcdInit
movlw 30 ;Wait 30 ms
call msDelay

;****************
; Reset sequence
;****************

bcf PORTB,LCD_RS ;Set LCD command mode

;Send a reset sequence to LCD


bsf PORTB,LCD_DB4
bsf PORTB,LCD_DB5
bcf PORTB,LCD_DB6
bcf PORTB,LCD_DB7

bsf PORTB,LCD_E ;Enables LCD


movlw 5 ;Wait 5 ms
call msDelay
bcf PORTB,LCD_E ;Disables LCD
movlw 1 ;Wait 1ms
call msDelay

bsf PORTB,LCD_E ;Enables LCD


movlw 1 ;Wait 1ms
call msDelay
bcf PORTB,LCD_E ;Disables LCD
movlw 1 ;Wait 1ms
call msDelay

bsf PORTB,LCD_E ;Enables E


movlw 1 ;Wait 1ms
call msDelay
bcf PORTB,LCD_E ;Disables E
movlw 1 ;Wait 1ms
call msDelay

bcf PORTB,LCD_DB4
bsf PORTB,LCD_DB5
bcf PORTB,LCD_DB6
bcf PORTB,LCD_DB7

bsf PORTB,LCD_E ;Enables LCD


movlw 1 ;Wait 1ms
call msDelay
bcf PORTB,LCD_E ;Disabled LCD
movlw 1 ;Wait 1ms
call msDelay

;Set 4 bit data bus length


movlw 28H;
call LcdSendCommand

;Entry mode set, increment, no shift


movlw 06H;
call LcdSendCommand

;Display ON, Curson ON, Blink OFF


movlw 0EH

66
call LcdSendCommand

;Clear display
call LcdClear

return

;**********************************************************************
; Clear LCD
;**********************************************************************

LcdClear
;Clear display
movlw 01H
call LcdSendCommand

movlw 2 ;Wait 2 ms
call msDelay

;DD RAM address set 1st digit


movlw 80H;
call LcdSendCommand

return

;**********************************************************************
; Locate cursor on LCD
; W = D7-D4 row, D3-D0 col
;**********************************************************************

LcdLocate
movwf tmpLcdRegister+0

movlw 80H
movwf tmpLcdRegister+1

movf tmpLcdRegister+0,W
andlw 0FH
iorwf tmpLcdRegister+1,F

btfsc tmpLcdRegister+0,4
bsf tmpLcdRegister+1,6

movf tmpLcdRegister+1,W
call LcdSendCommand

return

;**********************************************************************
; Send a data to LCD
;**********************************************************************

LcdSendData
bsf PORTB,LCD_RS
call LcdSendByte
return

;**********************************************************************
; Send a command to LCD

67
;**********************************************************************

LcdSendCommand
bcf PORTB,LCD_RS
call LcdSendByte
return

;**********************************************************************
; Send a byte to LCD by 4 bit data bus
;**********************************************************************

LcdSendByte
;Save value to send
movwf tmpLcdRegister

;Send highter four bits


bcf PORTB,LCD_DB4
bcf PORTB,LCD_DB5
bcf PORTB,LCD_DB6
bcf PORTB,LCD_DB7

btfsc tmpLcdRegister,4
bsf PORTB,LCD_DB4
btfsc tmpLcdRegister,5
bsf PORTB,LCD_DB5
btfsc tmpLcdRegister,6
bsf PORTB,LCD_DB6
btfsc tmpLcdRegister,7
bsf PORTB,LCD_DB7

bsf PORTB,LCD_E ;Enables LCD


movlw 1 ;Wait 1ms
call msDelay
bcf PORTB,LCD_E ;Disabled LCD
movlw 1 ;Wait 1ms
call msDelay

;Send lower four bits


bcf PORTB,LCD_DB4
bcf PORTB,LCD_DB5
bcf PORTB,LCD_DB6
bcf PORTB,LCD_DB7

btfsc tmpLcdRegister,0
bsf PORTB,LCD_DB4
btfsc tmpLcdRegister,1
bsf PORTB,LCD_DB5
btfsc tmpLcdRegister,2
bsf PORTB,LCD_DB6
btfsc tmpLcdRegister,3
bsf PORTB,LCD_DB7

bsf PORTB,LCD_E ;Enables LCD


movlw 1 ;Wait 1ms
call msDelay
bcf PORTB,LCD_E ;Disabled LCD
movlw 1 ;Wait 1ms
call msDelay

68
return

END

Análisis del programa fuente

En la primera parte, están definidas algunas constantes:

;LCD Control lines


LCD_RS equ 2 ;Register Select
LCD_E equ 3 ;Enable

;LCD data line bus


LCD_DB4 equ 4 ;LCD data line DB4
LCD_DB5 equ 5 ;LCD data line DB5
LCD_DB6 equ 6 ;LCD data line DB6
LCD_DB7 equ 7 ;LCD data line DB7

Estas constantes definen la asociación entre las líneas del PIC (todas conectadas al PortB) y las líneas
del LCD. Cada definición en particular, será usada en las subrutinas de gestión del LCD en lugar de
cada número de identificación de las líneas de I/O.

tmpLcdRegister res 2
msDelayCounter res 2

Seguidamente, se reserva el espacio para dos registros: tmpLcdRegister, usado por la subrutina de
gestión del LCD y msDelayCounter usada por la subrutina msDelay que generan los retardos por
sofware de 1 ms para el contenido del registro W. Estas subrutinas son usadas siempre por las
subrutinas de gestión para generar las temporizaciones requeridas durante la transmisión de datos y
comandos.

Sigue la definición de las líneas de conexión entre el PIC y el LCD y luego se arriba a la primera
subrutina que interesa:

call LcdInit

LcdInit es una subrutina que debe ser llamada solamente una vez en el inicio del programa y antes de
cualquier otra subrutina de gestión. Ella se ocupa de efectuar todas las operaciones necesarias para
inicializar correctamente el LCD y permite que las funciones sucesivas operen correctamente.

Con las instrucciones:

movlw 00H
call LcdLocate

se posiciona el cursor del display en la primera fila y en la primera columna de la pantalla. Los
caracteres enviados sucesivamente, serán visualizados a partir de esta posición. Los cuatro bits más
significativos del valor cargado en el registro W con la instrucción:

movlw 00H

69
Contienen el número de fila donde se quiere posicionar el cursor, los cuatro bits menos significativos,
contienen el número de columna.

Cambiando el valor en el registro W, se obtiene posicionamientos diferentes. Con el valor 10H, por
ejemplo, se obtiene:

HELLO WORD!_

Con el valor 12H, se obtiene:


HELLO WORD!_

A esta altura, para visualizar cada carácter de la frase, se usan las siguientes instrucciones:

movlw 'H'
call LcdSendData

Y así sucesivamente para cada letra a ser visualizada. El incremento de la posición del cursor, se hace
automáticamente.

Mayor información sobre el uso de los LCD’s, puede obtenerse de la hoja de datos que el fabricante
debe proveer.

70

También podría gustarte