Documentos de Académico
Documentos de Profesional
Documentos de Cultura
PIC18F4550
2
Contenido.
1. El Microcontrolador PIC18F4550.…………………………………………….…… 4
1.1. La familia de Microcontroladores PIC18F.
2. En Ambiente de Programación.………………………………………………….... 9
2.1. El entorno de programación de MPLAB.
2.2. Archivos de cabecera para el PIC18F4550 en C.
2.3. Descarga de los Programas en el PIC18F4550.
9.1 El Timer 0.
9.1.1 Funciones de C para configurar y operar el timer 0.
9.1.2 Practica E9.1. Generación de una señal de 5 Hz con el TIMER0.
9.2 El Timer 1.
9.2.1 Funciones de C para configurar y operar el timer 1.
9.2.2 Practica E9.2. Un Reloj muy exacto con el TIMER1.
9.3 El Timer 2.
9.3.1 Funciones de C para configurar y operar el timer 2.
9.3.2 Practica E9.3. Generación de una señal de 1kHz con el Timer2.
9.4 El Timer 3.
9.4.1 Funciones de C para configurar y operar el timer 3.
11.Bibliografía………………………………………………………………………….…. 106
1. El Microcontrolador PIC18F4550.
4
Los microcontroladores PIC’s de Microchip son los número uno en ventas de microcontroladores.
2. El Ambiente de Programación.
El software utilizado para realizar los programas del microcontrolador es MPLAB IDE. Este software lo podemos
descargar de manera gratuita de la página web oficial de la firma Microchip Inc. en www.microchip.com. MPLAB
IDE es un editor modular, donde puedes desarrollar los códigos de programas para todos los
microcontroladores de Microchip de 8, 16 o 32 bits que hay hasta el momento.
Para realizar la programación de los microcontroladores PIC en un lenguaje de nivel intermedio como el
lenguaje C, es preciso utilizar un compilador de C. Dicho compilador nos genera archivos en formato Intel-
hexadecimal (*.hex), que es el formato necesario para programar (utilizando un programador de PIC) un
microcontrolador de 6, 8, 18 ó 40 patillas. El compilador de C, que utilizamos es el PCW de la casa CCS Inc. A
su vez, el compilador lo integraremos en un entorno de desarrollo integrado (MPLAB IDE) que nos va a permitir
desarrollar todas y cada una de las fases en que se compone un proyecto, desde la edición hasta la
compilación pasando por la depuración de errores. La última fase, a excepción de la depuración y retoques
hardware finales, será programar el PIC.
Al igual que el compilador de Turbo C, éste "traduce" el código C del archivo fuente (.C) a lenguaje máquina
para los microcontroladores PIC, generando así un archivo en formato hexadecimal (*.HEX), siendo este
archivo *.hex, el que se descarga en el microcontrolador.
Una vez instalado MPLAB IDE y el compilador PCW de CCS en su computadora. Abre MPLAB, este presentara
una pantalla como la que muestra la figura.
En este entorno se programa en base a proyectos, para iniciar un proyecto damos un clic en la opción Project y
seleccionamos de la lista de opciones Project Wizard…, lo que nos abre la pantalla del asistente para la
creación de un nuevo proyecto.
10
De la ventana Project Wizard presionamos Siguiente >, lo que nos permite seleccionar el microcontrolador con
el que vamos a trabajar. Podemos elegir desde los microcontroladores de 8 bits como los PIC 10, PIC12,
PIC14, PIC16, PIC18, los microcontroladores de 16 bits los PIC24 hasta los dsPIC30 y dsPIC33.
Ya elegido el microcontrolador con el que trabajaremos (en nuestro caso es el PIC18F4550), presionamos
Siguiente >, para poder elegir el lenguaje de programación que utilizaremos para crear nuestro código.
Para programar en lenguaje C, de la lista desplegable elegimos la opción CCS C Compiler for
PIC10/12/14/16/18/24/dsPIC30/dsPIC33, lo que nos mostrara la herramienta CCS C compiler (ccsc.exe),
elegimos esta y presionamos Siguiente > nuevamente.
11
En la ventana Step Three, debemos proporcionar un nombre y una ubicación a nuestro proyecto,
asegurándonos de crear una carpeta nueva para guardar en esta los archivos que se generan en nuestro nuevo
proyecto.
Para esto presioné el botón Browse… y se abrirá una nueva venta que nos permitirá dar la ubicación en donde
se guardara nuestro proyecto y además poder asignarle un nombre (se recomienda que el nombre sea corto y
refleje la que el proyecto realizara).
Una vez elegida la ubicación, creado una nueva carpeta para nuestro proyecto y guardado en este lugar nuestro
proyecto, presionamos Siguiente >, lo que nos presenta las opciones para poder agregar o remover archivos a
nuestro proyecto.
Aquí se recomienda agregar un archivo *.c, que contenga algún código el lenguaje C, para no partir de cero en
nuestro nuevo proyecto. Si no se desea agregar algún archivo no hay ningún problema solo presionen
Siguiente> para continuar.
12
Una vez agregados los archivos a tu proyecto, presionamos Siguiente >, y finamente nos presenta un sumario
de características sobre nuestro proyecto, como el dispositivo, el compilador, la ubicación del proyecto y nombre
de nuestro proyecto. De esta última pantalla presionamos Finalizar,
Al presionar finalizar, cerramos el asistente para la creación de un nuevo proyecto y nos mostrara en la pantalla
inicial de MPLAB IDE nuestro proyecto.
El entorno de MPLAB IDE, nos muestra nuestro proyecto, en la parte izquierda de la pantalla y del lado derecho
la pantalla de salidas de compilación donde se muestran los errores y warning de compilación.
13
Ahora comencemos a programar, bien lo primero que necesitamos saber es que, si no agregamos a nuestro
proyecto ningún archivo *.c, tendremos que elegir desde el menú File -> New, para crear un nuevo archivo en
blanco, donde escribiremos nuestro código fuente.
Si nos damos cuenta este nuevo archivo, no tiene ninguna extensión,para darle la extensión la vamos a
guardar como Nombre.c y lo guardaremos dentro de la carpeta del proyecto. Después agregaremos este
archivo Nombre.c a nuestro proyecto dando clic con botón derecho sobre carpeta Source File de nuestro
proyecto y eligiendo la opción Add Files…, esto nos abrirá una pantalla de dialogo, que permite agregar a
nuestro proyecto este archivo que acabamos de guardar en la carpeta de nuestro proyecto como Nombre.c.
14
Una vez agregado este archivo a nuestro proyecto, lo podemos visualizar como un archivo dentro de la carpeta
Source Files de nuestro proyecto.
En este archivo se debe colocar el código en lenguaje C de nuestra aplicación, pero antes debemos saber algo
muy importante. Todas nuestra aplicaciones deben contener al inicio del archivo *.c ciertas sentencia conocidas
como archivos de cabecera.
Los archivos de cabecera que siempre deben contener nuestro archivo.c son:
/*CONFIGURACIÓN PIC*/
El microcontrolador PIC18F4550, cuenta con un puerto USB 2.0 compatible con Windows, que podemos utilizar
como interfaz para descargar los programas en la memoria Flash (usando los Bootloader), mediante la ayuda
del software proporcionado por el compilador PCW de la casa CCS Inc., que podemos encontrar en la carpeta
15
PICC, el software para descargar nuestro archivo *.hex, se llama Serial Input/Output Monitor y se accede a él
mediante la aplicación Siow.exe. El icono de la aplicación Soiw.exe es:
El circuito que utilizamos para descargar los programas en el PIC18F4550 por medio del puerto USB
(Bootloader) es:
A continuación se muestra una imagen real de la tarjeta del programador del PIC18F4550 que se desarrolló.
16
1. Conecta la tarjeta del programador del PIC18F4550, en el puerto USB de tu PC o laptop, para que la
tarjeta entre en modo AUTO PROGRAMADOR (Bootloader), oprime el botón RESET y el botón BOOT
simultáneamente, después de un segundo suelte el botón RESET (manteniendo el botón BOOT
presionado) y después de un segundo suelte el botón BOOT, en ese momento la tarjeta será detectada
por tu computadora, la primera vez te pedirá la ruta de driver para instalarlos.
Elija la opción Instalar desde una lista o ubicación especifica (avanzado) y presione Siguiente >.
Observe que instalo el Puertos (COM y LPT) CCS CDC Bootloader (COM8) lo que indica que nuestra
tarjeta fue aceptada correctamente. Podemos configurar el COM que deseemos o nos quedamos con el
que asigno.
2. Ahora abrir la aplicación Siow.exe, este programa nos muestra la siguiente ventana:
Donde elegiremos el puerto COM que se instaló para nuestra tarjeta y todos los demás parámetro que
se alcanzan a ver.
Presionamos OK para aceptar la configuración. Lo que nos presenta la ventana del programa Serial
Input/Output Monitor y nos indica que la tarjeta está conectada y podemos desconectarla desde el
19
botón Disconnect, que se encuentra en la parte inferior derecha de esta ventana o simplemente
presionando el botón Reset de la tarjeta Programadora del PIC.
Con la ventana del software Serial Input/Output Monitor abierta (Indicándonos Disconnect), y la tarjeta
del programador conectada en el puerto USB de su computadora. Ya podemos descargar el archivo
*.hex al microcontrolador y programarlo.
Presionando el botón File Dowload Sofware, se abrirá una ventana de búsqueda que nos permite
ubicar el archivo *.hex, que cargaremos en el microcontrolador.
20
Una vez seleccionado el archivo *.hex, que se desea descargar, presione el botón Abrir y el software
automáticamente comienza a descargar el código en el microcontrolador.Al terminar el proceso de
Borrando-Programando-Verificando, automáticamente el microcontrolador comienza a correr el
programa y el software queda deshabilitado (Connect). Debes repetir esta secuencia cada vez que
quieras descargar un programa al microcontrolador.
Nota: Recuerde para entrar en modo Bootloader, Conectar la tarjeta del programador del PIC18F4550, en el
puerto USB de tu PC o laptop, para que la tarjeta entre en modo AUTO PROGRAMADOR (Bootloader), oprime
el botón RESET y el botón BOOT simultáneamente, después suelte el botón RESET (manteniendo el botón
BOOT presionado) y después de un segundo suelte el botón BOOT, en ese momento será detectada la tarjeta
programadora por la computadora.
Es importante saber, que puerto COM nos asignó la PC, para direccionar el mismo en el software de descarga
(Soiw.exe).
Efectivamente es de mucha utilidad poder "Programar tu PIC si necesidad de un Programador Universal". Esto
que digo es relativo, ya que al menos una vez debes programar como siempre un código residente en memoria
flash, que se encargara de tomar los datos y re-programar su propia memoria Flash.
El término Bootloader hace referencia a un pequeño programa que se realiza para determinado
microcontrolador, ya sea PIC, Freescale, Atmel, etc. Este código lo que hace es tomar los datos que se le
enviaran por puerto serial UART, USB, Ethernet, el que sea que pueda enviar una cadena de Bytes al
microcontrolador. El microcontrolador va tomando esos datos que le van llegando y los va "Programando" en su
propia memoria Flash.
Que obtenemos con eso, que puedes actualizar en cualquier momento el código del PIC. Utilidades como estas
son de gran utilidad para desarrollar un producto o un proyecto y el usuario final no necesita un programador de
PIC para poder actualizarlo. Un fiel y gran ejemplo de esto es lo que implementan los programadores
("Quemadores") profesionales de PICs.
El programador te llega a casa totalmente funcional, pero en cualquier momento aparece una nueva referencia
de microcontrolador y si quieres actualizar el programador, solo te bajas de internet la actualización, conectas el
micro al USB y listo, le envías la actualización al programador. Listo él se autoprogramó la FLASH y ya tiene la
nueva referencia del micro para trabajar con este.
Es necesario tener un programador para usarlo solo una vez y así grabarle el programa residente en
memoria FLASH. Ya después de esto se usa el Bootloader para grabar la memoria flash del
microcontrolador.
El programa residente ocupa memoria flash y por ende tienes un poco menos de memoria para la
aplicación real del microcontrolador. No es mucha la que quita, pero hay que tener eso claro.
Por ningún motivo se puede sobrescribir las posiciones de memoria FLASH en la que se encentra el
programa residente Bootloader. Si se borra por algún error, pierdes el Bootloader y toca nuevamente
programarlo con un grabador externo.
No todos los micros pueden soportar Bootloader, esto se limita a aquellos que tienen la capacidad de
autograbar su memoria FLASH. Por ejemplo el 16F84 no lo permite, por eso sacaron la versión PIC16F628A
que si lo deja y además tiene mejores prestaciones. Ya con lo anterior en teoría, PODEMOS GRABAR EL PIC
SIN NECESIDAD DE UN PROGRAMADOR. Bueno, en este trabajo en el capítulo 14, trataremos el tema de
cómo implementar un Bootloader en un PIC18F4550, usando el puerto USB 2.0 Full-Speed que es su máximo
atractivo.
21
Con el compilador CCS en MPLAB para los microcontroladores PIC´s los tipos de datos básicos son:
Todos los tipos de datos excepto los flotantes (float), short y char, por defecto son signados, sin embargo puede
ser procedido por unsigned o signed. Ejemplo:
El compilador CCS en MPLAB proporciona una gran cantidad funciones integradas para acceder y utilizar los
periféricos del microcontrolador PIC. Esto hace que sea muy fácil para los usuarios configurar y controlar los
periféricos sin entrar en detalles en profundidad de los registros asociados con la funcionalidad.
bit_clear( )
Función: Simplemente pone en 0 el bit especificado (0-7, 0-15 o 0-31) en la variable dada. La
variable var puede ser de 8, 16 y 32 bits y el bit puede ser 0-7, 0-15 o 0-31.
Ejemplo: int x;
x=5; // x = 0b0101
bit_clear(x, 2); // Se pone en cero el bit 2 de x = 1
22
bit_set( )
Función: Simplemente pone en 1 el bit especificado (0-7, 0-15 o 0-31) en la variable dada. La
variable var puede ser de 8, 16 y 32 bits y el bit puede ser 0-7, 0-15 y 0-31.
Ejemplo: int x;
x=5; // x = 0b0101
bit_set(x, 3); // Se pone en 1 el bit 3 de x = 13
bit_test( )
Función: Checa el bit especificado (0-7, 0-15 o 0-31) en la variable dada y regresa el valor que
tiene (0 o 1). La variable var puede ser de 8, 16 y 32 bits y el bit puede ser 0-7, 0-15 y
0-31.
make8( )
Función: Extrae el byte en el offset (puede ser 0,1,2 o 3) de var (es un int16 o int32) y lo
deposita en la variable int8 (variable de 8 bits).
make16( )
Función: Hace un número de 16 bits (int16) de dos números de 8 bits (varhigh (MSB) y varlow
(LSB). Si los parámetros son de 16 o 32 bits se utiliza el byte menos significativo LSB.
make32( )
y = 0x12;
z = 0x4321;
x = make32(y,z); // x es 0x00124321;
x = make32(y,y,z); // x es 0x12124321;
_mul( )
Función: Lleva a cabo una multiplicación optimizada. Las variables var1 y var2 pueden ser de 8
o 16 bits. Esta función evita la sobrecarga y cambia los parámetros por tipos más
grandes. Entrega en prod un entero de 16 bits si los parámetros son de 8 bits y 32 bits
si los parámetros son de 16 bits.
rotate_left( )
Función: Rota hacia la izquierda un bit que se encuentra dentro de una variable o arreglo. El
puntero addrees apunta al inicio del arreglo y la variable bytes es el contador del
número de rotaciones.
rotate_right( )
Función: Rota hacia la derecha un bit que se encuentra dentro de una variable o arreglo. El
puntero addrees apunta al inicio del arreglo y la variable bytes es el contador del
número de rotaciones.
shift_left( )
Función: Desplaza hacia la izquierda un bit dentro de una variable o arreglo. El puntero addrees
apunta al inicio del arreglo, la variable bytes es el contador del número de
desplazamientos y value es el 0 o 1 que se desplazara. Retorna el bit desplazado.
Función: Desplaza hacia la derecha un bit dentro de una variable o arreglo. El puntero addrees
apunta al inicio del arreglo, la variable bytes es el contador del número de
desplazamientos y value es el 0 o 1 que se desplazara. Retorna el bit desplazado.
swap( )
Sintaxis: swap(Ivalue);
Función: Intercambia el nible (4 bits) superior con el inferior de una variable Ivalue de 8 bits.
Muchas veces en algunos programas es necesario realizar un retardo o esperar cierto tiempo antes de
continuar con la ejecución de nuestro programa, para esto el compilador CCS cuenta con funciones de retardo.
delay_cycles( )
Sintaxis: delay_cycles(count);
Función: Crea un retardo del número (count es una constante de 0 - 255) de ciclos de trabajo
especificados. Un ciclo de trabajo son 4 periodos del reloj oscilador.
delay_ms( )
Sintaxis: delay_ms(time);
Función: Crea un retardo del orden de los mili segundos (time es una variable o constante de 0 -
65535) del tiempo especificados. Esta función no utiliza ningún timer interno solo
instrucciones y requiere de la directiva #USE DELAY.
delay_us( )
Sintaxis: delay_us(time);
Función: Crea un retardo del orden de los micro segundos (time es una variable o constante de 0
- 65535) del tiempo especificados. Esta función no utiliza ningún timer interno solo
instrucciones y requiere de la directiva #USE DELAY.
unsigned int periodo=100, duty = 25; // periodo y duty son 8 bit sin signo.
Existe un grupo muy extenso de funciones de C para la manipulación de caracteres y cadenas de caracteres en
el compilador CCS en MPLAB.
atof( )
x = atof(str); // x = 123.45
x = atoi(str); // x = 123.
Función: Checa un carácter (datac es un carácter de 8 bits) para ver si cumple con los siguientes
criterios y retorna false (0) o true (1) (value es int1). Esta función requiere de la librería
#include <ctype.h>.
If(isalpha(id[ 0 ])) {
valid_id = true;
for(i=1;i<strlend(id);i++)
valid_id = valid_id && isalnum(id[ i ]);
} else
valid_id = false;
itoa( )
Función: Convierte un entero signado de 32,48 o 64 bits (i32value) en una cadena de caracteres
(string es un puntero que indica el inicio del vector) que representa la cantidad
pudiendo elegir la base del numero con base variable (i8base es una variable 8 bits que
sirve para colocar la base del número 2, 8, 10 y 16), si no logra la conversión regresa un
0. Esta función requiere de la librería #include <stdlib.h>.
sprintf( )
Función: Esta función es similar a la función printf() de C, imprime una cadenas de caracteres
(cstring cadenas de caracteres constante y values son variables separadas por comas
que se pueden mandar a imprimir) en un arreglo (string arreglo tipo char).
tolower( )
toupper( )
Función: Estas funciones cambian una letra del alfabeto (cvalue es un carácter), si el carácter no
es letra no lo cambia. Retorna un carácter de 8 bits en result.
El microcontrolador PIC18F4550 cuenta con 5 puertos digitales de entradas / salidas que incluyen un total de 35
líneas de E/S:
Los puertos digitales cuentan con tres registros de 8 bits para su configuración y control:
TRIS registro que sirve para configurar la dirección de los puertos, como entrada (1) o salida (0).
PORT registro contiene los estados actuales de los pines de los puertos digitales.
LAT registro sirve para leer, modificar y escribir los estado de los pines de los puertos digitales.
En lenguaje C existen un grupo de funciones que nos sirven para manipular los puertos digitales.
get_tris_x( )
set_tris_x( )
Sintaxis: set_tris_a(value);
30
set_tris_b(value);
set_tris_c(value);
set_tris_d(value);
set_tris_e(value);
.
.
set_tris_k(value);
Función: Permite que los registros de dirección de los puertos digitales E/S (TRI-State) sean
modificados. Value es un numero de 8 bit y cada bit representa un pin digital de E/S.
Uno 1 indica que el pin es una entrada digital y un 0 indica que es una salida digital.
input_x( )
value = input_d( );
value = input_e( );
.
.
value = input_k( );
Función: Devuelve un byte entero (8 bit) con los estados de los pin de un puerto digital.
input( )
Función: Devuelve el estado (false (0) o true (1)) del pin digital seleccionado. La palabra pin es
una constante y puede ser PIN_A0 – PIN_A6, PIN_B0 – PIN_B7, PIN_C0 – PIN_C7,
PIN_D0 – PIN_D7 o PIN_E0 – PIN_E3.
Input_state( )
Función: Lee el nivel del pin seleccionado y entrega el estado lógico que tenga sea 0 o 1. La
palabra pin es una constante y puede ser PIN_A0 – PIN_A6, PIN_B0 – PIN_B7,
PIN_C0 – PIN_C7, PIN_D0 – PIN_D7 o PIN_E0 – PIN_E3.
input_change_x( )
Función: Lee los nivel del puerto seleccionado y los compara con los resultados de la última vez
que la función input_charge_x( ) fue llamada. Entrega un 1 si el valor cambio y un 0 si el
valor es el mismo.
output_x( )
Sintaxis: output_a(value);
output_b(value);
output_c(value);
output_d(value);
output_e(value);
.
.
output_k(value);
Función: Entrega por el puerto seleccionado el valor de value (byte entero de 8 bit).
Ejemplo: output_c(0xf5); // Saca por el puerto C la cifra 0xf5, lo que pone en 1 los pines
// PIN_C4 –PIN_ C7 y en 0 los pines PIN_C0 – PIN_C3.
output_bit( )
Función: Entrega por el bit del puerto seleccionado el valor de value (bit 0 o 1). La palabra pin es
una constante y puede ser PIN_A0 – PIN_A6, PIN_B0 – PIN_B7, PIN_C0 – PIN_C7,
PIN_D0 – PIN_D7 o PIN_E0 – PIN_E3.
output_high( )
Sintaxis: output_high(pin);
Función: Pone el pin especificado en nivel alto. La palabra pin es una constante y puede ser
PIN_A0 – PIN_A6, PIN_B0 – PIN_B7, PIN_C0 – PIN_C7, PIN_D0 – PIN_D7 o PIN_E0
– PIN_E3.
32
output_low( )
Sintaxis: output_low(pin);
Función: Pone el pin especificado en nivel bajo. La palabra pin es una constante y puede ser
PIN_A0 – PIN_A6, PIN_B0 – PIN_B7, PIN_C0 – PIN_C7, PIN_D0 – PIN_D7 o PIN_E0
– PIN_E3.
output_float( )
Sintaxis: output_float(pin);
Función: Ajusta el pin especificado en modo entrada. Esto permitirá que el pin flote en nivel alto
para representar nivel alto en colector abierto en la conexión. La palabra pin es una
constante y puede ser PIN_A0 – PIN_A6, PIN_B0 – PIN_B7, PIN_C0 – PIN_C7,
PIN_D0 – PIN_D7 o PIN_E0 – PIN_E3.
output_toggle( )
Sintaxis: output_toggle(pin);
Función: Alterna entre 1 y 0 el estado del pin especificado. La palabra pin es una constante y
puede ser PIN_A0 – PIN_A6, PIN_B0 – PIN_B7, PIN_C0 – PIN_C7, PIN_D0 – PIN_D7
o PIN_E0 – PIN_E3.
output_drive( )
Sintaxis: output_drive(pin);
Función: Pone el pin especificado en modo salida. La palabra pin es una constante y puede ser
PIN_A0 – PIN_A6, PIN_B0 – PIN_B7, PIN_C0 – PIN_C7, PIN_D0 – PIN_D7 o PIN_E0
– PIN_E3.
port_x_pullups( )
Sintaxis: port_x_pullups(value);
Función: Ajusta las pullups de entrada. Con TRUE se activan y con FALSE se desactivan.
En esta práctica realizaremos el código para lograr que un diodo Led conectado con lógica inversa en el pin 0
del puerto E (RE0, aunque podemos elegir cualquier otro pin de los puertos A, B, C, D o E del PIC18F4550), se
encienda y se apaguen a una frecuencia de 2 Hz (o cualquier otra frecuencia que queramos), utilizando solo los
puertos digitales y la función delay (que puede ser delay_ms (retardo en milisegundo), delay_us (retardo en
micro segundos) o delay_cycle (retardo en ciclos en maquina)) que ya proporciona el compilador. El circuito que
se armara es el siguiente, en esta imagen solo se muestra la conexión del diodo Led:
/* Programa principal */
void main(void){
output_drive(LED); // Se configura el pin del LED como salida.
output_high(LED); // Se manda a apagar el LED.
En esta práctica realizaremos el código para lograr una secuencia de 8 luces con velocidad variable. Se
conectaran 8 diodos leds en el puerto D con lógica inversa (podemos utilizar también el puerto B). La velocidad
se aumenta presionando el botón UP conectado en el pin RE0 y se decrementa presionando el botón DOWN
conectado en el pin RE1, los botones entregan cero cuando son presionados y uno cuando no son presionados.
El circuito que se armara es el siguiente, solo muestra los diodos leds y los botones:
#include "18f4550.h"
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
void main(void){
for( ; ; ) {
En esta práctica realizaremos el código para lograr 5 secuencia de 8 luces con velocidad variable. Se
conectaran 8 diodos leds en el puerto D con lógica inversa (podemos utilizar también el puerto B). La velocidad
iniciara lenta y se aumenta presionando el botón UP conectado en el pin RE0 (después de alcanzar la velocidad
máxima al presionar el botón de nuevo regresa a la velocidad más lenta) y al presionar el botón SHIFT
conectado en el pin RE1, se cambiara de secuencia eligiendo entre 4 distintas. Los botones entregan cero
cuando son presionados y uno cuando no son presionados. El circuito que se armara es el siguiente, solo
muestra los diodos leds y los botones:
#include "18f4550.h"
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
void main(void){
for( ; ; ) {
if(selec == 1) {
if(cont >= 16) // La tabla de secu1 contiene 16 códigos.
cont = 0; // solo contar de 0 a 15.
}
else if(selec == 2) {
if(cont >= 32) // La tabla de secu2 contiene 32 códigos.
cont = 0; // solo contar de 0 a 31.
}
}
39
4.5 PRACTICA E4.4. Desplegué de Números en Display de 7 Segmentos con Cuatro Dígitos.
En esta práctica desarrollaremos el código para imprimir cifras numéricas de cuatro dígitos en displays de 7
segmentos de ánodo común multiplexados, utilizando el puerto D para conectar los segmentos y los pines RE0
(unidades), RE1 (decenas), RE2 (centenas) y RB0 (millares) para la activación de los ánodos comunes,
además contara con dos botones para incrementar (UP en RC0) o decrementar (DOWN en RC1) el número
desplegado. El circuito que se armara es el siguiente:
#include "18f4550.h"
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
// Declaración de funciones
void des_7int(unsigned long numi);
void des_7float(float numf, char ndf);
if(contf != 0.0)
contf -= 0.1;
contf += 0.1;
if(contf == 1000.0)
contf = 999.9;
}
// Función para desplegar cifras numéricas enteras positivas de 4 dígitos en displays de 7 segmentos.
// Función para desplegar cifras numéricas flotantes positivas de 3 dígitos enteros y 1 decimal en display de 7
segmentos.
if(ndf == 1)
numaux = numf * 10; // Se recuperan las milésimas.
else if(ndf == 2)
numaux = numf * 100; // Se recuperan las milésimas y centésimas.
output_high(UNID);
}
numfa = (numaux / 10) % 10; // Se extraen las unidades de numaux y se depositan en numfa.
output_d(segm[numfa]); // Se despliegan las unidades y el punto decimal.
if(ndf == 1)
output_low(PUNT);
output_low(DECE);
delay_us(200);
output_high(DECE);
El sistema de interrupciones disponen de dos niveles de prioridad, nivel alto y nivel bajo, todas las
interrupciones pueden ser programadas con cualquiera de los dos prioridades, salvo la interrupción externa 0
que siempre tiene prioridad alta.
Todas las interrupciones disponen de tres bits de configuración (excepto la interrupción externa 0):
Bit de habilitación de interrupción, que permite habilitar a nivel individual la interrupción.
Bandera de interrupción, que se pone en 1 cuando se produce la condición de interrupción
independientemente si la interrupción está habilitada o no (esta bandera debe ponerse en 0 por
software cuando se procesa la interrupción).
Bit de prioridad de interrupción, que establece si la interrupción es de alta o baja prioridad.
En este momento nos enfocaremos en las interrupciones externas (INT0, INT1 y INT2) y la interrupción por
cambio del puerto B (KBI0, KBI1, KBI2 y KBI3).
Las interrupciones externas se activan cuando detectan flancos ascendentes o descendentes en terminales
físicas del microcontrolador y tiene tres vectores de interrupción independientes para cada una de estas.
Cuando se configura el pin correspondiente como interrupción externa, la función de puerto digital de entrada
del pin sigue estando presente. Los pines que contiene estas interrupciones son:
La interrupción por cambio del puerto B, se activa cuando acurre un cambio en alguno de los estados de los
pines RB4 al RB7, cuando son estos pines son configurados como entradas, y tiene un solo vector de
interrupción. Cuando la interrupción es atendida es necesario limpiar la bandera de esta. Los pines asociados a
estas interrupciones son:
44
En lenguaje C existen un grupo de funciones que nos sirven para manipular las interrupciones externas e
interrupciones en general.
disable_interrups( )
Sintaxis: disable_interrups(level);
Función: Desactiva interrupciones dependiendo del comando level (es una constante definida).
Las constantes que se pueden usar para las interrupciones externas son GLOBAL,
INT_EXT, INT_EXT1, INT_EXT2 y INT_RB, recuerde que esta función es para
interrupciones en general.
enable_interrups( )
Sintaxis: disable_interrups(level);
Función: Permite las interrupciones dependiendo del comando level (es una constante definida).
Las constantes que se pueden usar para las interrupciones externas son GLOBAL,
INT_EXT, INT_EXT1, INT_EXT2 y INT_RB, recuerde que esta función es para
interrupciones en general.
ext_int_edge( )
clear_inerrup( )
Sintaxis: clear_interrup(level);
Función: Esta directiva genera el código para utilizar la selección de alta y baja prioridad en las
interrupciones.se usa para fijar la prioridad de las interrupciones..
Ejemplo: #device high_ints = true // Uso de prioridad alta y baja para interrupciones.
#priority
Función: Esta directiva se usa para fijar la prioridad de las interrupciones, se enlistan las
interrupciones separadas por comas (ints es una lista de una o más interrupciones
separadas por comas), en donde la primera de la lista es la de más alta prioridad. Si
dos interrupciones acurren al mismo tiempo se atiende primero la de más alta prioridad.
Si una interrupción es activada no se puede interrumpir.
#int_ext, #int_ext1
#int_ext2, #int_rb
Función: Estas directivas se utilizan para marcar el inicio del vector de las interrupciones INT0
(#int_ext), INT1 (#int_ext1), INT2 (#int_ext2) y KBI (#int_rb), existen otras más para las
interrupciones del ADC, TIMERS, COMUNICACIONES, etc. .
}
}
}
46
5.2 PRACTICA E5.1. Múltiples Secuencias de Luces con Velocidad Variable utilizando Interrupciones
Externas.
En esta práctica realizaremos el código para lograr 5 secuencia de 8 luces con velocidad variable. Se
conectaran 8 diodos leds en el puerto D con lógica inversa (podemos utilizar también el puerto B).
La velocidad iniciara lenta y se aumenta presionando el botón UP conectado en el pin INT0 (después de
alcanzar la velocidad máxima al presionar el botón de nuevo regresa a la velocidad más lenta) y al presionar el
botón SHIFT conectado en el pin INT1, se cambiara de secuencia eligiendo entre 5 distintas. Los botones
entregan cero cuando son presionados y uno cuando no son presionados.
Retomaremos el código que hicimos en la práctica 3 (Múltiples Secuencias de Luces con Velocidad Variable) y
habilitaremos las interrupciones externas INT0 e INT1, además reubicaremos parte del código en los vectores
de las interrupciones. El circuito que se armara es el siguiente, solo muestra los diodos leds y los botones:
#include "18f4550.h"
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
void main(void){
for( ; ; ) {
switch(selec){
case 1: if(cont >= 16) // La tabla de secu1 contiene 16 códigos.
cont = 0; // solo contar de 0 a 15.
output_d(~secu1[cont]); // Se saca el código de la secu1.
cont++; // Se incrementa el cont.
break;
case 2: if(cont >= 32) // La tabla de secu2 contiene 32 códigos.
cont = 0; // solo contar de 0 a 31.
48
#int_ext
void ext_isr0() {
switch(vel){ // Se aumenta la velocidad disminuyendo vel.
case 500: vel = 350; break;
case 350: vel = 250; break;
case 250: vel = 150; break;
case 150: vel = 80; break;
case 80: vel = 20; break;
case 20: vel = 500; break; // Con velocidad máxima regresa y la mínima.
}
if(!input(UP)){
delay_us(500);
}
}
#int_ext1
void ext_isr1() {
if(selec == 5) // Se checa si selec == 5 para hacerlo 0, y si no
selec = 1; // se incrementa en 1.
else
selec++;
if(!input(SHIFT)){
delay_us(500);
}
}
49
set_adc_channel( )
Sintaxis: set_adc_channel(chan);
Función: Especifica el canal del convertidor AD que se usara para la próxima llamada de la
instrucción read_adc( ). Se recomienda esperar unos 10 us para que se complete la
operación de selección. La constante chan es un número entre el 0 y el 12 para elegir
el canal del convertidor.
setup_adc_ports( )
Sintaxis: setup_adc_ports(value);
setup_adc_ports(port, [reference]);
Función: Selecciona los pines que serán analógicos, digitales y los voltajes VREF que se usaran.
La constante value o port puede ser NO_ANALOGS, ALL_ANALOG, AN0_TO_AN11,
AN0_TO_AN10, AN0_TO_AN9, AN0_TO_AN8, AN0_TO_AN7, AN0_TO_AN6,
AN0_TO_AN5, AN0_TO_AN4, AN0_TO_AN3, AN0_TO_AN2, AN0_TO_AN1, AN0,
otras constantes opcionales son VSS_VDD, VREF_VREF, VREF_VDD, VSS_VREF.
setup_adc( )
Sintaxis: setup_adc(mode);
read_adc( )
Función: Lee el valor digital del convertidor (entrega 16 bits u 8 bits). Los modos de lectura mode
son ADC_START_AND_READ (Inicia la conversión y lee el valor digital, esta es el
modo por defaul), ADC_START_ONLY (solo empieza la conversión y regresa),
ADC_READ_ONLY (Lee la última conversión).
adc_done( )
Función: Sondea si el convertidor ADC tiene lista la conversión o está ocupado, retorna un TRUE
(1) si la conversión esta lista y un FALSE (0) siel convertidor está ocupado, esta
instrucción se utiliza para manejar el A/D sin interrupción.
#int_ad
Función: Esta directiva se utiliza para marcar el inicio del vector de la interrupción del A/D, que se
ejecuta cuando tiene una conversión completa.
}
}
#device
Función: Esta directiva se utiliza para elegir si queremos que el convertidor trabaje a 8 o 16 bits.
}
}
6.2 PRACTICA E6.1. Vúmetro digital con el convertidor A/D sin interrupción.
En esta práctica se desarrollara el código para realizar la lectura del voltaje en la terminal AN0 del convertidor
A/D y se mandaran iluminar unos leds conectados en el puerto D de acuerdo al voltaje censado, iluminando se
todos cuando el voltaje 5V y ninguno cuando el voltaje sea 0V. El circuito que se armara es el siguiente:
/*CONFIGURACIÓN PIC*/
#include "18f4550.h"
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF { }
void main(void){
}
}
54
6.3 PRACTICA E6.2. Vúmetro digital con el convertidor A/D con interrupción.
En esta práctica se desarrollara el código para realizar la lectura del voltaje en la terminal AN0 del convertidor
A/D utilizando la interrupción de conversión completa y se mandaran iluminar unos leds conectados en el puerto
D de acuerdo al voltaje censado, iluminando todos cuando el voltaje 5V y ninguno cuando el voltaje sea 0V. El
circuito que se armara es el siguiente:
/*CONFIGURACIÓN PIC*/
#include "18f4550.h"
#device adc=10
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
void main(void){
6.4 PRACTICA E6.3. Medición de Multivariable con el convertidor A/D con interrupción.
En esta práctica se desarrollara el código para realizar la lectura del voltaje de las terminales AN0, AN1, AN2 y
AN3 del convertidor A/D utilizando la interrupción de conversión completa y se mandaran iluminar unos leds
conectados en el puerto D de acuerdo al voltaje censado, se mostrara el valor del puerto analógico
seleccionado en el puerto D y se utilizaran los pines RE0 (S0) y RE1 (S1) para seleccionar el canal. El circuito
que se armara es el siguiente:
S1 S0 CANAL
0 0 AN0
0 1 AN1
1 0 AN2
1 1 AN3
/*CONFIGURACIÓN PIC*/
#include "18f4550.h"
#device adc=10
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
void main(void){
set_tris_d(0x00); // Se configura en Puerto D como salidas Digitales.
output_d(0xff); // Se apagan los leds conectados en el Puerto D.
output_float(S0); // Se configuran el RE0 y RE1 como entradas.
output_float(S1);
while(true){
if(adcan == 10){ // Se verifica si todos los canales tienen dato nuevo.
sel = input_e() & 0x03; // Se deposita en sel el código del puerto que desplegara.
switch(sel){
case 0: adcd = adc0; break; // Si S1 y S0 son 0 0.
case 1: adcd = adc1; break; // Si S1 y S0 son 0 1.
case 2: adcd = adc2; break; // Si S1 y S0 son 1 0.
case 3: adcd = adc3; break; // Si S1 y S0 son 1 1.
}
Este display de cristal liquido cuenta con dos renglones con capacidad de presentar hasta 16 caracteres que
pueden ser alfanumérico o signos especiales como el punto, la coma, los signos de admiración ¡!, ¿?, ( ), [ ],{ },
”, #, $, &, =, +, -, *, /, entre otros.
La librería que se utiliza para controlar el display LCD con interfaz de 4 bits es #include<lcd.c>, antes de incluirla
se deben definir los pines que serán utilizados para el bus de datos (DB4, DB5, DB6 y DB7) y para el bus de
control (RS, E/W y E) de la LCD, los pines DB0 al DB3 de la LCD no se conectan con interfaz de 4 bits.
La librería lcd.c, contiene funciones que podemos utilizar para manejar la LCD.
lcd_init ( )
Sintaxis: lcd_init( );
Función: Inicializa la LCD, limpia la pantalla y posiciona el cursor en la posición (1,1), esta
función debe llamarse antes de cualquier otra función.
60
lcd_putc ( )
lcd_gotoxy ( )
Función: Posiciona el cursor en la posición (x, y) del LCD, x es una variable int8 que representa
las columnas puede ser 1 a 15 e y es una variable tipo int8 que representa las
columnas pude ser 1 (línea superior) o 2 (línea inferior). La posición superior izquierda
es (1, 1).
lcd_getc ( )
printf ( )
Función: Esta función nos permite desplegar una cadena de caracteres (cstring) y variables
(values) separadas por comas, utilizando como medio de despliegue la LCD utilizando
la función lcd_putc( ) (fname,si no colocas una función por default despliega por el
puerto serie RS232).
En esta práctica se desarrollara el código para desplegar en una LCD de 16 x 2, algunos mensajes y variables
utilizando la librería lcd.c. Usaremos la LCD con interfaz de 4 bits conectando el bus de datos en RD4 al RD7,
RS con RB7, R/W con RB6 y E con RB5. El circuito que se armara es el siguiente:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/* Programa principal*/
63
void main() {
char k = 56;
int b=56;
float g = 2.345;
}
}
Los teclados matriciales son ensamblados en forma de matriz como se muestra en la figura:
Este diagrama muestra un teclado como una matriz de 4x4 16 teclas configuradas en cuatro columnas y
cuatro renglones, cuando no se oprime ninguna tecla no hay conexión entre renglones y columnas. Cuando se
oprime una tecla se hace una conexión entre la columna y el renglón de la tecla.
64
Muchos teclados comerciales ya traen incluido un decodificador, que escanea el teclado y si una tecla es
presionada regresa un número que identifica la tecla. Otra alternativa es adquirir por separado un chip
decodificador (74LS922) y conectarlo al teclado como muestra la figura.
El decodificador mostrado tiene 8 entradas, las 4 entradas X son conectadas a las columnas del teclado y las 4
entradas Y son conectadas a los renglones. No se muestran los capacitores que gobiernan la rapidez a la que
se escanea el teclado.
Cuando se oprime una tecla el código de 4 bits de la tecla aparecerá en las 4 lineas de salida Q, y la línea de
dato disponible DA se pone en bajo. Si conectamos una interrupción externa en línea DA, cuando se oprima el
alguna tecla, la rutina de servicio de la interrupción, leerá los 4 bits de salida Q y procesara el dato. La terminal
de habilitación de salida OE, permite mantener en el tercer estado las salidas hasta que decidamos leerlas.
El chip del decodificador se encarga de eliminar el rebote de las teclas, lo que libera al programador de esta
responsabilidad, esta es una ventaja al utilizar un chip decodificador.
Siempre que se utiliza un teclado matricial con un microcontrolador, es necesario conectar también un display
LCD y el circuito típico es:
65
Se observa que se necesitan prácticamente dos puertos de 8 pines (un total de 15 terminales digitales) para
conectar el teclado de 4x4 y la LCD con interfaz de 4 bits, estas son muchas terminales consumidas.
Existe un método para eliminar el número de terminales digitales empleadas y consiste en multiplexar el bus de
datos de cuatro bits que utiliza la LCD, con los renglones del teclado matricial. El circuito quedaría como se
muestra a continuación y nos ahorramos 4 terminales:
En este circuito se consume todo un puerto de 8 bits y además 3 terminales de cualquier otro puerto sumando
un total de 11 terminales.
Con el microcontrolador PIC18F4550, se recomienda utilizar el puerto D (como si fuera el puerto P1) para
conectar el teclado matricial y el bus de datos de la LCD, ya que este no contiene funciones multiplexadas en
estas terminales y para los pines E, RS y R/W de la LCD utilizar un grupo de tres terminales que puedan ser
configuradas como puertos de salidas.
El compilador CCS cuenta con una librería (kbd.c) que sirve para decodificar la tecla presionada en un teclado
matricial de 3x4 conectado en el puerto D o en el puerto B según se elija. Para lograr conectar de manera
correcta el teclado matricial de 3x4 con la LCD utilizando el puerto D o el puerto B, tendremos que abrir el
archivo kbd.c y configurar los pines que utilizaremos como renglones y columnas para el teclado.
Al abrir la librería kbd.c, que se encuentra en la carpeta Archivos de Programa\ PICC\Drivers\, necesitamos
ubicar en el código la sección que se muestra en la imagen y realizar los cambios que se muestran y guardar
los cambios.
Realizados los cambios anteriores ya podemos conectar el teclado matricial de 3x4 en las terminales marcadas
del puerto B o el puerto D, según elijamos cual será la interfaz del teclado matricial de 3x4.
Para utilizar esta librería es necesario primero definir una directiva para elegir en cual puerto se conectara el
teclado, como se muestra en seguida:
O simplemente:
#include <kbd.c> // Cuando solamente Incluimos la librería del teclado, por default
// el puerto D es elegido para conectar el teclado.
La librería kbd.c, contiene funciones que podemos utilizar para manejar el teclado matricial de 3x4.
kbd_init ( ) kbd_getc ( )
kbd_init ( )
Sintaxis: kbd_init ( );
Función: Esta función inicializa el teclado y debe ser llamada antes que cualquier otra función.
67
kbd_getc ( )
char k;
void main() {
char k; // Se declara la variable donde se guarda el código de la tecla.
kbd_init();
}
68
7.4 PRACTICA E7.2. Visualización de los datos de un teclado matricial 3x4 en la LCD.
En esta práctica se desarrollara el código para desplegar en la LCD los códigos generados cuando se presiona
una tecla del teclado matricial conectado en el puerto D, utilizando la librería kbd.c del compilador CCS. Los
pines utilizados se pueden ver en el diagrama del circuito que se muestra a continuación:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/*Programa principal*/
void main( ) {
char k; // k es la variable donde se guardara el código de la tecla presionada.
int x; // variable para desplegar el valor entero.
lcd_init(); // Se inicializa la LCD.
kbd_init(); // Se inicializa el Teclado de 3x4.
printf(lcd_putc,"\fListo...\n"); // Se despliega el mensaje "Listo...".
if(k != 0){
if(k == '*') // Si se presiona la tecla '*' se limpia la LCD.
lcd_putc('\f'); // y se posiciona el cursor en la posición (1,1).
else
lcd_putc(k); //Imprime carácter de la tecla presionada.
Si lo que se quiere es conectar un teclado de 4x4, la librería kbd.c, puede ser modificada y guardada con otro
nombre por ejemplo kbd1.c, las modificaciones que se deben realizar son:
70
71
Una vez realizado todos los cambios mostrados anteriormente, guarde el archivo como kbd1.c, en la misma
dirección de kbd.c y listo, ya tenemos una librería para un teclado matricial de 4x4.
72
7.6 PRACTICA E7.3. Visualización de los datos de un teclado matricial 4x4 en la LCD.
En esta práctica se desarrollara el código para desplegar en la LCD los códigos generados cuando se presiona
una tecla del teclado matricial de 4x4 conectado en el puerto D, utilizando la librería kbd1.c que creamos a
partir de la librería kbd.c del compilador CCS. Los pines utilizados se pueden ver en el diagrama del circuito
que se muestra a continuación:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/*Programa principal*/
void main( ) {
if(k != 0){
if(k == '*') // Si se presiona la tecla '*' se limpia la LCD.
printf(lcd_putc, "\fListo...\n"); / Se despliega el mensaje "Listo...".
else
lcd_putc(k); //Imprime carácter de la tecla presionada.
delay_ms(350);
}
}
}
74
Cuando se realiza una operación de escritura la circuitería interna del PIC, se encarga de borrar previamente la
posición en la que se desea escribir. La duración de un ciclo completo de borrado/escritura de un byte en la
memoria EEPROM es de unos 4 ms.
En lenguaje C existen un grupo de funciones que nos sirven para leer y escribir datos en la EEPROM.
read_eeprom ( )
char volumen;
write_eeprom ( )
#rom
En esta práctica se desarrolla el código para realizar un control de acceso mediante el teclado de 3x4, la LCD y
la memoria para datos EEPROM. El usuario presionara la palabra clave, si esta correcta se activara por dos
segundos un relevador conectado en el pin RA0. El circuito que se armara es el siguiente:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#use delay(clock=48000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/*Programa principal*/
void main( ) {
char k;
int i,npass;
char data[8], clave[8]; //Matrices para guardar clave y datos
npass = read_eeprom(8);
for (i=0; i <= npass-1; i++) { //Pasa datos de eeprom a la matriz clave
clave[ i ] = read_eeprom(i);
}
inicio:
printf(lcd_putc,"\fPress Password: \n");
i = 0; // Posición de la matriz
goto inicio;
cambio:
printf(lcd_putc,"\f Password Old \n");
i = 0; //posición de la matriz
i = 0; //posición de la matriz
npass = i;
lcd_gotoxy(1,1);
printf(lcd_putc," Clave Aceptada "); // Compara los datos con la clave
delay_ms(2000);
lcd_gotoxy(1,1);
printf(lcd_putc," Copiando Datos "); // Compara los datos con la clave
78
for (i=0; i <= npass-1; i++) { //Pasa datos de eeprom a la matriz clave
clave[i] = read_eeprom(i);
}
delay_ms(1500);
goto inicio;
}
else {
printf(lcd_putc,"\f Password Old "); //Clave erronea
printf(lcd_putc,"\n Incorrecto ");
delay_ms(2000);
}
}
79
9. Los Timers.
El microcontrolador PIC18F4550 cuenta con 4 módulos timers:
Timer 0
Timer 1
Timer 2
Timer 3
9.1 El Timer 0.
En el diagrama interno del timer 0, muestra cómo podemos utilizar el pin T0CKI (RA4) para conectar la fuente
externa de reloj o elegir reloj interno con ¼ de la frecuencia del oscilador interno. Cuenta con 1 bits para elegir si
se elige prescala o no y 3 bit para programar la prescala para dividir la fuente del reloj entre 2, 4, 8, 16, 32, 64,
128 y 256. Si decides trabajar el timer 0 con 8 bits el registro TMR0L es el registro contador o si decides trabajar
el timer 0 con 16 bits los registros TMR0H y TMR0L contiene la cuenta. Los registros TMR0H y TMR0L se
pueden leer y escribir mediante software. Todos los timer son contadores ascendentes y cuando llegan a su
número máximo del conteo se activa la bandera de sobre flujo o se activa la interrupción si esta se habilito.
El compilador CCS cuenta con un grupo de funciones para configurar y controlar la operación del timer 0, estas
funciones son:
Recuerde que el timer 0, cuenta con la interrupción por sobre flujo y las funciones para configurar las
interrupciones se vieron en el Capitulo 6. Las Interrupciones Externas.
setup_timer_0 ( )
Función: Configura el timer 0, la constante mode pueden ser uno o dos definiciones contenidas
en el archivo *.h del dispositivo, algunas de estas definiciones son RTCC_INTERNAL,
RTCC_EXT_L_TO_H o RTCC_EXT_H_TO_L, RTCC_DIV_2, RTCC_DIV_2,
RTCC_DIV_8, RTCC_DIV_16, RTCC_DIV_32, RTCC_DIV_64 y RTCC_DIV_256,
RTCC_OFF y RTCC_8_BIT, entre otras.
set_timer0 ( )
Función: Ajusta el registro contador del timer 0, la variable value puede ser de 8 o 16 bits. RTCC
y TIMER0 son lo mismo, cuando el contador del timer 0 llega a su cuenta máxima da
vuelta e inicia de nuevo con 0 y continua contando.
Ejemplo: // Con el reloj interno en 20 MHz, sin prescala, y configurado el timer 0 a 8 bits
// la cuenta es cada 200 ns (Tosc/4).
get_timer0 ( )
Función: Retorna el valor del registro contador del timer0, la variable value puede ser de 8 o 16
bits. RTCC y TIMER0 son lo mismo.
while (get_timer0( ) < 200); // Mientras el registro contador sea menos a 200.
#int_timer0
Función: Esta directiva marca el inicio del vector de la interrupción por sobre flujo del timer 0. el
sobre flujo acurre cuando el registro contador del timer0, llega a su valor máximo de
conteo y regresa a cero.
void main ( ){
81
for ( ; ; ){
}
}
#int_timer0
void TIMER0_OVF(void){ // Se genera una señal de 10 kHz.
set_timer0 ( 6 );
output_toggle(PIN_E0);
clear_interrupt(INT_TIMER0); // Se limpia la bandera del timer0.
}
82
En esta práctica se desarrollara el código para generar una señal de 5 Hz, utilizando el timer0 con reloj interno y
la terminal RE0. El circuito que se armara es el siguiente:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#use delay(clock=20000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/* Programa principal*/
void main(void)
{
output_drive(PIN_E0); // RE0 como salida
enable_interrupts(GLOBAL); // Habilita interrupciones globales.
enable_interrupts(INT_TIMER0); // Habilita la interrupción del Timer0.
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_16); //Tosc/4 , prescala 16.
set_timer0(34286); // Sobre flujo cada 0.1 s.
while(true){
}
}
9.2 El Timer 1.
En la figura, se apreciar que el timer 1,puede elegir entre oscilador externo (con los pines RC0/T1OSO y
RC1/T1OSI que sirven para colocar un cristal XTAL) e interno trabajando a Fosc/4. Tiene un selector de
prescala para dividir la frecuencia de la fuente del reloj entre 1, 2, 4 y 8 lo que nos permite hacer más lenta la
cuenta del timer. Este timer utiliza los registros TMR1H y TMR1L para realizar el conteo, si el registro contador
llega al número máximo, se reinicia en 0 y continua contando. Cada vez que el registro contador llega al número
máximo y se reinicia, se activa una bandera de sobre flujo lo que puede activa la interrupción de sobre flujo del
timer 1, si es que esta se habilito.
El compilador CCS cuenta con un grupo de funciones para configurar y controlar la operación del timer 1, estas
funciones son:
setup_timer_1 ( ) set_timer1 ( )
get_timer1 ( ) #int_timer1
Recuerde que el timer 1, cuenta con la interrupción por sobre flujo y las funciones para configurar las
interrupciones se vieron en el Capitulo 6. Las Interrupciones Externas.
84
setup_timer_1 ( )
Función: Configura el timer 1, la constante mode pueden ser uno o dos definiciones contenidas
en el archivo *.h del dispositivo, algunas de estas definiciones son T1_DISABLED,
T1_INTERNAL, T1_EXTERNAL y T1_EXTERNAL_SYNC, T1_CLK_OUT,
T1_DIV_BY_1, T1_DIV_BY_2, T1_DIV_BY_4 y T1_DIV_BY_8.
set_timer1 ( )
Función: Ajusta el registro contador del timer 1, la variable value es de 16 bits. Cuando el
contador del timer 1 llega a su cuenta máxima,da vuelta e inicia de nuevo con 0 y
continúa contando.
Ejemplo: // Con el reloj interno en 20 MHz, sin prescala, y configurado el timer 0 a 8 bits
// la cuenta es cada 200 ns (Tosc/4).
get_timer1 ( )
Función: Retorna el valor del registro contador del timer 1, la variable value debe ser de 8 o 16
bits.
while (get_timer1( ) < 500); // Mientras el registro contador sea menos a 500.
#int_timer1
Función: Esta directiva marca el inicio del vector de la interrupción por sobre flujo del timer 1. El
sobre flujo acurre cuando el registro contador del timer 1, llega a su valor máximo de
conteo y regresa a cero.
void main ( ){
for ( ; ; ){
}
}
#int_timer1
void TIMER1_OVF(void){ // Se genera una señal de 6.25 Hz.
set_timer1 ( 15536 );
output_toggle(PIN_E0);
}
86
En esta práctica desarrollaremos el código para crear un reloj muy preciso utilizando el timer 1 con reloj externo
tipo cristal XTAL, tendremos tres botones para ajustar los minutos, las horas y am o pm, desplegaremos la hora
en la LCD. El circuito que se armara es el siguiente:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#use delay(clock=20000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/* Programa principal*/
void main(void) {
enable_interrupts(GLOBAL); // Habilita interrupciones globales.
enable_interrupts(INT_TIMER1); // Habilita la interrupción del Timer1.
setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1); //XTAL externo , prescala/1.
set_timer1(32768); // Sobre flujo cada 1 s.
while(true){
if(dato == 1){ // Se imprime la hora cada segundo.
printf(lcd_putc, "\f Son las: \n");
if(apm == 0)
ampm = "AM";
else
ampm = "PM";
printf(lcd_putc, " %2u:%2u:%2u %2s ", horas, minutos, segundos, ampm);
}
}
}
}
else
minutos++;
}
else
segundos++;
apm = ~apm;
if(~input(PIN_B0))
delay_us(500);
}
9.3 El Timer 2.
Temporizador de 8 bits.
Prescala programables con 3 opciones.
Postscala programable con 16 opciones.
Cuenta con el registro de comparación.
Interrupción cuando el registro de comparación es igual al del timer.
Se puede utilizar junto con los módulos CCP y ECCP.
Se puede utilizar como señal de reloj del modulo MSSP en modo SPI.
En la figura se puede observar, que este timer tiene como reloj interno Fosc/4 y se puede dividir la frecuencia
del reloj entre 1, 4 y 16. Cuando el registro del timer TMR2 es igual al registro de comparación PR2 se activa la
bandera y se genera la interrupción del timer 2 si está habilitada.
89
El compilador CCS cuenta con un grupo de funciones para configurar y controlar la operación del timer 1, estas
funciones son:
setup_timer_2 ( ) set_timer2( )
get_timer2( ) #int_timer2
Recuerde que el timer 2, cuenta con la interrupción que ocurre cuando TMR2 = PR2 y las funciones para
configurar las interrupciones se vieron en el Capitulo 6. Las Interrupciones Externas.
setup_timer_2 ( )
Función: Configura el timer 2, la constante mode pueden ser uno o dos definiciones contenidas
en el archivo *.h del dispositivo, algunas de estas definiciones son T2_DISABLED,
T2_DIV_BY_1, T2_DIV_BY_4 y T2_DIV_BY_16. La variable period es de 8 bits y
puede ser desde 1 hasta 255 y determina el periodo,y la variable postscale es un
número entre 1 y 16 que determina el número de sobre flujos antes de ejecutar la
interrupción.
Ejemplo: setup_timer_2 ( T2_DIV_BY_4, 0xc0, 2); // Con 20MHz, los incrementos del timer
// son cada 800 ns, con sobre flujos
// cada 153.6 us y la interrupción
// ocurrirá a los 307.2 us.
set_timer2 ( )
Función: Ajusta el registro contador del timer 2, la variable value es de 8 bits. Cuando el contador
del timer 2, llega a su cuenta máxima, da vuelta e inicia de nuevo con 0 y continúa
contando.
Ejemplo: // Con el reloj interno en 20 MHz, sin prescala, y configurado el timer 0 a 8 bits
// la cuenta es cada 200 ns (Tosc/4).
get_timer2 ( )
Función: Retorna el valor del registro contador del timer 2, la variable value debe ser de 8 bits.
while (get_timer1( ) < 500); // Mientras el registro contador sea menos a 150.
#int_timer2
Función: Esta directiva marca el inicio del vector de la interrupción del timer 2. La interrupción del
timer 2, ocurre cuando el registro contador TMR2 = al registro del periodo PR2, el
número de veces que la postscala marca. Cuando el registro contador del timer 2, llega
a su valor máximo de conteo y regresa a cero y continua contando.
void main ( ){
for ( ; ; ){
}
}
#int_timer2
void TIMER2_EQ(void){ // Se genera una señal de 25 Hz.
output_toggle(PIN_E0);
}
91
En esta práctica se desarrollara el código para generar una señal de 1kHz utilizando timer
el 2, el pin que
utilizaremos como salida es el RE0. El circuito que se armara es el siguiente:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#use delay(clock=8000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/* Programa principal*/
void main(void)
{
output_drive(PIN_E0); // RE0 como salida
enable_interrupts(GLOBAL); // Habilita interrupciones globales.
enable_interrupts(INT_TIMER2); // Habilita la interrupción del Timer2.
setup_timer_2(T2_DIV_BY_1, 250, 4); // (Tosc*4)*4 , periodo = 500 us.
set_timer2(0);
while(true){
}
}
9.4 El Timer 3.
En la figura, se apreciar que el timer 3, puede elegir entre oscilador externo (con los pines RC0/T1OSO y
RC1/T1OSI que sirven para colocar un cristal XTAL) e interno trabajando a Fosc/4. Tiene un selector de
prescala para dividir la frecuencia de la fuente del reloj entre 1, 2, 4 y 8 lo que nos permite hacer más lenta la
cuenta del timer. Este timer utiliza los registros TMR3H y TMR3L para realizar el conteo, si el registro contador
llega al número máximo, se reinicia en 0 y continua contando. Cada vez que el registro contador llega al número
máximo y se reinicia, se activa una bandera de sobre flujo lo que puede activa la interrupción de sobre flujo del
timer 3, si es que esta se habilito.
El compilador CCS cuenta con un grupo de funciones para configurar y controlar la operación del timer 2, estas
funciones son:
setup_timer_3 ( ) set_timer3 ( )
get_timer3 ( ) #int_timer3
93
Recuerde que el timer 3, cuenta con la interrupción por sobre flujo y las funciones para configurar las
interrupciones se vieron en el Capitulo 6. Las Interrupciones Externas.
setup_timer_3 ( )
Función: Configura el timer 3, la constante mode pueden ser uno o dos definiciones contenidas
en el archivo *.h del dispositivo, algunas de estas definiciones son T3_DISABLED,
T3_INTERNAL, T3_EXTERNAL y T3_EXTERNAL_SYNC, T3_CLK_OUT,
T3_DIV_BY_1, T3_DIV_BY_2, T3_DIV_BY_4 y T3_DIV_BY_8.
set_timer3 ( )
Función: Ajusta el registro contador del timer 3, la variable value es de 16 bits. Cuando el
contador del timer 3 llega a su cuenta máxima, da vuelta e inicia de nuevo con 0 y
continúa contando.
Ejemplo: // Con el reloj interno en 20 MHz, sin prescala, y configurado el timer 0 a 8 bits
// la cuenta es cada 200 ns (Tosc/4).
get_timer3 ( )
Función: Retorna el valor del registro contador del timer 3, la variable value debe ser de 8 o 16
bits.
while (get_timer3( ) < 500); // Mientras el registro contador sea menos a 500.
#int_timer3
Función: Esta directiva marca el inicio del vector de la interrupción por sobre flujo del timer 3. El
sobre flujo acurre cuando el registro contador del timer 3, llega a su valor máximo de
conteo y regresa a cero.
void main ( ){
94
for ( ; ; ){
}
}
#int_timer3
void TIMER1_OVF(void){ // Se genera una señal de 6.25 Hz.
set_timer3 ( 15536 );
output_toggle(PIN_E0);
}
El timer 1 y el timer 3 son totalmente idénticos y comparten las terminales RC0/T1OSO y RC1/T1OSI que nos
sirven para conectar una fuente de reloj externo y como contador de pulsos.
95
Modo captura: se utiliza para medir eventos externos, como la duración de pulsos digitales.
Modo de comparación: se utiliza para generar señales digitales con temporizadores programables.
Modo PWM: se utiliza para generar señales de modulación de ancho de pulso.
El compilador CCS cuenta con un grupo de funciones que nos sirven para configura y controlar los módulos
Comparadores, capturadores y PWM.
setup_ccp1 ( )
setup_ccp2 ( )
Deshabilita el CCP:
CCP_OFF Deshabilita el modulo CCP
T3_CCP1_TO_2 Elije el timer 3 para los módulos CCP1 y CCP2
T3_CCP2 Elije el timer 3 solo para CCP2
CCP_PWM_L_L
CCP_PWM_FULL_BRIDGE
CCP_PWM_FULL_BRIDGE_REV
CCP_PWM_HALF_BRIDGE
CCP_SHUTDOWN_ON_COMP1
CCP_SHUTDOWN_ON_COMP2
CCP_SHUTDOWN_ON_COMP
CCP_SHUTDOWN_ON_INT0
CCP_SHUTDOWN_ON_COMP1_INT0
CCP_SHUTDOWN_ON_COMP2_INT0
CCP_SHUTDOWN_ON_COMP_INT0
CCP_SHUTDOWN_AC_L
CCP_SHUTDOWN_AC_H
CCP_SHUTDOWN_AC_F
CCP_SHUTDOWN_BD_L
CCP_SHUTDOWN_BD_H
CCP_SHUTDOWN_BD_F
CCP_SHUTDOWN_RESTART
CCP_DELAY
set_pwm1_duty ( )
set_pwm2_duty ( )
Función: Escribe un valor de 10 bits en el PWM para ajustar el ciclo de trabajo. Un valor de 8 bits
puede ser usado, si los bits más significativos no son requeridos. El valor value puede
ser una constante o variable de 8 o 16 bits. La ecuación para calcular el ciclo el trabajo
para 10 bits es:
En el modo de captura, el valor del timer 1 o del timer 3, pasa al registro CCP_x (x = 1 o 2) del modulo CCP1 o
CCP2, cuando se produce alguno de los siguientes eventos:
La manera de elegir el canal CCPx que deseamos y el timer asociado a este es la siguiente:
El modulo CCPx en el modo capturador tiene la capacidad de generar interrupción interna, cada vez que se
detecte un evento en las terminales RC1/CCP1 y RC2/CCP2, y las directivas asociadas son #int_ccp1 y
#int_ccp2.
Las instrucciones para configurar las interrupciones, se vieron en la sección 5, Interrupciones Externas.
98
En este programa desarrollaremos el código para realizar la medición del ancho de pulso de una señal
cuadrada, mediante el modulo CCP2 en modo captura. La lectura en microsegundos se desplegara en la LCD.
El circuito que se debe armar es:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#use delay(clock=20000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/* Declaración de variables*/
/* Programa principal*/
void main(void) {
lcd_init();
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); //Configuración TMR1
setup_ccp2(CCP_CAPTURE_RE); //Configuración modo Captura en flanco de subida
flanco = 0; //Control de cambio a 0
while(true) {
if(npul == 1){ //¿Pulso nuevo?
TF = TFB - TFS; //Ancho de pulso.
AP = TF * 1.6; //Ancho de pulso en microsegundos (a 20MHz:1.6us)
printf(lcd_putc,"\nTon = %6.1f us ", AP);
npul = 0; //Pulso ya medido, espera nuevo
}
}
}
#int_ccp2
void ccp2_RFE( ) { //Función interrupción
if(flanco == 0){ //Flanco de subida
TFS = CCP_2; //Carga del valor del registro CCPR1 en flanco subida
setup_ccp2(CCP_CAPTURE_FE); //Configuración modo Captura en flanco de bajada
flanco = 1; //Control de cambio de flanco
}
else { //Flanco de Bajada
TFB = CCP_2; //Carga del valor del registro CCPR1 en flanco bajada
setup_ccp2(CCP_CAPTURE_RE); //Configuración modo Captura en flanco de subida
flanco = 0; //Control de cambio de flanco
El pin de salida RC1/CCP2 o RC2/CCP1 se inicializa a 0. Cada vez que se produce la igualdad entre el
registro CCP_x y el temporizador asociado se complementa el pin de salida y se pone a 1 la bandera
del módulo CCP. (CCP_COMPARE_INT_AND_TOGGLE).
La salida RC1/CCP2 o RC2/CCP1 se inicializa a 0. Cuando se produce la igualdad entre el registro
CCP_x y el temporizador asociado se pone a 1 el pin de salida y se pone a 1 la bandera del modulo
CCP. (CCP_COMPARE_SET_ON_MATCH).
100
El pin de salida RC1/CCP2 o RC2/CCP1 se inicializa a 1. Cada vez que se produce la igualdad entre el
registro CCP_x y el temporizador asociado se pone a 0 el pin de salida y se pone a 1 la bandera del
módulo CCP. (CCP_COMPARE_CLR_ON_MATCH).
El pin de salida RC1/CCP2 o RC2/CCP1 toma el valor del latch correspondiente. Cuando se produce la
igualdad el registro CCP_x y el temporizador asociado se pone a 1 la bandera del modulo CCP.
(CCP_COMPARE_INT).
El pin de salida RC1/CCP2 o RC2/CCP1 toma el valor del latch correspondiente. Cuando se produce la
igualdad el registro CCP_x y el temporizador asociado se inicializa a cero el timer asociado, se inicia
una conversión del A/D y se pone a 1 la bandera del modulo CCP.
(CCP_COMPARE__RESET_TIMER).
101
En esta práctica se desarrollara el código para generar una señal de 2 kHz, mediante el modulo CCP2, con el
los modos Comparación, RC2 inicia en 0, coincidencia puesta en 1 (CCP_COMPARE_SET_ON_MATCH) y el
modo Comparación, RC2 inicia en 1, coincidencia puesta en 0 (CCP_COMPARE_CLR_ON_MATCH). El
circuito que se debe armar es:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#use delay(clock=20000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/* Declaración de Variables*/
short cambio = 0;
/* Programa principal*/
void main(void) {
disable_interrupts(global);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); // Configuración TMR1
setup_ccp2(CCP_COMPARE_SET_ON_MATCH); // Configuración inicial modulo CCP
CCP_2 = 1250; // Inicialización del registro CCPR2 para un Duty del 50%
while(true) {
}
102
#int_ccp2
void ccp2_int(){ // Función de interrupción
if(++cambio == 1){
setup_ccp2(CCP_COMPARE_CLR_ON_MATCH); // Modo Comparación, cambio a 0
}
else{
setup_ccp2(CCP_COMPARE_SET_ON_MATCH); // Modo Comparación, cambio a 1
cambio = 0;
}
El modo PWM permite generar una señal PWM con ciclo de trabajo y frecuencia programable. El
funcionamiento de este modulo es el siguiente:
( 2 + 1) ∗ 4 ∗ ( 2)
( ) =
Establecer el ciclo de trabajo de la señal PWM mediante la función ccp_pwmx_duty( valor ) donde x = 1 o 2. El
ciclo de trabajo de la señal será:
( )∗( 2)
=
∗
104
10.4.1. Practica E10.3.Generación de señal PWM proporcional a la señal medida del ADC.
En esta práctica desarrollaremos el código para generar una señal PWM con periodo de 720 us, mediante el
modulo CCP1, el ciclo de trabajo será proporcional a la señal mediada por el convertidor A/D con el canal 0. el
circuito que se armara es el siguiente:
/*CONFIGURACION PIC*/
#include "18f4550.h"
#device adc=10
#use delay(clock=20000000)
#build(reset=0x02000,interrupt=0x02008)
#org 0x0000,0x1FFF {}
/* Declaración de Variables*/
long valor = 0;
/* Programa principal*/
void main(void) {
disable_interrupts(global);
setup_adc_ports(AN0); // Habilitación RA0 analógico
setup_adc(ADC_CLOCK_INTERNAL); // Reloj interno RC
105
set_adc_channel(0); // Canal 0
while(true) {
}
}
#int_ad
void adc_int(){ // Función interrupción AD