Microcontroladores PIC 18F4550 Ejemplos prácticos Ricardo Álvarez González 1 MICROCONTR OLADORES PIC 18F4550: EJEMPLOS PRÁCTICOS EN LENGUAJE ENSAMBLADOR Introducción Se presenta a continuación unas notas que tiene n por objetivo facilitar al lector en el uso de microcontroladores PIC 18F4550, mediante una serie de ejemplos prácticos que le permiten iniciarse en el uso de los mismos, y también aprender a escribir sus propios programas, tomando como base estos ejemplos, complementándolos a sus necesidades específicas añadiéndole su toque personal. La información teórica se tomó de las hojas de datos del fabricante, pero no se pretende ser un reemplazo de ellas, por lo que siempre se sugiere acudir a las mismas, de tal manera que se sugiere al lector consultar dicha literatura para abundar sobre el tema que considere que se trata aquí de una manera muy escueta. Se agradece la elaboraci ón de la mayoría de las figuras a Cesar Hugo Pimentel Romero.
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Los componentes de una computadora estan conectados mediante buses:
Bus de direcciones: Cuando el procesador “lee instrucciones de” ó “escribe datos a” la memoria a
la que desea accesar. Cada dispositivo de E/S como un monitor, teclado o disco duro también
tienen una dirección de memoria.
Los datos son transferidos vía el bus de datos, cuando el CPU busca datos de la memoria primero
se leen las direcciones del bus de direcciones, después el microprocesador genera la señal delectura, y accede a las instrucciones mediante el bus de datos.
El bus de control, consiste en un conjunto de señales dedicadas a realizar operaciones tales como
lectura (RD), escritura (WR), especificar si una lectura o escritura se refiere a un dispositivo de
memoria, o a un dispositivo de salida (M/IO), la señal de reset etc.
Un sistema puede tener una jerarquía de buses:Puede usar bus de direcciones, de datos y de control para accesar a la memoria y a un controlador
de E/S. El controlador de E/S, en cambio, podría accesar a todos los dispositivos de E/S usando un
segundo bus llamado: Bus local ó Bus de E/S. La perspectiva práctica describe al bus PCI, un bus
local usado comunmente en computadoras personales
MicrocontroladoresUn microcontrolador es una computadora completa de capacidades limitadas orientada aefectuar tareas de control, la cual se encuentra encapsulada en un solo circuito integrado.
Es un sistema mínimo encapsulado en un solo circuito integrado, el cual contienememorias de: programa, datos, datos no volátiles (en algunos casos), puertos deentrada/salida, temporizadores/contadores, temporizador vigía, oscilador, unidades decomunicación serial, convertidores analógico digitales etc.
Existe una amplia variedad de fabricantes de microcontroladores, entre los cuales podemos
mencionar algunos de ellos:
INTEL, el cual es considerado como el padre de los microcontroladores, con la familia MCS8048, posteriormente comercializó a la popular familia MCS 8051, ambas de 8 bits, tienetambién poderosas familias de microcontroladores de 16 bits, por ejemplo: MCS96,algunos elementos de esta familia tienen convertidores A/D integrados y otros tienencontroladores de motores.
MOTOROLA, con sus familias de microcontroladores 68HC05, 68HC07.
TEXAS INSTRUMENTS tiene su familia de microcontroladores de 8 bits TMS370, a la cualpertenecen dispositivos con una variedad de periféricos y prestaciones, en encapsuladosdesde 28 hasta 68 pines.
ATMEL inicialmente contribuyó con microcontroladores que eran “clones” de otros
fabricantes, especialmente de Intel, pero bajo tecnología Flash, uno de sus elementoscaracterísticos es el 89C51, el cual es un clon del Intel 87C51. Actualmente contribuye demanera muy importante con sus microcontroladores AVR.
MICROCHIP, con su amplia gama de familias de microcontroladores de 8, 16 y 32 bits,presentan desde microcontroladores en encapsulados de 6 pines, 33 instrucciones y stackde dos niveles (familia 10F), dispositivos de 8 pines con un conjunto de 33 instrucciones(familia 16c5x), familias de 35 instrucciones con chips con una variedad de características yconsecuentemente, diversos números de pines (familias 16xxx), familias poderosas de 77instrucciones, con un tamaño de palabra de instrucción de 16 bits (familias: 17xxx, 18xxx)varios de estos microcontroladores disponen también de interfaz USB., también ofrece susfamilias de Controladores digitales de señales: los dsPICs, los cuales son chips poderososde 16 bits que incluyen instrucciones para procesamiento de señales mediante su máquinaDSP, debido al auge que han tenido estos procesadores, el fabricante también ofrece estoschips, pero sin la máquina DSP (para aplicaciones que no usan las instrucciones DSP),obteniendo así la familia PIC24XXX; para realizar aplicaciones inalámbricas: los rfPICs hastallegar a los poderosos microcontroladores de 32 bits, con la familia PIC32.
Cada uno de los fabricantes antes mencionados proveen típicamente de las especificaciones de
sus chips (data sheets), notas de aplicación, herramientas de desarrollo etc. Algunos fabricantes
venden esta información, otros la proporcionan libremente en sus sitios de internet.
NÚMEROS EN BINARIO, HEXADECIMAL Y DECIMALAntes de entrar de lleno a la revisión de los conceptos arquitectónicos fundamentales de los
microcontroladores PIC18F4550, es importante recordar los equivalentes en binario y decimal de
los números hexadecimales, ya que la notación hexadecimal es abreviada, debido a que cada uno
de sus números representan 4 bits, es más cómodo y menos susceptible de error escribir un
StackLa pila (stack) permite cualquier combinación de hasta 31 llamadas y reconocimientos de
interrupción. El contador del programa (PC) es metido (pushed) al stack , cuando se ejecuta unainstrucción CALL o RCALL o cuando una interrupción es reconocida. El PC es recuperado ( pulled )
del stack, en cualquiera de las instrucciones siguientes: RETURN, RETLW o RETFIE.
El stack funciona como una RAM de 31 palabras de 21 bits cada una y un apuntador del stack
La parte alta del stack es de lectura y escritura. Existen tres registros: TOSU, TOSH y TOSL, que
mantienen el contenido de la localidad apuntada por el registro STKPTR, esto permite a los
usuarios implementar un stack por software si es que fuera necesario.
El registro STKPTR contiene el valor del apuntador del stack y los bits de estado STKFUL (stack
lleno) y STKUNF (subflujo del stack). El registro 4-1 muestra el registro STKPTR. El apuntador delstack se incrementa cuando se introducen valores en la pila, y se decrementa cuando se extraen
valores de ella.
Reinicializaciones (resets) por stack lleno, o por desbordamiento pordefecto del stack.
Estos resets son habilitados por la programación del bit STVREN. Cuando este bit es deshabilitado,
la condición de full o de underflow pondrá los bits STKFUL o STKUNF, respectivamente, pero no
causará una reinicialización del dispositivo, si el bit STVREN es puesto, además de ponerse los bits
antes mencionados en caso de que se llene el stack o se produzca un subflujo, causará que se
reinicie (reset) el procesador. Los bits STKFUL y STKUNF deben de limpiarse por software o
mediante un reset por apagado del dispositivo (POR).
PCL, PCLATH Y PCLATUEl contador de programa (PC) especifica la dirección en la que se buscará la instrucción (fetch)
para su ejecución. El PC es de 21 bits de ancho. El byte mas bajo es llamado el registro PCL, este
registro es de lectura y escritura. El byte alto es el registro PCH, este registro contiene los bits
PC<15:8> y no se puede leer o escribir directamente; se pueden hacer actualizaciones al registro
PCH se pueden efectuar mediante el registro PCLATH. El byte superior es llamado PCU, este
registro contiene los bits PC<20:16> y no se puede leer o escribir directamente, se pueden hacer
correcciones al registro PCU mediante el registro PCLATU.
El PC direcciona bytes en la memoria de programa. Para prevenir que el PC este desalineado con
las palabras de instrucción, el LSB del PCL esta fijo al valor de “0”. El PC incrementa por dos las
direcciones de instrucciones secuenciales en la memoria de programa.
CICLO DE INSTRUCCIÓN.
La entrada de reloj (mediante OSC1) es dividida internamente por cuatro, para generar cuatroseñales de reloj en cuadratura, no traslapados, llamados Q1, Q2 Q3 y Q4. Internamente el PC es
incrementado cada Q1, la instrucción es buscada de la memoria de programa y capturada en el
registro de instrucción en Q4. La instrucción es decodificada y ejecutada durante el siguiente Q1
hasta Q4. Las señales de reloj y la ejecución del flujo de instrucciones se muestran en la siguiente
El operando de destino “d” indica en donde se colocará el resultado de la operación:
Si d=0, el resultado es colocado en el registro de trabajo (WREG).
Si d=1, el resultado es colocado en el registro f especificado por la instrucción.
Todas las instrucciones orientadas a bits tienen tres operandos:
1. El registro de archivo (representado por “f”).
2. El bit en el registro de archivo (representado por “b”).
3. La memoria accedida (representada por “a”).
El operando de bit “b”, selecciona el número del bit afectado por la operación, mientras el de
registro de archivo “f” representa el número del archivo en el cual está localizado el bit.
Las instrucciones de literal pueden usar algunos de los siguientes operandos:
Un valor de literal que será cargado en un registro de archivo, especificado por “k”.
El registro deseado en el cual se cargará el valor de la literal (representado por “f”).
Ningún operando requerido (representado por “-”).
Las instrucciones de control pueden usar algunos de los siguientes operandos:
Una dirección de memoria de programa (especificado por “n”).
El modo de las instrucciones Call o Return (especificado por “s”). El modo de instrucciones para lectura y escritura de la tabla (especificado por “m”).
Todas las instrucciones de una sola palabra son ejecutadas en un solo ciclo, a menos que una
prueba condicional sea verdadera, o que el contador de programa haya cambiado como resultado
de la instrucción. En esos casos, la instrucción toma dos ciclos de instrucción, con el ciclo de
instrucción adicional ejecutado como un NOP.
Una vez que se ha presentado superficialmente el repertorio de instrucciones de los PIC18FXXX,
para mostrar algunos programas de ejemplo, solo nos hace falta revisar algunas directivas delensamblador MPASM, que son imprescindibles para escribir programas.
PANORAMA GENERAL DE MPASMMPASM es una aplicación basada en el sistema operativo DOS o en WINDOWS, que suministra una
plataforma para desarrollar código en lenguaje ensamblador para las familias de
microcontroladores PIC de 12-bit, 14-bit, 16-bit, y 16-bit incrementadas de Microchip,
manteniendo una compatibilidad directa con el ambiente de desarrollo integrado (IDE) MPLAB.
Directivas del lenguajeLas directivas son comandos que aparecen en el código fuente, pero no son traducidas
directamente en código de operación. Son usadas para controlar el ensamblador: su entrada,
salida y localización de datos. Muchas de las directivas de ensamblador tienen nombres y formatos
alternos. Esos podrían existir para proporcionar compatibilidad con ensambladores previos de
Microchip y para ser compatible con prácticas individuales de programación.
Existen cinco tipos básicos de directivas proporcionadas por MPASM:
a. Directivas de control. Permiten secciones de código condicionalmente ensamblado.b. Directivas de datos. Controlan la localización de variables en memoria y proporcionan una
manera para referirse a los datos simbólicamente mediante nombres significativos.c. Directivas de listado. Controlan el formato del archivo de listado (*.lst) generado por
MPASM. Permitiendo la especificación de títulos, paginación y otros controles del listado.d. Macro directivas. Controlan la localización de datos y la ejecución mediante definiciones
de macros.e. Directivas de archivo Objeto. Se usan solamente cuando se crea un archivo objeto.
Ahora estamos listos para escribir nuestro primer programa. Se trata de un ejemplo muy simple,
en el cual se han colocado leds en cada uno de los pines del PUERTO D, lo que queremos hacer es
encender secuencialmente cada uno de ellos, para lo cual debemos de declarar dicho puerto como
salida, lo cual conseguimos escribiendo la palabra adecuada de configuración en su registro de
dirección de datos TRISD, tomando en cuenta que al escribir un 1 en un bit del registro TRISD,
configurará su bit de puerto asociado como entrada, en el caso de escribir un cero se configurará
su bit de puerto respectivo como salida. Esto se muestra en las figuras siguientes, debido a que
esto se cumple para cualquier bit del puerto D, (y en verdad para cualquier puerto de los
microcontroladores PIC), se han representado como los bits de puerto m y n, en donde pueden
tener valores máximos entr 0 y 7, ya que PORTD es de 8 bits
TRISDm 0
PORTDm Out, Salida
TRISDn 1
PORTDn In, entrada
Nótese que esto es mas facil de recordar si escribimos los nombres en ingles: 1 In entrada, 0 Out
LIST P=18F4550 ;directiva para definir el procesador#include <P18F4550.INC> ;definiciones de variables especificas del procesador
;******************************************************************************;Bits de configuración
CONFIG FOSC = INTOSC_XT ;Oscilador INT usado por el uC , XT usado por el USBCONFIG BOR = OFF ;BROWNOUT RESET DESHABILITADOCONFIG PWRT = ON ;PWR UP Timer habilitadoCONFIG WDT = OFF ;Temporizador vigia apagadoCONFIG MCLRE=OFF ;Reset apagadoCONFIG PBADEN=OFFCONFIG LVP = OFF
;******************************************************************************;Definiciones de variables
CBLOCK 0x000 ;ejemplo de definición de variables en RAM de accesocontENDC
LIST P=18F4550 ;directiva para definir el procesador#include <P18F4550.INC> ;definiciones de variables especificas del procesador
;******************************************************************************;Bits de configuración
CONFIG FOSC = INTOSC_XT ;Oscilador INT usado por el uC , XT usado por el USBCONFIG BOR = OFF ;BROWNOUT RESET DESHABILITADOCONFIG PWRT = ON ;PWR UP Timer habilitadoCONFIG WDT = OFF ;Temporizador vigia apagadoCONFIG MCLRE=OFF ;Reset apagadoCONFIG PBADEN=OFFCONFIG LVP = OFF
;Inicio del programa principalbcf OSCCON,IRCF2,0bsf OSCCON,IRCF0,0 ;Oscilador interno a125 kHzmovlw 0x0Fmovwf ADCON1,0 ;Puertos Digitalesclrf PORTD,0clrf TRISD,0 ;Puerto D Configurado como salidabsf PORTD,0,0 ;enciende LED conectado en RD0
correi rlcf PORTD,F,0call repite ;llama a una rutina más largabra correi
return;******************************************************************************repite movlw d'25' ;llama 25 veces a la rutina retardo
movwf ciclollama call retardo
decfsz ciclo,F,0bra llamareturnEND
Programa E1.1 Segunda Versión de rotación de luces
En la segunda versión del programa de rotación de luces, el tiempo en el que durará encendidocada led, será veinticinco veces más largo que en el ejemplo 1, esto se logra con la subrutina repite
la cual llama a la rutina retardo veinticinco veces. Notese como el número de veces se especifica
directamente en decimal, para lo cual es necesario encerrar el número entre apóstrofes y debe ir
precedido por una d de “decimal”, esto está expresado en la instrucción:
movlw d'25' ;llama 25 veces a la rutina retardo
Con esta rutina repite se puede modificar el número de veces que se llamará a la rutina retardo
Ejemplo 2 Control de un exhibidor de siete segmentos de ánodo común
conectado al PORTD.Ahora estamos listos para controlar un exhibidor de siete segmentos. Como ya sabemos existen
básicamente dos tipos de displays de siete segmentos, de acuerdo a como se muestra en la
Si ya se entendió el funcionamiento del ejemplo 1, en donde encendemos un led, ahora en este
ejemplo podemos encender ocho leds, dispuestos en un exhibidor de siete segmentos, tambiénllamado por su nombre en ingles display; se mostrarán los dígitos del 0 al 9, la duración de
exhibición de cada uno de ellos está determinada por la rutina repite del ejemplo anterior.
Ejemplo2: Control de un exhibidor de siete segmentosSe muestra en la tabla siguiente, la asignación de los pines del puerto D para la conexión de los
segmentos del display:
RD7 RD6 RD5 RD4 RD3 RD2 RD1 RD0
Puntodecimal
g f E d c b a
Tabla E2
Los códigos de siete segmentos para el display de ánodo común, (considerando en
este caso que encienden con cero) son los siguientes:
Usando el mismo orden de los bits mostrado en la tabla E2, genere los códigos de siete segmentospara los dígitos hexadecimales faltantes, para un exhibidor de siete segmentos de ánodo común
Ejercicio E2.2Genere los códigos de siete segmentos, usando el mismo orden de los bits mostrado en la tablaE2 para los dígitos hexadecimales, para un exhibidor de siete segmentos de cátodo común
LIST P=18F4550 ;directiva para efinir el procesador#include <P18F4550.INC> ;definiciones de variables especificas del procesador
;******************************************************************************;Bits de configuración
CONFIG FOSC = INTOSC_XT ;Oscilador INT usado por el uC , XT usado por el USBCONFIG BOR = OFF ;BROWNOUT RESET DESHABILITADOCONFIG PWRT = ON ;PWR UP Timer habilitadoCONFIG WDT = OFF ;Temporizador vigia apagadoCONFIG MCLRE=OFF ;Reset apagadoCONFIG PBADEN=OFFCONFIG LVP = OFF
;******************************************************************************;Definiciones de variables
CBLOCK 0x000 ;ejemplo de definición de variables en RAM de accesocontcicloENDC ;fin del bloque de constantes
Programa E2: Control de un display de siete segmentos
Como podemos apreciar en el código anterior, el control del display es muy simple, consiste
solamente en enviar cada código por el puerto d y llamar a la rutina repite para que permanezca
encendido el tiempo que dure la misma, esto se logra con las instrucciones:
Cero movlw 0xC0 ;código del ceromovwf PORTD,0call repite
De tal manera que estas tres instrucciones se repiten diez veces, empezando por el código del ceroy terminando con el del nueve, después de esto se regresa el programa nuevamente al código delcero, con la instrucción de salto incondicional:
bra cero
Se muestra en la siguiente figura el diagrama de la simulación en ISIS Proteus:
Ejercicio E2.3Vimos que el ejemplo anterior, consistía en una repetición de diez veces del mismo bloque deinstrucciones. Reescriba el código del ejemplo 2, disminuyendo el número de instrucciones,usando subrutinas
Ejercicio E2.4Extienda el programa del ejemplo 2, para mostrar los dígitos hexadecimales del 0,1,2,…,F,0…
Ejemplo 3: Contador ascendente-descendenteHasta ahora, hemos realizado ejemplos usando únicamente puertos de salida, por lo cual, el
siguiente ejemplo usará un puerto de entrada RB0, de acuerdo a como se explica en la siguiente
LIST P=18F4550 ;directiva para definir el procesador#include <P18F4550.INC> ;definiciones de variables especificas del procesador
;******************************************************************************;Bits de configuración
;CONFIG FOSC = INTOSC_XT ;Oscilador INT usado por el uC , XT usado por el USBCONFIG BOR = OFF ;BROWNOUT RESET DESHABILITADOCONFIG PWRT = ON ;PWR UP Timer habilitadoCONFIG WDT = OFF ;Temporizador vigia apagadoCONFIG MCLRE=OFF ;Reset apagadoCONFIG PBADEN=OFFCONFIG LVP = OFF
;******************************************************************************;Definiciones de variables
CBLOCK 0x000 ;ejemplo de definición de variables en RAM de accesocontciclo
ENDC ;fin del bloque de constantes;******************************************************************************;Reset vector
return;******************************************************************************repite movlw d'1' ;llama una vez a la rutina retardo
movwf ciclonada2 nop
call retardodecfsz ciclo,1,0bra nada2returnEND
Programa E3: Contador ascendente descendente
En este programa, no es necesario declarar el pin RB0, ya que después de un reset, los puertos del
microcontrolador están declarados como entrada. Una vez que se ha mostrado un digito en el
display, el tiempo determinado por la rutina “repite”, se verifica el nivel en el puerto B, con la
instrucción:
btfss PORTB,0,0
si el nivel de RB0 es alto, la instrucción brincará la instrucción:bra cero
y mandará a escribir en el puerto D el código de siete segmentos del uno, de lo contrario seramificará para mostrar el código de siete segmentos del cero. El programa está formado por larepetición de las instrucciones mostradas a continuación:
uno movlw 0xF9 ;código del unomovwf PORTD,0call repitebtfss PORTB,0,0bra cero
Se observa en el programa E3 la secuencia completa, notando que si el nivel en RB0 es alto, el
conteo será ascendente, de lo contrario será descendente.
Ejercicio E3.1Agregue al programa 3 las instrucciones necesarias para exhibir los dígitos hexadecimales de la A
a la F
InterrupcionesEsta familia de microcontroladores tiene múltiples fuentes de interrupción. Una interrupción
consiste en que el procesador suspende temporalmente la tarea que está ejecutando para atender
a algún periférico, mediante la ejecución de una rutina de servicio de interrupción, una vez que se
concluye esta, el procesador continua con la tarea que estaba ejecutando antes de haber sido
18 Con el chorro de agua pequeño, la cubeta se llena en 10 minutos
¿Cuál será el valor de precarga necesario para que este sistema nos indique que ha transcurrido
un tiempo de 6 minutos? Seria depositar en la cubeta 4/10 de su capacidad. Si se ha entendido el
funcionamiento de este sistema, se entenderá fácilmente el funcionamiento típico de los timers.
La familia a la que pertenece el PIC18F4550 incluye los timers:
Timer Número de bitsTemporizador/contador 0 8 ó 16
Temporizador/contador 1 16
Timer2 8Temporizador/contador 3 16
Temporizador/Contador 0Este periférico puede funcionar como timer, teniendo como base de tiempo, el ciclo interno de
ejecución de instrucciones Fosc/4, pero también puede funcionar como contador, contando losimpulsos que le llegan por el pin RA4 en su función alterna T0CKI (Timer 0 Clock Input ).
Este periférico puede funcionar en dos modalidades: 8 y 16 bits
En la figura siguiente, se muestra el diagrama de bloques del timer 0 en modo de 8 bits. Como se
puede observar, existen multiplexores para seleccionar mediante sus bits de selección (los cuales
Debemos de tomar en cuenta, que este valor obtenido en la expresión 2, tenemos que restarlo delmáximo valor del timer (de la misma manera que restábamos la cantidad de agua de la capacidad
total de la cubeta).
De tal manera que:
Valor_precarga=máximo valor del timer-valor_del_timer (3)
Valor_precarga= máximo valor del timer-Temporización/(4.Tosc.valor_prescaler) (4)
El máximo valor para un timer de 8 bits es 0xFF +1, ya que se desborda al iniciar nuevamente en
cero, en decimal este valor es 256.
Para un timer de 16 bits el máximo valor al que puede llegar es 0xFFFF y se desborda en 0xFFFF+1,
es decir 65536 en decimal.
Para saber más, se recomienda revisar en las hojas de datos del fabricante DS39632C:
Módulo timer 0
Módulo timer 1
Módulo timer 2
Módulo timer 3
Ejemplo 4Ahora estamos listos para nuestro siguiente ejemplo, usando el timer 0. Se quiere generar un
tiempo de 500 ms, considerando que el oscilador principal funciona a una frecuencia de 4 MHz y
un prescaler de 64, sustituyendo estos valores en la ecuación 3 tenemos:
Valor_del_timer=500 ms/(4*0.25 µs*64)
=500 ms/64 µs
=7812.5
Debido a que solo podemos usar valores enteros, el valor del timer es 7812, y notamos tambiénque debemos de usar el timer de 16 bits, pero recordemos que este valor tenemos que restarlo
del valor máximo. Por lo que:
Valor_precarga=65536-7812=57724
Este valor tenemos que cargarlo en los registros TMR0H y TMR0L, por lo que debemos de
convertir el valor a hexadecimal, que es 0xE17C. Esto se hará con las instrucciones:
movlw 0xE1movwf TMR0H,0movlw 0x7cmovwf TMR0L,0 ;valor de precarga para 500ms a 4MHz
Ejercicios E4.1
a. Considerando un pre divisor de 32 y una frecuencia del oscilador principal de 1 MHz,calcule el valor necesario de precarga del timer 0, para generar un intervalo de tiempo de100 ms.
b. Use el timer 0, para generar una frecuencia de 440 Hz, proponga los valores necesariosdel oscilador principal, así como del pre divisor
;******************************************************************************;Bits de configuración
CONFIG FOSC = INTOSC_XT ;Oscilador INT usado por el uC , XT usado por el USBCONFIG BOR = OFF ;BROWNOUT RESET DESHABILITADOCONFIG PWRT = ON ;PWR UP Timer habilitado
CONFIG WDT = OFF ;Temporizador vigia apagadoCONFIG MCLRE=OFF ;Reset apagadoCONFIG PBADEN=OFFCONFIG LVP = OFF
;******************************************************************************;Definiciones de variables
CBLOCK 0x000 ;ejemplo de definición de variables en RAM de accesoflags ;definimos la dirección 0 como registro de banderasENDC ;fin del bloque de constantes
;******************************************************************************ORG 0x0000 ; vector de resetbra inicioorg 0x08 ;vector de interrupción
bra RST0 ;ramifica servicio interrupción T0org 0x0020
inicio bsf OSCCON,IRCF2,0 ;Inicio del programa principalbsf OSCCON,IRCF1,0bcf OSCCON,IRCF0,0 ;Oscilador interno a 4 MHzmovlw 0x0Fmovwf ADCON1,0 ;Puertos Digitalesclrf PORTD,0clrf TRISD,0 ;Puerto D Configurado como salidamovlw 0x95movwf T0CON,0 ;timer 16 bits prescalerX64movlw 0XA0
movwf INTCON,0 ;interrupcion TMR0 habilitadamovlw 0xE1movwf TMR0H,0movlw 0x7cmovwf TMR0L,0 ;valor de precarga para 500ms a 4MHz
cero movlw 0xC0 ;código del ceromovwf PORTD,0call repitebtfss PORTB,0,0bra nueve
uno movlw 0xF9 ;código del unomovwf PORTD,0call repite
btfss PORTB,0,0bra cero
dos movlw 0xA4 ;código del dosmovwf PORTD,0call repitebtfss PORTB,0,0bra uno
INTERRUPCIÓN EXTERNA 1Ya revisamos en el ejemplo anterior cómo funciona la interrupción del temporizador 0. Ahora
veremos cómo funciona la interrupción externa INT1. Recordemos que para lograr que el contador
fuera ascendente o descendente, dependía del valor del nivel lógico presente en el puerto de
entrada RB0:
RB0 Conteo
1 Ascendente0 descendente
Nuestra nueva versión de este programa, hará uso de la interrupción externa INT1, la cual estáasociada al puerto de entrada RB1, colocaremos en este pin un botón pulsador y en vez de
detectar un nivel lógico en este pin, se detectará un flanco ascendente generado por el
accionamiento de este botón, que interrumpirá al procesador y en su rutina de servicio de
interrupción modificaremos el valor de una bandera de propósito general, para cambiar el sentido
del contador.
Para activar la interrupción INT1, debemos:
poner el bit INT1IE del registro INTCON3:
limpiar el bit INT1IP, ya que le asignaremos un nivel de prioridad bajo a esta interrupción
Según la tabla anterior, el valor para el registro INTCON3 sera: 1000 1000 es decir 0x88
En este programa ejemplificaremos el uso de prioridades de interrupción, por lo que el timer 0, lo
dejaremos en alta prioridad y la interrupción externa en baja, pero debemos de activar el bit de
habilitación de prioridades de interrupción IPEN (Interrupt Priority ENable), que está ubicado en el
registro de control de reset RCON:
RCON
R/W-0 R/W-1 U-0 R/W-1 R-1 R-1 R/W-0 R/W-0
IPEN SBOREN - /RI /T0 /PD /POR /BORBit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Para activar este bit lo haremos con la instrucción:
bsf RCON,IPEN,0 ;habilitamos prioridades de interrupción
En el ejemplo anterior, solamente teníamos una interrupción activa: la del timer 0, que funcionaba
en alta prioridad, ya que no habíamos activado las prioridades de interrupción; esta interrupción
cuando se generaba, respondía en el vector 0x08. Ahora se adicionan prioridades de interrupción
e INT1, le asignamos baja prioridad, por lo que responderá en el vector de baja prioridad de
interrupción 0x 18.
Para saber más, se recomienda revisar en las hojas de datos del fabricante, DS39632C:
Interrupciones en los pines INTn
En el siguiente programa se muestran en rojo las instrucciones correspondientes a la activación de
la interrupción INT1, así como también las instrucciones que se modificaron como consecuencia
de este cambio
Ejemplo 5 Contador ascendente descendente, usando el timer 0 y lainterrupción externa INT1;******************************************************************************
LIST P=18F4550 ;directiva para definir el procesador#include <P18F4550.INC> ;definiciones de variables especificas del procesador
;******************************************************************************;Bits de configuración
;CONFIG FOSC = INTOSC_XT ;Oscilador INT usado por el uC , XT usado por el USBCONFIG BOR = OFF ;BROWNOUT RESET DESHABILITADOCONFIG PWRT = ON ;PWR UP Timer habilitadoCONFIG WDT = OFF ;Temporizador vigía apagadoCONFIG MCLRE=OFF ;Reset apagadoCONFIG PBADEN=OFFCONFIG LVP = OFF
;******************************************************************************;Definiciones de variables
CBLOCK 0x000 ;ejemplo de definición de variables en RAM de accesoflags ;banderas
22 Diagrama de simulación del contador con interrupción externa
Como hemos visto, en el programa anterior, mandamos a exhibir los códigos de manera directa,
uno por uno, con la consecuencia de que el programa tiene una secuencia repetitiva de
instrucciones, pero ¿qué pasaría si quisiéramos usar dos displays y exhibir un conteo hasta el 99?
¡No sería conveniente repetir una secuencia de instrucciones 100 veces!, por lo cual tenemos que
usar otro método más eficiente para enviar los códigos de siete segmentos al PORTD, que
veremos en la siguiente sección.
Ejemplo 6: Manejando tablas por el método del “goto calculado” Para crear tablas de búsqueda en memoria de programa con los microcontroladores PIC18, existen
dos métodos:
1. Goto calculado
2. Instrucciones específicas de lectura de tabla
En este ejemplo, veremos el primer método. Para crear un goto calculado, es necesario primero
cargar el valor de desplazamiento en el registro w y sumar un valor de desplazamiento (offset) al
contador de programa, seguida de un grupo de instrucciones retlw nn, como se muestra a
continuación:
movf desplazamiento,Wcall tabla
.
.tabla addwf PCL
retlw nn
retlw nn..
retlw nn
El valor del desplazamiento indica el número de bytes que debe avanzar el contador de programa
y debe ser un múltiplo de dos. Este método recibe el nombre de goto calculado, porque igual que
una instrucción goto, realiza una ramificación, o equivalentemente un salto; pero se logra
sumando un valor al contador de programa.
Debido a que únicamente se suma el desplazamiento al PCL, debe tenerse cuidado de que el grupo
de instrucciones retlw nn, no cruce una página de 256 bytes, ya que el PCL es de 8 bits, y cundo
cambie de FF a 00, no generará un acarreo hacia el registro PCH, ocasionando un salto fuera delgrupo de instrucciones retlw nn, afectando la secuencia del programa, que percibiremos como
un funcionamiento errático del mismo.
Veamos la nueva versión del contador ascendente-descendente del 0 al 9:
;Bits de configuración;CONFIG FOSC = INTOSC_XT ;Oscilador INT usado por el uC , XT usado por el USBCONFIG BOR = OFF ;BROWNOUT RESET deshabilitadoCONFIG PWRT = ON ;PWR UP Timer habilitadoCONFIG WDT = OFF ;Temporizador vigia apagadoCONFIG MCLRE=OFF ;Reset apagadoCONFIG PBADEN=OFFCONFIG LVP = OFF
;******************************************************************************;Definiciones de variables
CBLOCK 0x000 ;ejemplo de definición de variables en RAM de accesoflags ;banderasíndiceENDC ;fin del bloque de constantes
;******************************************************************************ORG 0x0000 ;vector de resetbra inicioorg 0x08 ;vector de alta prioridadbra RST0 ;ramifica servicio interrupcion T0org 0x18 ;vector de baja prioridadbra RSINTorg 0x0020
inicio bsf OSCCON,IRCF2,0bsf OSCCON,IRCF1,0bcf OSCCON,IRCF0,0 ;Oscilador interno a 4 MHz
movlw 0x0Fmovwf ADCON1,0 ;Puertos Digitalesclrf PORTD,0clrf TRISD,0 ;Puerto D Configurado como salida
retlw 0xb0 ;código del tresretlw 0x99 ;código del cuatroretlw 0x92 ;código del cincoretlw 0x82 ;código del seisretlw 0xb8 ;código del sieteretlw 0x80 ;código del ocho
retlw 0x98 ;código del nueveend
Programa 6, contador ascendente-descendente del 0 al 9, usando tablas con goto calculado
Como podemos ver en el programa 6, la rutina tabla implementa el método de goto calculado,
vemos que en comparación con el programa 5, esta versión es más compacta. Las modificaciones
de este programa aparecen en rojo. Cada uno de los códigos de siete segmentos forma parte de
una instrucción retlw, de tal manera que cuando se produce el retorno de subrutina, el registro W
contiene este código; por lo tanto, usando este método se puede almacenar un dato de un byte
por cada palabra de instrucción, que como recordamos, es de dos bytes.
Ejemplo 7, tablas de búsqueda en memoria de programa usandoinstrucciones de lectura de tablaAhora veremos el segundo método, que ya habíamos mencionado. Para tener un alcance
suficiente, en los PIC18 que tengan mayor capacidad de memoria de programa, tenemos un
apuntador de tabla de 24 bits, llamado TBLPTR (TaBLe PoinTeR) que está distribuido en tres
registros de 8 bits:
TBLPTRL
TBLPTRH
TBLPTRU
Apuntador de tabla partesuperior
Apuntador de tabla parte alta Apuntador de tabla parte baja
TBLPTRU TBLPTRH TBLPTRL
También existe un registro llamado TABLAT (TABle LATch), para almacenar el dato leído,
correspondiente a la dirección especificada por TBLPTR.
RSINT bcf INTCON3,INT1IF,0 ;Limpiamos bandera de interrupciónbtg flags,1,0 ;bit monitor de interrupciónretfie
;********************************************************************org 0x200 ;DB directiva que Define Byte
DB 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xB8,0x80,0x98END
Programa 7, usando instrucciones de lectura de tabla
Nuevamente en el programa 7, se muestran en rojo, las instrucciones usadas para la lectura de
tablas. Iniciamos el apuntador de tabla, TBLPTR con la dirección 0x200, nótese que esto lohacemos al configurar los tres registros correspondientes al apuntador. También usamos una
nueva directiva: DB, la cual sirve para definir un byte a partir de la dirección especificada por la
directiva ORG que la antecede, en nuestro caso, indica la dirección 0x200, a partir de esta
dirección definimos nuestros códigos de siete segmentos. Usando esta técnica, optimizamos el uso
de la memoria, ya que almacenamos cada código usando un solo byte. Este programa hace
exactamente lo que el anterior, pero usando las instrucciones de lectura de tabla, pero en el
siguiente programa, veremos cómo usar instrucciones de lectura de tabla, para hacer un contador
de 0 al 99.
Ejemplo 8, manejando dos displays de siete segmentosAl tener dos displays, podemos incrementar el conteo hasta el 99, para esto usamos transistores
como interruptores, para encender un solo display a la vez, ya que los segmentos estánconectados en paralelo, es decir, el segmento a esta conectado con el a, el b con el b, y así
sucesivamente, pero solo encenderá el display cuyo transistor está encendido, esta técnica se
conoce como multiplexión, al encender y apagar los transistores a una frecuencia suficientemente
alta (en el órden de los 10 KHz), apreciamos los dos displays como si siempre estuvieran
LIST P=18F4550 ;directiva para definir el procesador#include <P18F4550.INC> ;definiciones de variables especificas del procesador
;******************************************************************************;Bits de configuración
;CONFIG FOSC = INTOSC_XT ;Oscilador INT usado por el uC , XT usado por el USBCONFIG BOR = OFF ;BROWNOUT RESET DESHABILITADOCONFIG PWRT = ON ;PWR UP Timer habilitadoCONFIG WDT = OFF ;Temporizador vigía apagadoCONFIG MCLRE=OFF ;Reset apagadoCONFIG PBADEN=OFFCONFIG LVP = OFF
;******************************************************************************;Definiciones de variables
CBLOCK 0x000 ;ejemplo de definición de variables en RAM de acceso
flags ;banderasiun ;índice de unidadescuni ;código de 7 segmentos de unidadesidec ;índice de decenascdec ;código de 7 segmentos de decenascontENDC ;fin del bloque de constantes
lee movff iun,TBLPTRL ;ajusta apuntador tblrd * ;lee la tabla sin modificar apuntadormovff TABLAT,cuni ;cuni tiene código 7 segmentosmovff idec,TBLPTRL ;ajusta apuntador tblrd * ;lee la tabla sin modificar apuntadormovff TABLAT,cdec ;cdec tiene código 7 segmentos
;******************************************************************************RST0 bcf INTCON,TMR0IF,0 ;apagamos bandera timer0
movlw 0xE1movwf TMR0H,0movlw 0x7cmovwf TMR0L,0 ;valor de precarga para 500ms a 4MHzbsf flags,0,0retfie
;********************************************************************RSINT bcf INTCON3,INT1IF,0 ;Limpiamos bandera de interrupción
btg flags,1,0 ;bit monitor de interrupciónretfie
;********************************************************************org 0x200 ;DB directiva que Define ByteDB 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xB8,0x80,0x98
END
Programa 8, controlando dos exhibidores de 7 segmentos
Como observamos en el código del programa anterior, para controlar dos exhibidores,
necesitamos dos bits de puerto configurados como salida, ya que ellos serán los encargados de
encender y apagar los transistores que manejan los displays. En este programa, no se usa la
instrucción TBLRD *+, ya que tenemos un par de registros en donde se guardan los índices
necesarios para el conteo:
Iun índice de unidades
Idec índice de decenas
El programa mantiene los índices dentro del valor adecuado, iun inicia en cero, y se incrementa
exactamente cada 500 ms, hasta llegar a 9, una vez que iun tiene el valor de 9, en los siguientes
500 ms, iun se regresa a cero, pero a la vez se incrementa idec. Debido a que para encender los
dígitos necesitamos los códigos de 7 segmentos, tenemos también dos registros que los
almacenan
Cuni código de 7 segmentos de unidades
Cdec código de 7 segmentos de decenas
El programa lo que hace es cargar el código de siete segmentos en el PORTD, el tiempo que dura la
rutina retardo, cada 500ms se incrementa iun (y cuando regresa a cero, se incrementa idec) y se
actualiza el código de siete segmentos con las instrucciones:
movff iun,TBLPTRL ;ajusta apuntador con el valor de unidadestblrd * ;lee la tabla sin modificar apuntadormovff TABLAT,cuni ;cuni tiene código 7 segmentos de unidades
movff idec,TBLPTRL ;ajusta apuntador con el valor de decenastblrd * ;lee la tabla sin modificar apuntadormovff TABLAT,cdec ;cdec tiene código 7 segmentos de decenas
Ejemplo 9: Usando un teclado MatricialHasta ahora solamente hemos trabajado básicamente con puertos configurados como salida,también hemos usado un bit de puerto configurado como entrada, para el contador ascendente
descendente. Usaremos un teclado de 16 teclas, pero con la finalidad de optimizar puertos de
entrada, trabajaremos con el teclado matricial, ya que con el solo necesitaremos un puerto de 8
bits, ya que está organizado en cuatro renglones y cuatro columnas. Esto se observa en la
siguiente figura:
Se muestra en la siguiente figura, el diagrama de flujo para el funcionamiento del teclado
;Bits de configuración;CONFIG FOSC = INTOSC_XT ;Oscilador INT usado por el uC , XT usado por el USBCONFIG BOR = OFF ;BROWNOUT RESET DESHABILITADOCONFIG PWRT = ON ;PWR UP Timer habilitadoCONFIG WDT = OFF ;Temporizador vigía apagadoCONFIG MCLRE=OFF ;Reset apagadoCONFIG PBADEN=OFFCONFIG LVP = OFF
;Definiciones de variablesCBLOCK 0x000 ;ejemplo de definición de variables en RAM de accesoflags ;banderasiun ;índice de unidadescuni ;código de unidadesidec ;índice de decenas
cdec ;código de decenasconttecla ;registro de código de tecla pulsadaENDC ;fin del bloque de constantes
ORG 0x0000bra inicioorg 0x08 ;vector de alta prioridadbra RST0 ;ramifica servicio interrupción T0org 0x18 ;vector de baja prioridadbra RSINTorg 0x0020
inicio bsf OSCCON,IRCF2,0bsf OSCCON,IRCF1,0bcf OSCCON,IRCF0,0 ;Oscilador interno a 4 MHz
movlw 0x0Fmovwf ADCON1,0 ;Puertos Digitales
clrf PORTB,0movlw 0xf0movwf TRISB,0 ;Configurar puerto para tecladoclrf PORTD,0clrf TRISD,0 ;Puerto D Configurado como salidamovlw 0xfcmovwf TRISC,0 ;RC0 y RC1 como salidas
movlw 0XE8movwf INTCON,0 ;interrupciones TMR0,RBIE prioridad alta y bajabsf RCON,IPEN,0 ;habilitamos prioridades de interrupciónmovlw 0xE1movwf TMR0H,0movlw 0x7cmovwf TMR0L,0 ;valor de precarga para 500ms a 4MHzmovlw 0x04
movwf INTCON2,0 ; int RBI en baja prioridad y activamos R pull upclrf TBLPTRL,0
movlw 0x02movwf TBLPTRH,0clrf TBLPTRU,0 ;tblptr=0x000200clrf iun,0clrf idec,0 ;iniciamos en 0
lee movff iun,TBLPTRLtblrd * ;leemovff TABLAT,cuni ;cuni tiene codigo 7 segmentosmovff idec,TBLPTRL
movlw 0x0acpfslt tecla,0 ;salta si es que se pulso una tecla entre 0 y 9bra limpiamovff tecla,idec ;carga número de tecla pulsada en decenasmovff idec,TBLPTRLtblrd * ;leemovff TABLAT,cdec ;cdec tiene código 7 segmentosbra exit
Programa 9, Contador de 0-99, con programación de decenas y borrado con un teclado matricial
Ejercicios 9.1Modifique la rutina RSINT, para que al pulsar la tecla A, el conteo sea ascendente y al pulsar latecla D, el conteo sea descendente.
Ejemplo 10: Usando el timer 2
En los programas anteriores, usamos nuevamente nuestra rutina básica de retardo, la cual
funciona por el tiempo de ejecución de instrucciones. Pero ahora, modificaremos esta rutina,
usando el timer 2.
Como podemos ver en su diagrama de bloques, el timer 2 tiene las siguientes característicasúnicas, con respecto a los otros timers existentes en este microcontrolador:
Solamente puede funcionar como timer
Cuenta además del pre divisor, con post divisor
Su funcionamiento no está basado en su desbordamiento, sino que cuenta con un registro
de periodo en donde se coloca su valor de precarga, y mediante un comparador detecta
cuando el timer alcanza y coincide con ese valor
En este ejemplo el timer 2 se utiliza para encender cada display por un intervalo de tiempo de 0.1
ms, el timer 0 se usa para que se incremente el conteo cada 500 ms. Hemos usado nuestroregistro de propósito general (GPR) flags, con la función de sus dígitos como sigue:
movlw 0xf8movwf TRISC,0 ;RC0, RC1 y RC2 como salidasclrf PORTC,0movlw 0x95movwf T0CON,0 ;timer 16 bits prescalerX64movlw 0XE8
movwf INTCON,0 ;interrupciones TMR0,RBIE prioridad alta y bajamovlw 0x06movwf T2CON,0 ;Prescalerx16 y TIMER2 ON, POSTCALER 1 movlw 0x06movwf PR2,0 ;valor para registro de periodo de .1mSbsf IPR1,TMR2IP,0 ;timer2 alta prioridadbsf PIE1,TMR2IE,0 ;habilitamos interrupciónbsf RCON,IPEN,0 ;habilitamos prioridades de interrupciónmovlw 0xE1movwf TMR0H,0movlw 0x7cmovwf TMR0L,0 ;valor de precarga para 500ms a 4MHzmovlw 0x04
movwf INTCON2,0 ;habilitamos int RBI en baja prioridad y R pull upclrf TBLPTRL,0movlw 0x02movwf TBLPTRH,0clrf TBLPTRU,0 ;tblptr=0x000200clrf iun,0clrf idec,0 ;iniciamos en 0
lee movff iun,TBLPTRLtblrd * ;leemovff TABLAT,cuni ;cuni tiene código 7 segmentosmovff idec,TBLPTRLtblrd * ;lee
bra RST2bcf INTCON,TMR0IF,0 ;apagamos bandera timer0movlw 0xE1movwf TMR0H,0movlw 0x7cmovwf TMR0L,0 ;valor de precarga para 500ms a 4MHzbsf flags,0,0retfie
RST2 bcf PIR1,TMR2IF,0
bsf flags,2 ;monitor de .1 msbtg PORTC,2retfie
;********************************************************************RSINT call KBD ;Llamamos rutina del teclado
Modifique el programa 10, calculando el valor de timer 0, para encender cada dígito por unsegundoModifique el programa 10, usando el bit de bandera flags,1 para que dependiendo de su valor,se genere el conteo ascendente o descendente