1

Diseño y simulación de
sistemas
microcontrolados en
lenguaje C










Programación con MikroC PRO
Simulación en Proteus ISIS


Juan Ricardo Clavijo Mendoza
2



3



















Dedicado a mí esposa:
Sandra, mi hijo Miguel, mis padres Jorge y Bibiana

4

5

Diseño y simulación de
sistemas
microcontrolados en
lenguaje C



Programación con el compilador MikroC PRO y
simulación en Proteus ISIS
















Juan Ricardo Clavijo Mendoza


6






Diseño y simulación de sistemas microcontrolados en lenguaje C.






Se prohíbe la reproducción total o parcial de este libro
sea cual fuere el medio, electrónico o mecánico,
sin el consentimiento escrito del autor.





Autor
Ing. Juan Ricardo Clavijo Mendoza




Corrección de estilo
Ing. Sandra Milena Bernate Bautista
Dr. Duilio Cruz Becerra


Hecho e impreso en Colombia






ISBN 978-958-44-8619-6
Primera edición mayo de 2011
7
Contenido
1 El Compilador MikroC y los PICMicro ................................................................................. 17
1.1 El compilador MikroC PRO ............................................................................................ 18
2 Fundamentos de lenguaje C ................................................................................................... 20
2.1 Declaración de variables en lenguaje C ........................................................................... 20
2.2 Formatos numéricos usados en el lenguaje C .................................................................. 23
2.3 Operadores en lenguaje C ................................................................................................ 23
2.4 Funciones en lenguaje C .................................................................................................. 27
2.5 Creación de un programa en lenguaje C .......................................................................... 29
2.6 Condicionales e iteraciones en lenguaje C ...................................................................... 30
2.6.1 Creación de condiciones lógicas en lenguaje C ....................................................... 30
2.6.2 La sentencia condicional if e if else ......................................................................... 31
2.6.3 La sentencia switch case........................................................................................... 32
2.6.4 El ciclo iterativo while y do while............................................................................ 32
2.6.5 El ciclo iterativo for .................................................................................................. 33
2.6.6 Uso anidado de ciclos iterativos ............................................................................... 33
3 El simulador ISIS de Proteus ................................................................................................. 34
3.1 Características básicas del ISIS para simular .................................................................. 35
4 Creación del primer programa en MikroC PRO .................................................................... 38
5 Visualización de datos ............................................................................................................ 44
5.1 Display de 7 segmentos ................................................................................................... 44
5.1.1 Control de display de 7 segmentos ........................................................................... 45
5.1.2 Control de displays 7 segmentos dinámicos............................................................. 47
5.2 Display LCD de caracteres .............................................................................................. 50
5.2.1 Funciones para imprimir caracteres ......................................................................... 54
5.2.2 Funciones para imprimir cadenas de texto ............................................................... 55
5.2.3 Impresión de valores numéricos ............................................................................... 57
5.2.4 Creación de caracteres propios ................................................................................. 59
5.3 Display LCD gráficos ...................................................................................................... 64
6 Teclados y sistemas de entrada de datos ................................................................................ 74
6.1 Uso de pulsadores ............................................................................................................ 74
6.2 Uso de Dip-Switch ........................................................................................................... 76
6.3 Uso de teclados matriciales ............................................................................................. 78
6.4 Uso de teclados PS2 o Din .............................................................................................. 84
7 Comunicaciones seriales ........................................................................................................ 88
7.1 Modulo serial I²C ............................................................................................................. 88
7.2 Módulo USART .............................................................................................................. 91
7.3 Módulo USB .................................................................................................................... 95
8 Conversión AD y DA ........................................................................................................... 109
8.1 Conversión AD, o ADC ................................................................................................. 109
8.2 Conversión DA o DAC .................................................................................................. 110
8.2.1 Conversión DA con PWM ..................................................................................... 110
8.2.2 Conversión DA con arreglo R-2R .......................................................................... 114
9 Memorias EEPROM y FLASH ............................................................................................ 117
9.1 Memoria EEPROM ....................................................................................................... 117
9.2 Memoria FLASH ........................................................................................................... 118
10 Módulos Timer ..................................................................................................................... 121
8
11 Interrupciones ....................................................................................................................... 124
12 Sensores ................................................................................................................................ 128
12.1 Sensor de temperatura LM35 ..................................................................................... 128
12.2 Sensores de presión .................................................................................................... 131
12.3 Sensores de distancia ................................................................................................. 134
12.4 Sensores LDR ............................................................................................................ 138
12.5 Sensores de humedad y temperatura .......................................................................... 141
13 Comunicación con dispositivos ............................................................................................ 147
13.1 Módulos GPS ............................................................................................................. 147
13.2 Módulos inalámbricos unidireccionales..................................................................... 161
13.3 Módulos inalámbricos bidireccionales....................................................................... 167
13.4 Comunicación RS 485 ............................................................................................... 172
13.5 Módulos inalámbricos infrarrojos .............................................................................. 174
13.6 Comunicación con memorias SD ............................................................................... 177
13.7 Relojes en tiempo real ................................................................................................ 183
14 Tratamiento digital de señales .............................................................................................. 190
14.1 Muestreo .................................................................................................................... 190
14.2 Funciones de transferencia ......................................................................................... 191
14.3 Convolución ............................................................................................................... 192
14.4 Filtros FIR .................................................................................................................. 192
14.4.1 Filtro Pasa bajas ..................................................................................................... 193
14.4.2 Filtros Pasa Altas .................................................................................................... 194
14.4.3 Filtro Pasa Banda ................................................................................................... 194
14.4.4 Filtro Rechaza Banda ............................................................................................. 195
14.4.5 Filtro Multi Band .................................................................................................... 196
14.4.6 Ventanas fijas ......................................................................................................... 196
14.4.7 Ventanas Rectangulares ......................................................................................... 197
14.4.8 Ventanas Hamming ................................................................................................ 198
14.4.9 Ventanas Hanning .................................................................................................. 199
14.4.10 Ventanas Blackman ............................................................................................ 201
14.5 Ejemplos de diseño para filtros FIR ........................................................................... 202
14.5.1 Ejemplo de diseño para filtro pasa bajas ................................................................ 204
14.5.2 Ejemplo de diseño para filtro pasa altas ................................................................. 207
14.5.3 Ejemplo de diseño para filtro pasa banda ............................................................... 208
14.5.4 Ejemplo de diseño para filtro rechaza banda ......................................................... 209
14.5.5 Ejemplo de diseño para filtro multi banda ............................................................. 210
14.6 Filtros IIR ................................................................................................................... 212
14.6.1 Transformación bilineal ......................................................................................... 213
14.6.2 Filtro Pasa Bajas IIR .............................................................................................. 213
14.6.3 Ejemplo de diseño para filtro pasa bajas ................................................................ 215
14.6.4 Filtro Pasa Altas IIR ............................................................................................... 216
14.6.5 Ejemplo de diseño para filtro pasa altas ................................................................. 217
14.6.6 Filtro Pasa Banda IIR ............................................................................................. 219
14.6.7 Ejemplo de diseño para filtro pasa banda ............................................................... 220
14.6.8 Filtro Rechaza Banda IIR ....................................................................................... 221
14.6.9 Ejemplo de diseño filtro rechaza banda ................................................................. 222
14.7 Osciladores digitales .................................................................................................. 224
14.7.1 Ejemplo de diseño oscilador doble cuadrado ......................................................... 225
14.7.2 Ejemplo de diseño para oscilador de acople en cuadratura .................................... 226
9
14.8 Transformada discreta de Fourier DFT ...................................................................... 228
14.8.1 Transformada rápida de Fourier FFT ..................................................................... 232
14.9 Control digital PID ..................................................................................................... 238
15 Transmisión de datos ............................................................................................................ 243
15.1 Control de flujo .......................................................................................................... 244
15.2 Transparencia de datos ............................................................................................... 247
15.3 Control de errores CRC ............................................................................................. 252
16 Actuadores y potencia .......................................................................................................... 262
16.1 Actuadores DC ........................................................................................................... 262
16.1.1 Relevadores ............................................................................................................ 263
16.1.2 Motores DC ............................................................................................................ 264
16.1.3 Puente H ................................................................................................................. 265
16.1.4 Motores Paso .......................................................................................................... 267
16.1.5 Servomotores .......................................................................................................... 271
16.2 Actuadores AC ........................................................................................................... 273
17 Anexos .................................................................................................................................. 279
17.1 Tabla ASCII ............................................................................................................... 279
Bibliografía ................................................................................................................................... 281
Índice ............................................................................................................................................ 283



10
11
El autor
Juan Ricardo Clavijo Mendoza, Ingeniero Electrónico egresado de la universidad Manuela
Beltrán de Bogotá D.C, Colombia. Autor de capitulo de libro de: “Ciencia, tecnología e
innovación”, Tomo 1, publicado por la Universidad Católica de Colombia. Docente de la
Universidad Católica de Colombia, en el programa de Tecnología en Electrónica y
Telecomunicaciones, asesor temático en proyectos de investigación de la Universidad Católica de
Colombia, y la Escuela de Telemática de la Policía Nacional de Colombia, desarrollador de
proyectos tecnológicos en diversas empresas de Bogotá Colombia.
12
13
Prologo
El desarrollo de la tecnología hace parte vital de la evolución y el progreso de una nación, está
razón motiva la creación de documentos que formen a los desarrolladores e investigadores en las
diversas áreas de la ciencia. Este libro busca hacer un aporte en este propósito como un curso de
fácil entendimiento en el diseño de sistemas microcontrolados, para este fin este texto se enfoca
en la simulación de sistemas digitales en el paquete de software PROTEUS, y la programación de
microcontroladores PIC con la herramienta de programación en lenguaje C MikroC PRO. Este
libro es ideal para estudiantes de ingeniería, tecnología, o carreras técnicas en electrónica,
sistemas, mecatrónica, biomédica, y todas aquellas afines con la electrónica. De igual manera es
un aporte importante al desarrollo de proyectos de investigación y tecnología.
14
15
Introducción
Este libro trata temáticas relacionadas con el diseño de sistemas con microcontroladores, de la
familia microchip 12F, 16F, y 18F. Usando como herramienta de diseño, el compilador MikroC
PRO, y el paquete de software para simulación Proteus. Para entender este libro de forma clara el
estudiante o desarrollador debe tener bases sólidas de electrónica digital como circuitos
combinacionales y secuenciales. También es de utilidad tener conocimientos básicos de
programación en lenguaje C y algoritmos. De la misma forma, se debe conocer las teorías de
circuitos eléctricos, y circuitos electrónicos análogos, teorías básicas de algebra, y algebra lineal
así como la manipulación de números complejos en todas sus expresiones. Este libro se enfoca en
la demostración de diseños simples que posteriormente pueden ser integrados para realizar
diseños de mayor complejidad en función de la necesidad del desarrollador.
16
17
1 El Compilador MikroC y los
PICMicro
Un microcontrolador es un dispositivo electrónico encapsulado en un circuito de alto nivel de
integración. Los microcontroladores se pueden adquirir comercialmente de diferentes casas
fabricantes como: Freescale, Motorola, Intel, Philips, y Microchip.

Microchip en particular es una empresa fabricante de dispositivos electrónicos, en sus líneas de
producción se encuentran los microcontroladores PICMicro, los cuales se pueden adquirir en
diferentes familias, algunas de ellas son: 12F, 16F, 18F, 24F, 30F, y 33F.

En función de la necesidad del proyecto el desarrollador debe escoger la familia y la referencia
que más se acerque a su necesidad, por ejemplo el microcontrolador 12F675 es un PIC de 8 pines
con módulos integrados básicos como: Timer y ADC. Un microcontrolador como el 16F877
cuenta con 40 pines y módulos como: Timer, ADC, USART, I2C, PWM, entre otros. Fácilmente
se pueden apreciar diferencias que permiten crear aplicaciones diferentes entre estos dos
ejemplos.


Figura 1-1

La información técnica, la masiva comercialización y la increíble información publicada acerca
de los microcontroladores PIC, los hace ideales para aprender y estudiar su funcionamiento, la
empresa Microchip cuenta con el portal WEB www.microchip.com en donde se puede descargar
información y aplicativos de software que facilitan los desarrollos con sus microcontroladores.

Básicamente implementar un desarrollo con un microcontrolador PIC consiste en identificar la
problemática del desarrollo, crear editar y depurar un programa de máquina y programar
eléctricamente el microcontrolador con un programador especifico para los PICMicro. Microchip
suministra programadores especializados en diferentes escalas, tal vez el más popular es el
PICSTART Plus, sin embargo existen otros como el PICkit2, PICkit3. A pesar de que existan
programadores comerciales un desarrollador puede construir o adquirir un programador didáctico
de bajo costo, de estos últimos existe amplia información publicada en Internet.

18


Figura 1-2

Un microcontrolador tiene una arquitectura básica que es similar a la de un computador de
escritorio, cuenta con un bloque de memoria OTP o Flash en la cual se guardan las instrucciones
del programa está sección es similar al disco duro del computador, el PICMicro cuenta con una
memoria RAM, que cumple las mismas funciones de la memoria RAM de un ordenador personal,
el microcontrolador posee puertos de entrada y salida que son similares a los periféricos de
entrada y salida del computador tales como puertos para el ratón, impresora, monitor, teclado y
demás. Estás características hacen que un microcontrolador sea ideal para crear aplicaciones a
pequeña escala que tengan interfaz de usuario, adecuando teclados, botones, lectura de memorias
de almacenamiento masivo, sensores de diversas variables como: temperatura, humedad, presión,
luminosidad, proximidad, entre otros. De igual manera, es posible crear ambientes de
visualización con displays numéricos, alfanuméricos y gráficos. Los puertos seriales como la
USART y USB permiten crear comunicaciones seriales y comunicaciones inalámbricas con otros
dispositivos. En síntesis las posibilidades son infinitas.
1.1 El compilador MikroC PRO
La programación de microcontroladores se basa en un código de máquina que es conocido como
código ensamblador, este código contiene una a una las instrucciones del programa, este código
ensamblador o también conocido como código asembler es minucioso, y tedioso de editar. El
asembler crea códigos de programa extensos y de difícil comprensión. La creación de
compiladores de alto nivel facilitó la edición y creación de programas en todo modo de
programación lógica, por supuesto los microcontroladores no fueron la excepción,
comercialmente existen varios compiladores de diferentes fabricantes y diferentes lenguajes de
alto nivel.

Es posible adquirir compiladores como el PICC, CCS, PIC Basic, entre otros. El estudio de este
libro se centra en el compilador MikroC PRO, que es un compilador en lenguaje C para
microcontroladores PICMicro de la familia 12F, 16F, y 18F.

MikroC PRO es un paquete de software con una amplia variedad de ayudas y herramientas que
facilita la creación de proyectos y aplicativos para los microcontroladores PICMicro.

El estudio de este entorno de desarrollo es posible debido a que el estudiante puede descargar una
versión demo o estudiantil, que tiene las mismas características de la versión completa, la única
limitación es la dimensión del código de máquina que no puede exceder 2K bytes, sin embargo es
una capacidad suficiente al tratarse de un prime aprendizaje. La versión demo se puede descargar
de la pagina: www.mikroe.com.
19
En la siguiente figura se puede apreciar la apariencia visual del entorno de desarrollo.


Figura 1-3

El compilador de alto nivel en lenguaje C utiliza estructuras que facilitan la programación,
optimiza las operaciones matemáticas y los procesos, por medio del uso de funciones
predefinidas y las no predefinidas que el desarrollador puede crear, así como el uso de un
conjunto de variables, de tipo carácter, entero, y punto decimal. El compilador crea
automáticamente el código ensamblador y a su vez un código similar consignado en un archivo
con extensión *.hex, este archivo es el resultado primordial del compilador dado que con este se
programa eléctricamente el microcontrolador o con el mismo se puede realizar una simulación
computacional.
20
2 Fundamentos de lenguaje C
El lenguaje C data del año 1972; fue creado por los laboratorios Bell como resultado de la
necesidad de reescribir los sistemas operativos UNIX con el fin de optimizar el conocido código
ensamblador. De igual manera el lenguaje C fue la evolución de lenguajes previos llamados B, y
BCPL. El nuevo lenguaje C, rápidamente tomó fuerza por su funcionalidad y facilidad en la
implementación en diversos sistemas computacionales que requerían códigos de máquina.

La forma del lenguaje C, se fundamenta en un complejo estructural que requiere un amplio
estudio de las mismas, sin embargo para la programación de los microcontroladores el estudiante
requiere una porción fundamental que le permita iniciar y crear los primeros proyectos en
MikroC PRO, para este fin el actual capítulo se centra en conocer las nociones necesarias para
iniciar el estudio de los microcontroladores con este libro.
2.1 Declaración de variables en lenguaje C
Las variables básicas en este compilador específico son:
- bit
- char
- short
- int
- long
- float
- double

Las variables bit permiten almacenar un valor lógico es decir verdadero o falso, 0 ó 1.

Las variables char se utilizan para almacenar caracteres codificados con el código ASCII, son
útiles para guardar letras o textos.

Una variable short almacena un número entero de 8 bits corto puede valer de: -127 a 127.

Las variables tipo int guardan números enteros de 16 bits, está variable permite guardar números
de: -32767 a 32767.

La variable tipo long almacena números enteros largos de 32 bits, su rango puede ser de:
-2147483647 a 2147483647.

Las variables tipo float y double permiten guardar números con punto decimal.

Las anteriores variables pueden declararse incluyendo el signo positivo y negativo, o se pueden
declarar por medio de la opción sin signo con la directriz unsigned.

21
En la siguiente tabla se pueden apreciar las características de las variables.

Tipo de variable Tamaño en Bytes Valores que soporta
bit 1 0 ó 1
char 1 -127 a 127
short 1 -127 a 127
int 2 - 32767 a 32767
long 4 - 2147483647 a 2147483647
float 4 -1.5x10^45 a 3.4x10^38
double 4 -1.5x10^45 a 3.4x10^38
unsigned char 1 0 a 255
unsigned short 1 0 a 255
unsigned int 2 0 a 65535
unsigned long 4 0 a 4294967295

Tabla 2-1

La declaración de variables se realiza indicando el tipo de variable seguido de un nombre que el
desarrollador asigna arbitrariamente a la variable. En el punto de la declaración de una variable es
posible dar un valor inicial a cada una de las variables sin embargo este último no es
estrictamente necesario. Por último la declaración debe culminar con el carácter punto y coma (;).

En los siguientes ejemplos se puede apreciar como se hacen las declaraciones:

bit VARIABLE1_BIT; //Declaración de una variable tipo bit.
char CARACTER; //Declaración de una variable tipo char.
char CARACTER2='J'; //Declaración de una variable tipo char inicializada con el
//valor ASCII del carácter J.
int ENTERO=1234; //Declaración de una variable tipo entera inicializada con
//el valor 1234.
float DECIMAL=-12.45; //Declaración de una variable con punto decimal
//inicializada con el valor -12,45.
double DECIMAL2=56.68; //Declaración de una variable con punto decimal
//inicializada con el valor 56,68.
long ENTERO2=-954261; //Demacración de una variable de tipo entero largo
//inicializada con el valor -954261.

Los siguientes ejemplos muestras como declarar variables sin signo:

unsigned char CARACTER; //Declaración de una variable tipo char sin signo.
unsigned int ENTERO; //Declaración de una variable tipo entera sin signo.
unsigned long ENTERO2; //Demacración de una variable de tipo entero largo sin signo.

Las variables también pueden ser declaradas en un formato que asocia varias variables a un
mismo nombre, este formato se conoce como una cadena de variables, o un vector e incluso
puede ser una matriz de variables, en conclusión este tipo de declaraciones pueden ser de una o
más dimensiones.

22
El siguiente ejemplo muestra un vector de caracteres, o también conocido como una cadena de
caracteres:

char Texto[20]; //Cadena de caracteres con 20 posiciones de memoria.

De igual manera las cadenas de caracteres o de variables pueden ser declaradas con un valor
inicial, este tipo de declaración se puede ver en el siguiente ejemplo:

char Texto[20] = “Nuevo Texto”; //Declaración de una cadena de caracteres
//inicializada con el texto: Nuevo Texto.
int Enteros[5]={5,4,3,2,1}; //Declaración de una cadena de enteros con
//valores iniciales.
float Decimales[3]={0.8,1.5,5.8}; //Declaración de una cadena de números con
//punto decimal inicializadas.

La declaración de las variables debe respetar algunas reglas básicas que evitan errores y
contradicciones en la compilación del código, para este fin tenga presente las siguientes
recomendaciones:

- Las variables no deben tener nombres repetidos.
- Las variables no deben empezar por un número.
- Una variable no puede utilizar caracteres especiales como: / * „ ; { }-\! · % &.

A continuación se muestran ejemplos de declaraciones de variables que no se pueden hacer:

bit 1_VARIABLE-;
char -CARÁCTER!;
int 3ENTERO*;

De igual manera es posible crear estructuras de información como un nuevo tipo de variable
creada por el desarrollador. Estás estructuras se pueden realizar con las variables ya predefinidas
y pueden ser declaraciones de variables o de arreglos de variables. Este tipo de estructuras se
declarar, por medio de la directriz: typedef struct, y la forma de declarar una variable creada por
el desarrollador es la siguiente:

typedef struct
{
char Nombre[10];
int Edad;
}Usuario;

La siguiente es la forma de usar una variable personalizada por el desarrollador:

Usuario U;
U.Edad = 25;
U.Nombre[0]=‟J‟;
U.Nombre[1]=‟u‟;
U.Nombre[2]=‟a‟;
23
U.Nombre[3]=‟n‟;
U.Nombre[4]=0;
2.2 Formatos numéricos usados en el lenguaje C
Los aplicativos en lenguaje C usan números en diferentes bases numéricas, a pesar de que para el
trabajo de bajo nivel del microcontrolador todos sus números están procesados en base 2 es decir
en números binarios. El ser humano no está acostumbrado a pensar y procesar operaciones en
está base numérica. Desde las primeras etapas de la formación académica las escuelas y colegios
enseñan a pensar y procesar todos los cálculos en base 10, es decir con números decimales. Es
por esto que los compiladores en lenguaje C trabajan los números decimales facilitando los
diseños para el desarrollador. Además del sistema decimal el compilador en lenguaje C puede
trabajar otras bases tales como el binario, y hexadecimal haciendo más simple realizar cálculos y
tareas que en decimal serían más complejas. Los sistemas numéricos en base 2, 10, y 16, son los
implementados en este compilador en lenguaje C. La escritura de números decimales, es la forma
de mayor simplicidad ya que se escriben en lenguaje C de la misma manera convencional que se
aprende desde los primeros cursos de matemáticas. Los números binarios se escriben con el
encabezado 0b seguidos del número en binario, un ejemplo de está escritura es: 0b10100001 que
es equivalente al número decimal 161. Los números en base 16 o hexadecimales se denotan con
el encabezado 0x precedidos de un número en hexadecimal, la expresión 0x2A, es un ejemplo de
un número hexadecimal en lenguaje C, y equivale a 42 en decimal. Los números binarios solo
pueden tener dos dígitos que son el 0 y el 1. Los números hexadecimales pueden tener 16 dígitos
que son; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E y F.
2.3 Operadores en lenguaje C
El lenguaje C permite hacer operaciones aritméticas o matemáticas básicas entre números
contenidos en variables o constantes. Las operaciones aritméticas disponibles son las siguientes:

- Suma
- Restá
- Multiplicación
- División
- Modulo

La suma aritmética entre dos o más números, se puede hacer como se ve en el siguiente ejemplo:
int A;
int B;
int C;
C = A+B; //Está expresión guarda la suma de A y B en la variable C.
C = A+B+C; //Está expresión guarda la suma de A, B y C en la variable C.

La restá aritmética entre dos o más números, se puede hacer como se ve en el siguiente ejemplo:
int A;
int B;
int C;
C = A-B; //Está expresión guarda la diferencia entre A y B en la variable C.
C = A-B-C; //Está expresión guarda la diferencia entre A, B y C en la variable C.

24
La operación matemática de multiplicación se puede realizar entre dos o más números, la
operación se relaciona con el carácter asterisco (*), está expresión se puede apreciar con mayor
claridad en los siguientes ejemplos:
int A;
int B;
int C;
C = A*B; //Está expresión guarda la multiplicación entre A y B en la variable C.
C = A*B*C; //Está expresión guarda la multiplicación entre A, B y C en la variable C.

La división aritmética en lenguaje C se especifica por medio de la barra inclinada (/), en el
siguiente ejemplo se puede observar su implementación:
int A;
int B;
int C;
C = A/B; //Está expresión guarda la división A entre B en la variable C.

La operación módulo calcula el módulo de una división aritmética es decir calcula el residuo de
una división, el cálculo del módulo se denota con el carácter de porcentaje, (%), la aplicación de
está operación se puede ver en el siguiente ejemplo:
int A;
int B;
int C;
C = A%B; //Está expresión guarda el residuo de la división de A entre B en la variable C.

Las operaciones aritméticas pueden ser usadas en forma combinada, es decir que se pueden
mezclar varias operaciones en una misma expresión, para ver esto con mayor claridad observe los
siguientes ejemplos:
int A;
int B;
int C;

C = (A+B)/C; //Está expresión es equivalente a C = (A+B)÷C.
C = (A/B)*C; // Está expresión es equivalente a C = (A÷B) X C.

Otros operadores matemáticos abreviados pueden ser utilizados cuando se requiere de conteos o
cambios de una variable de forma constante, por ejemplo es posible incrementar o decrementar
una variable en términos de un número, de igual manera es posible hacer está operación con la
multiplicación y la división. Para entender de forma más clara este concepto observe los
siguientes ejemplos:

int A=100;
int B=10;

A++; //Este operador incrementa en una unidad el valor de A.
A--; //Este operador decrementa en una unidad el valor de A.
A+=4; //Este operador incrementa en 4 el valor de A.
A-=5; //Este operador decrementa en 5 el valor de A.
A/=4; //Este operador divide el valor de A en 4.
A*=3; //Este operador multiplica el valor de A por 3.
25
A+=B; //Este operador incrementa el valor de A en el valor de B unidades.
A*=B; //Este operador multiplica el valor de A por B veces.


Otras operaciones matemáticas de mayor complejidad se pueden realizar en lenguaje C, por
medio de funciones predefinidas en la librería math, está temática será tratada posteriormente
cuando se estudie el uso y declaración de funciones.
Los operadores lógicos permiten realizar acciones u operaciones que respetan la lógica digital
planteada por el matemático inglés George Boole, en el siglo XIX. Boole planteó las operaciones
OR, AND, NOT, XOR, NOR, NAND, XNOR, Estás operaciones son ampliamente estudiadas en
los cursos de sistemas digitales combinacionales y secuenciales.

Las operaciones lógicas se realizan en lenguaje C entre dos variables o constantes, estás se hacen
bit a bit, del mismo peso en una variable o número. Para ver y entender los ejemplos recuerde
primero las tablas de verdad de las operaciones lógicas que se muestran a continuación:


Inversor NOT
Entrada Salida
0 1
1 0


OR inclusiva
Entrada 1 Entrada 2 Salida
0 0 0
0 1 1
1 0 1
1 1 1

OR exclusiva o XOR
Entrada 1 Entrada 2 Salida
0 0 0
0 1 1
1 0 1
1 1 0

AND
Entrada 1 Entrada 2 Salida
0 0 0
0 1 0
1 0 0
1 1 1



NOR inclusiva
Entrada 1 Entrada 2 Salida
0 0 1
0 1 0
1 0 0
1 1 0

NOR exclusiva o XNOR
Entrada 1 Entrada 2 Salida
0 0 1
0 1 0
1 0 0
1 1 1

NAND
Entrada 1 Entrada 2 Salida
0 0 1
0 1 1
1 0 1
1 1 0
26
Las operaciones lógicas en lenguaje C, se realizan con caracteres específicos, que denotan cada
una de ellas, en el siguiente ejemplo pueden verse las aplicaciones de los operadores lógicos:

Operación lógica NOT, negación, se denota con el carácter virgulilla (~);
unsigned short VALOR1=0b01010000; //Variable inicializada en binario con el número 80.
unsigned short RESULTADO;
RESULTADO = ~VALOR1; //La variable RESULTADO guarda el complemento de
//VALOR1, 175.

Operación lógica OR, o inclusiva, está se denota con el carácter barra vertical (|);
unsigned short VALOR1=0b01010000; //Variable inicializada en binario con el número 80.
unsigned short VALOR2=0b01011111; //Variable inicializada en binario con el número 95.
unsigned short RESULTADO;
RESULTADO = VALOR1|VALOR2; //El valor de la operación lógica o, es guardado en
//RESULTADO, 95.

Operación lógica XOR, o exclusiva, está se denota con el carácter acento circunflejo (^);
unsigned short VALOR1=0b01010000; //Variable inicializada en binario con el número 80.
unsigned short VALOR2=0b01011111; //Variable inicializada en binario con el número 95.
unsigned short RESULTADO;
RESULTADO = VALOR1^VALOR2; //El valor de la operación lógica AND
//es guardado en RESULTADO, 15.

Operación lógica AND, y, está se denota con el carácter ampersand (&);
unsigned short VALOR1=0b01010000; //Variable inicializada en binario con el número 80.
unsigned short VALOR2=0b01011111; //Variable inicializada en binario con el número 95.
unsigned short RESULTADO;
RESULTADO = VALOR1&VALOR2; //El valor de la operación lógica o
//exclusiva, es guardado en RESULTADO, 80.

La implementación de las operaciones lógicas NAND, NOR, XNOR, son similares a AND, OR,
y XOR, a agregando el carácter de negación virgulilla, observe los siguieres ejemplos:

unsigned short VALOR1=0b01010000; //Variable inicializada en binario con el número 80.
unsigned short VALOR2=0b01011111; //Variable inicializada en binario con el número 95.
unsigned short RESULTADO;
RESULTADO = ~(VALOR1|VALOR2); //El valor de la operación lógica o negada
// exclusiva, es guardado en RESULTADO.
RESULTADO = ~(VALOR1&VALOR2); //El valor de la operación lógica y negada,
// es guardado en RESULTADO.
RESULTADO = ~(VALOR1^VALOR2); //El valor de la operación lógica o negada
// exclusiva negada, es guardado en RESULTADO.

El desplazamiento de bits dentro de una variable es útil para realizar procesos y tareas que
impliquen la manipulación de datos a nivel de los bits. El corrimiento a la derecha en una
variable o valor constante, en lenguaje C se realiza por medio del doble carácter mayor que, (>>),
de la misma manera el corrimiento a la izquierda se ejecuta con el doble carácter menor que,
(<<). La operación de corrimiento hace perder los valores de los bits que salen, e ingresa ceros en
27
los nuevos bits. En los siguientes ejemplos se puede observar la implementación de estos
operadores:
short Dato=0xFF; //Declaración de variables.
short Resultado;
Resultado = Dato>>4; //Está operación guarda en la variable Resultado el corrimiento de 4 bits
//a la derecha de la variable Dato, valor final de Resultado es 0x0F.
Resultado = Dato<<4; //Está operación guarda en la variable Resultado el corrimiento de 4 bits
//a la izquierda de la variable Dato, valor final de Resultado es 0xF0.

En la siguiente tabla se resumen las operaciones lógicas y aritméticas contempladas
anteriormente:

Descripción de la operación Operador
Suma +
Restá -
División /
Módulo o residuo de la división entera %
Incremento ++
Decremento --
Operación OR |
Operación AND &
Operación XOR ^
Complemento a 1 ~
Corrimiento a la derecha >>
Corrimiento a la izquierda <<

Tabla 2-2
2.4 Funciones en lenguaje C
Una función es una fracción de código que realiza una tarea específica cada vez que está es
invocada por el flujo del programa principal. Las funciones cuentan con dos características
fundamentales, uno o más parámetros de entrada, y un parámetro de salida. Cualquiera de estás
dos características puede ser una variable o un arreglo de ellas, de igual manera estos parámetros
de entrada y salida pueden ser vacíos.

La estructura de las funciones se puede apreciar en los siguientes ejemplos:

void Funcion ( void ) //Función con parámetros de entrada y salida vacíos.
{ // Apertura de la función con corchete.
//Porción de código donde se ejecutan la rutina de la función.
} //Cierre de la función con corchete.

void Funcion ( int A ) //Función con un parámetro de entrada y salida vacía.
{ // Apertura de la función con corchete.
//Porción de código donde se ejecutan la rutina de la función.
} //Cierre de la función con corchete.

28
int Funcion ( void ) //Función con parámetro de entrada vacío y salida entera.
{ // Apertura de la función con corchete.
//Porción de código donde se ejecutan la rutina de la función.
} //Cierre de la función con corchete.

int Funcion ( int A ) //Función con un parámetro de entrada y salida enteras.
{ // Apertura de la función con corchete.
//Porción de código donde se ejecutan la rutina de la función.
} //Cierre de la función con corchete.

Los nombres que se designan a las funciones cumplen con las mismas reglas para nombrar las
variables. El siguiente ejemplo muestra una función que es capaz de calcular el producto de dos
números con punto decimal:

float Producto ( float A, flota B ) //Función para calcular el producto de A y B.
{ // Apertura de la función con corchete.
float RESULTADO;
RESULTADO = A*B; //Producto de A y B.
return RESULTADO; //La función retorna el resultado guardado en RESULTADO.
} //Cierre de la función con corchete.

Una función puede recurrir a otra función para realizar funciones más complejas, para demostrar
está situación se puede ver el siguiente ejemplo, que realiza el cálculo del área de una
circunferencia en función de su radio:


Figura 2-1

El área de la circunferencia se calcula por medio de la ecuación A=πr², donde el valor de π es
aproximadamente 3,1416.

float Valor_PI ( void ) //Función que retorna el valor de π.
{ // Apertura de la función con corchete.
float PI=3.1416;
return PI;
} //Cierre de la función con corchete.

En el caso en que una función requiera variables internas está declaración se debe hacer al
principio de la misma, antes de realizar cualquier otra acción o instrucción.

float Area_Circunferencia ( float Radio ) //Función para calcular el área del circulo.
{ // Apertura de la función con corchete.
float Area;
29
Area = Valor_PI()*Radio*Radio; //Cálculo del área del circulo.
return Area;
} //Cierre de la función con corchete.

Las funciones son creadas por el desarrollador, sin embargo existen algunas que están
predefinidas en el compilador y pueden ser consultadas e implementadas respetando los
parámetros de entrada y salida, la más importante de ellas es la función main o principal. La
importancia de está función radica en el hecho de que es sobre ella que corre todo el programa
del microcontrolador y está se ejecuta automáticamente cuando el microcontrolador se energiza.
La función main en el caso particular de los microcontroladores no contiene parámetros de
entrada ni de salida, su declaración se puede apreciar en el siguiente ejemplo:

void main( void ) //Declaración de la función main.
{ // Apertura de la función main.
//Código del programa principal del microcontrolador.
} //Cierre de la función main.

Las declaraciones de funciones hechas por el desarrollador deben ser declaradas antes de la
función main, y en el caso de una función que invoque a otra función debe ser declarada antes de
la función que hace el llamado.

Las funciones matemáticas trigonométricas y otras como logaritmos, exponenciales, y
potenciación pueden ser usadas con la librería predefinida por el compilador. Está librería se
denomina C_Math.
2.5 Creación de un programa en lenguaje C
La estructura de un programa en lenguaje C es relativamente simple, primero es indispensable
declarar las variables globales que el desarrollador considere necesarias para el funcionamiento
del programa, estás variables globales son reconocidas por todos los puntos de código del
programa incluidas las funciones propias del desarrollador y la función main. El paso a seguir es
hacer las declaraciones de funciones diseñadas por el desarrollador para las tareas específicas en
su programa. Posteriormente se declara la función main y al comienzo de está se deben declarar
las variables que se requieran dentro de la misma. El código que sigue debe configurar e
inicializar los puertos y módulos del microcontrolador que sean indispensables en la aplicación.
Por último se edita el código que contiene el aplicativo concreto del programa. En el siguiente
ejemplo se puede ver cómo hacer una estructura para un programa:

int Varible1; //Declaración de variables globales.
char Variable2;
float Variable3;

//Declaración de funciones propias del desarrollador.
float Valor_PI ( void )
{
float PI=3.1416;
return PI;
}

30
float Calculo_Area_Circulo( float Radio )
{
float Area;
Area = Valor_PI()*Radio*Radio; //Cálculo del área.
return Area;
}

void main( void ) //Declaración de la función main o principal
{
float Valor_Radio; //Declaración de variables de la función main.
float Valor_Area;
TRISB=0; //Configuración de puertos y módulos.
TRISC=0;
PORTB=0;
PORTC=0;
while( 1 ) //Código concreto del aplicativo.
{
Valor_Area=Calculo_Area_Circulo( Valor_Radio );
}
}
2.6 Condicionales e iteraciones en lenguaje C
Las formas condicionales e iterativas hacen parte vital de las estructuras de cualquier código en
lenguaje C. Estás permiten hacer ciclos en función de conteos y condiciones de variables que
hacen efectivo el flujo del programa. Las sentencias condicionales if, if else, y switch case,
permiten realizar menús, tomar decisiones, y controlar el flujo del programa o aplicativo. Las
estructuras iterativas while, do while y for, permiten crear bucles iterativos, que cuentan o
gobiernan tareas y procesos desarrollados en el programa.
2.6.1 Creación de condiciones lógicas en lenguaje C
Las condiciones lógicas permiten tomar decisiones en función de una evaluación. Las
condiciones lógicas solo retornan dos posibles valores: falso o verdadero. Cuando la condición
lógica se hace directamente sobre una variable o número, la condición es falsa cuando su valor es
0 y retorna verdadero para cualquier otro valor diferente de 0. Las formas condicionales usadas
en lenguaje C son: NOT, o negación, OR o o inclusiva, AND o y. También se pueden usar
comparaciones de magnitud entre dos valores tales como: mayor que, menor que, mayor o igual
que, menor o igual que, diferente que, e igual que. El uso de la negación se realiza con el carácter
admiración (!), la condición OR, se realiza con los caracteres doble barra vertical (||), la condición
AND o y, se usa con los caracteres doble ampersand (&&), la condición mayor que se usa con el
carácter que tiene su mismo nombre (>), la condición menor que, se hace con el carácter menor
que (<), la condición mayor o igual que, se realiza con los caracteres mayor que, e igual (>=), la
condición menor que, se realiza con los caracteres menor que, e igual (<=), la condición de
igualdad se hace con los caracteres doble igual (==), y la condición diferente que, se usa con los
caracteres de igualdad y admiración (!=). Para entender con claridad estos operadores observe y
analice los siguientes ejemplos:

short A;
31
short B;
//La condición es verdadera si A vale 10 y B vale 20.
(A==10)&&(B==20)
//La condición es verdadera si A es diferente de 5 y B es mayor que 2.
(A!=5)&&(B>2)
//La condición es verdadera si A es menor o igual que 4 o si B es mayor o igual que 6.
(A<=4)&&(B>=6)
//La condición es verdadera si A no es mayor que 3 y si B es diferente de 4.
!(A>3)&&(B!=4)
2.6.2 La sentencia condicional if e if else
La estructura de la sentencia if evalúa una condición lógica y ejecuta una porción de código si y
solo si el resultado de la evaluación es verdadera. Observe la estructura del la sentencia if en el
siguiente ejemplo:

short Valor;
if( Valor>100 ) //Este if evalúa si la variable Valor es mayor que 100.
{
// Porción de código que se ejecuta si el if es verdadero.
}

La estructura if else es igual que la sentencia if, su única diferencia es que en el dado caso de que
la evaluación de la condición sea falsa se ejecuta la porción de código asociada al else. Para
entender está situación observe el siguiente ejemplo:

short Valor;
if( Valor>100 ) //Este if evalúa si la variable Valor es mayor que 100.
{
// Porción de código que se ejecuta si la condición del if es verdadero.
}
else
{
// Porción de código que se ejecuta si la condición del if es falsa.
}

Las sentencias if e if else se pueden anidar para crear estructuras más completas y complejas, está
característica se puede ver con claridad en el siguiente ejemplo:

short Valor1; //Declaración de variables.
short Valor2;
if( Valor1>30 ) //Sentencia if.
{
// Código que se ejecuta cuando el primer if tiene una condición verdadera.
if( (Valor2==4)&&(Valor1<50) ) //Segunda sentencia if anidad.
{
//Código que se ejecuta cuando el segando if tiene una condición verdadera.
}
}
32
2.6.3 La sentencia switch case
La sentencia switch case, funciona de manera similar a la sentencia if, difiere en que no se evalúa
una condición si no el valor de una variable, y ejecuta una porción de código para cada valor
posible de la variable. Los valores contemplados en los casos o case posibles de la variable los
escoge el desarrollador arbitrariamente en función de su criterio y necesidad. Los casos o valores
que no son de interés para el desarrollador se ejecutan en un fragmento de código por defecto, por
medio de la directriz default. Cada uno de los fragmentos de código editados deben terminar con
la directriz break para romper el flujo de la estructura switch case, está acción es necesaria dado
que si dos casos están seguidos los fragmentos de código seguidos se ejecutarán hasta encontrar
la directriz break o hasta finalizar la estructura switch case. Para comprender de forma clara el
funcionamiento de la sentencia switch case observe el siguiente ejemplo:

short Valor;
switch( Valor ) //Evaluación de la variable Valor.
{
case 0: //Fragmento de código correspondiente al valor 0 de la variable Valor.
break; //Ruptura del caso 0.
case 1: //Fragmento de código correspondiente al valor 1 de la variable Valor.
break; //Ruptura del caso 1.
case 10: //Fragmento de código correspondiente al valor 10 de la variable Valor.
break; //Ruptura del caso 10.
case 20: //Fragmento de código correspondiente al valor 20 de la variable Valor.
break; //Ruptura del caso 20.
case 50: //Fragmento de código correspondiente al valor 50 de la variable Valor.
break; //Ruptura del caso 50.
default: //Código correspondiente para cualquier otro valor por defecto.
break; //Ruptura del default.
}
2.6.4 El ciclo iterativo while y do while
El ciclo iterativo while repite o ejecuta un fragmento de programa siempre y cuando la condición
contenida en el while sea verdadera. Para conocer la forma de declarar este tipo de ciclo observe
el siguiente ejemplo:

short CONT=0; //Declaración de variable entera que cuenta hasta 100.
while( CONT<100 ) //Declaración del ciclo while.
{
CONT++; //Incremento de la variable CONT.
}

La implementación de un ciclo do while, es similar al ciclo while, con la diferencia puntual de
que en este ciclo el fragmento de código se ejecuta y después evalúa la condición del while. En
conclusión el código se ejecuta por lo menos una vez antes de evaluar la condición. En el
siguiente ejemplo se puede ver la forma de usar este tipo de ciclo:

short CONT=0; //Declaración de variable entera que cuenta hasta 100.
do //Declaración del ciclo do while.
{
33
CONT++; //Incremento de la variable CONT.
} while( CONT<100 );
2.6.5 El ciclo iterativo for
El ciclo iterativo for es una estructura parecida al ciclo while, la diferencia es una estructura más
compleja en los paréntesis de la condición. La condición cuenta con tres parámetros, el primero
es un espacio de código para dar valores iniciales a una o más variables, el segundo campo
permite crear una condición lógica que hace repetir el fragmento de código del for cuando está es
verdadera, el último espacio realiza cambio sobre una o más variables, tal como: incrementos,
decrementos, etc. Las variables que se cambian en el último campo son generalmente las mismas
que se inician en el primer campo, pero está no es una regla. Observe la estructura del for en el
siguiente modelo:

for( _INICIO_ ; _CONDICION_; _CAMBIO_ )
{
}

Para conocer el funcionamiento del ciclo for observe el siguiente ejemplo:

short A; //Declaración de variables.
short B;
for( A=0, B=100; (A<100)||(B>20); A++, B-- ) //Estructura del ciclo for.
{
//Fragmento de código que se ejecuta cuando la condición del for es verdadera.
}
2.6.6 Uso anidado de ciclos iterativos
Muchas de las aplicaciones incrementan su funcionalidad y optimizan el tamaño del código de
máquina final al usar los ciclos iterativos de forma anidada. La anidación de ciclos consiste en
ejecutar un ciclo dentro de otro. Un ciclo puede contener uno o más ciclos anidados. Para
comprender este concepto de mejor manera observe el siguiente ejemplo:

short COL; //Declaración de variables.
short FIL;
short MATRIZ[10][10]; //Declamación de un vector bidimensional.
for( COL=0; COL<10; COL++ ) //Declaración de ciclo for.
{
//Fragmento del primer ciclo.
for( FIL=0; FIL<10; FIL++ ) //Declaración de ciclo for anidado.
{
//Fragmento del ciclo anidado.
MATRIZ[COL][FIL] = COL*FIL;
}
}
El ejemplo anterior, guarda valores en una matriz de 10 por 10, recorriendo una a una las
posiciones de la misma.
34
3 El simulador ISIS de Proteus
El simulador ISIS de Proteus es un poderoso paquete de software, desarrollado por la compañía
labcenter electronics, que se ha posicionado desde hace mas de 10 años, como una de las
herramientas más útiles para la simulación de los microcontroladores PICMicro. El ISIS permite
la simulación de las familias de los PICMicro más populares tales como la: 12F, 16F, 18F.
Además de los PIC, el ISIS puede simular una gran variedad de dispositivos digitales y
analógicos, entre los dispositivos digitales es posible simular displays de siete segmentos, de
caracteres, y gráficos. ISIS puede simular sensores de temperatura, humedad, presión, y
luminosidad, entre otros. El simulador permite, simular actuadores como: motores dc, servo
motores, luces incandescentes entre otros. Es posible simular periféricos de entrada y salida
como teclados, y puertos físicos del ordenador como: RS232, y USB. Este simulador cuenta con
una amplia variedad de instrumentos de medición como voltímetros, amperímetros,
osciloscopios, y analizadores de señal. En conclusión estás y otras características hacen del ISIS
de Proteus, una herramienta ideal para el diseño y estudio de los PICMicro. Una versión
demostrativa del paquete de software se puede descargar de la página: www.labcenter.com. En la
siguiente imagen se puede apreciar la apariencia visual del entorno de desarrollo del ISIS:



Figura 3-1

El uso y trabajo bajo en el torno de ISIS demanda una amplia cantidad de herramientas y
opciones que se deben conocer pero se conocerán paulatinamente en el transcurso de los
ejemplos.
35
3.1 Características básicas del ISIS para simular
En la siguiente sección se mostrarán las características básicas para hacer la primera simulación
en ISIS. Como primera medida se debe identificar la paleta de dispositivos, que está a la
izquierda de la pantalla, en está paleta el desarrollador debe identificar el botón P, para mayor
claridad observe la siguiente imagen:


Figura 3-2

Al picar el botón P, el programa abre una nueva ventana que permite buscar los dispositivos
electrónicos por medio de la referencia comercial, o bajo la clasificación que el mismo ISIS tiene.
Para buscar un dispositivo por medio de la referencia se digita la referencia en la casilla:
Keywords, el programa genera una lista en la parte derecha de la ventana con los dispositivos
relacionados a la solicitud del usuario. Para seleccionar un dispositivo de está lista se da doble
clic sobre cada uno de los numerales de la lista. Para iniciar busque los siguiente dispositivos:
BUTTON, LED-RED, RES, que corresponden a un pulsador, un LED de color rojo, y una
resistencia. Después de este proceso, en la paleta de dispositivos debe verse lo siguiente:


Figura 3-3

El paso siguiente es buscar las terminales de referencia y poder, para este fin se debe pulsar el
icono siguiente: este se encuentra en la paleta de herramientas que está en la parte izquierda
de la ventana del programa. Cuando este botón se pica permite ver una lista de terminales, en las
36
cuales se encuentra GROUND, y POWER, las cuales corresponden respectivamente a la
referencia eléctrica y al poder o Vcc. La terminal de poder tiene una diferencia de potencial por
defecto de 5 voltios. Para colocar estás terminales en el área de trabajo se pica los ítems en la
paleta de terminales y posteriormente en el área de trabajo, después de está acción en el área de
trabajo se debe ver lo siguiente:

Figura 3-4

El paso siguiente es pegar los dispositivos en el área de trabajo para esto pique el botón: que
se encuentra en la paleta de herramientas de la izquierda. Para pegar los dispositivos en el área de
trabajo se sigue el mismo procedimiento de las terminales, al finalizar se bebe tener la siguiente
vista:

Figura 3-5

El paso siguiente es realizar las conexiones eléctricas, para este ejemplo se conectan todos los
elementos en serie, para este fin el cursor del ratón adopta la forma de lápiz, para hacer la
conexión entre dos terminales se pica una terminal y después la siguiente. El programa termina
rutiando la conexión. Al terminar las conexiones se ve la siguiente vista:

37


Figura 3-6

Para finalizar se puede cambiar el valor de la resistencia dando clic izquierdo sobre la resistencia,
con está acción sale una ventana que permite editar el valor de la resistencia, que por defecto es
de 10k, lo que se debe hacer es cambiarlo por 330.

El paso siguiente es ejecutar o correr la simulación para esto se utiliza la paleta de reproducción
que está en la parte inferior izquierda de la pantalla. La apariencia de está paleta es la siguiente:


Figura 3-7

Por último se pica el botón de play y la simulación se ejecuta, cuando la simulación este
corriendo se puede pulsar el BUTTON, y ver el efecto de la simulación. Este simple ejemplo
permite mecanizar el proceso de creación de un circuito digital, para su posterior simulación.

La creación de circuitos en ISIS, implica hacer otra serie de acciones, que serán tratadas en el
transcurso de los ejemplos.
38
4 Creación del primer
programa en MikroC PRO
El proceso siguiente debe ser mecanizado para ser implementado cada vez que se haga un
proyecto o programa nuevo para un PICMicro. Al correr el MikroC PRO, se identifica en el
menú superior el ítem Project, se pica y dentro de este se pica New Project…, con está acción el
programa despliega un asistente fácil de usar para crear el nuevo proyecto. La apariencia visual
de este asistente es la siguiente:


Figura 4-1

La siguiente acción es pulsar el botón Next, con este paso el asistente muestra una casilla para
seleccionar la referencia del PICMicro, que se desea usar. En está opción seleccione el PIC
P16F84A. El siguiente paso es definir la frecuencia se oscilación con la cual trabajará el PIC, en
este ejemplo se selecciona 4.000000 MHz. La siguiente opción permite definir el directorio en
donde el desarrollador guardará el proyecto, en este directorio el programa guardará todos los
archivos requeridos, en los cuales la fuente del código será el archivo con extensión .c, y el
ejecutable del PIC es el archivo .hex. El siguiente paso solicita adicionar archivos que serán
anexados al proyecto. Cuando se realiza un proyecto nuevo este paso se puede omitir y se pulsa
Next, El último ítem del asistente pregunta si el desarrollador quiere seleccionar las librerías que
usará en este trabajo, por defecto este ítem selecciona todas las librerías habilitadas para este PIC,
39
lo mejor es dejar todas las librerías activas. Por último se termina la configuración y se crea el
proyecto, al terminar debe aparecer una vista como la siguiente:


Figura 4-2

Cuando un cambio se realice sobre el código del programa se debe compilar el código picando el
siguiente botón: que está ubicado en la parte superior del programa dentro de la paleta de
herramientas. Está acción genera los resultados de la compilación que se encuentran en la parte
inferior de la ventana del programa. Los mensajes se deben terminar con un texto de finalización
exitosa.

Para iniciar la edición del proyecto, se configuran los puertos del PIC, y posteriormente se
encierra el programa en un bucle infinito. El PIC 16F84A, tiene dos puertos él A y el B, para
configurar los puertos como salida o como entrada se manipula el registro TRIS. Cada puerto
cuenta con su respectivo registro TRIS, que hace alusión a los tres estádos posibles, alto, bajo, y
alta impedancia. Los registros TRIS cuentan con el mismo número de bits del puerto, por ejemplo
el puerto B o PORTB de este PIC tiene 8 bits, por lo tanto el TRISB también tiene 8 bits. Los bits
de los registros TRIS, son correspondientes al puerto, y definen bit a bit el estádo del puerto. Si
un bit en el TRIS es 0 el mismo bit en el puerto es de salida, y si el bit del TRIS es 1 el mismo bit
del puerto es de entrada o está en alta impedancia. Para entender este concepto con mayor
claridad observe y analice el siguiente ejemplo:

TRISB = 0b11110000; // Configura los cuatro bits de menor peso como salida, y
//los cuatro bits de mayor peso como entrada.
PORTB=0b00000000; //Los bits de salida asumen un 0 lógico.

Este ejemplo usa un pulsador y un par de LEDs para ver el comportamiento del programa.
Observe y analice el programa a continuación:
void main ( void )
40
{
unsigned int CONTADOR=0;
TRISB = 0b11110000; // Configura los cuatro bits de menor peso como salida, y
//los cuatro bits de mayor peso como entrada.
PORTB=0b00000000; //Los bits de salida asumen un 0 lógico.
while( 1 ) //Bucle infinito
{
if( PORTB.F7==0 ) //Evalúa si bit RB7 es 0
{
if( PORTB.F0==1 ) //Evalúa el valor del bit RB0 y conmuta su valor.
PORTB.F0=0;
else
PORTB.F0=1;
while( PORTB.F7==0 ); //Espera a que el RB7 cambie a 1.
}
CONTADOR++; //Incrementa el valor del CONTADOR.
//La siguiente condición if cambia automáticamente el estádo del bit RB1
if( CONTADOR&0x0100 ) //Evalúa si el bit 8 del CONTADOR es 1
PORTB.F1=1;
else
PORTB.F1=0;
}
}

El paso siguiente es hacer la simulación en ISIS, las resistencias de los LEDs deben ser
cambiadas a 330Ω, la terminal Master Clear, MCLR debe ser conecta a Vcc para que el PIC, no
se reinicie al terminar se debe ver de la siguiente forma:


Figura 4-3
41
Antes de correr la simulación se debe cargar el archivo .hex, para este proceso se hace clic
izquierdo sobre el PIC, y aparece una ventana que permite buscar el archivo .hex, en está ventana
también se ajusta la frecuencia de oscilación. Por defecto este valor es 1MHz, para efectos de
simulación en este caso se debe seleccionar 4MHz, la vista de está ventana es la siguiente:


Figura 4-4


Para fines de la programación física del microcontrolador es importante tener presente las
condiciones de configuración del PIC, cada uno de los microcontroladores de Microchip, poseen
un campo de memoria que caracteriza el comportamiento de real del PIC, entre estos se pueden
destácar; el tipo de oscilador o reloj de procesamiento, por ejemplo cuando se usa un cristal de
4M Hz, el oscilador del PIC se debe configurar como XT, si el oscilador es de 20M Hz se usa la
configuración HS, si se trata de un cristal de baja velocidad como 400K Hz se usa la
configuración LP, y si se implementa un oscilador por medio de circuito RC, o circuito de
resistencia en serie con capacitor se usa la opción RC. De la misma forma es posible configurar
otras opciones como el uso del perro guardián, la protección de código en memoria FLASH y
EEPROM, está última opción es de suma importancia para proteger la información del
microcontrolador y evitar que el programa o los datos de la memoria sean leídos. Para modificar
la configuración del PIC, se debe buscar el ítem Edit Proyect… que se encuentra en la pestáña
Proyect, del menú principal del programa MikroC PRO.

El acceso a este ítem se puede apreciar en la siguiente figura:

42

Figura 4-5


Después de picar está opción el compilador MikroC PRO, despliega una ventana con casillas de
selección para editar las opciones de configuración del PIC, en función del microcontrolador está
ventana puede cambiar de apariencia dado que no todos los PIC, cuentan con las mismas
configuraciones, sin embargo en la siguiente figura se puede apreciar la apariencia de esta
ventana para un PIC 16F877A:

43


Figura 4-6

44
5 Visualización de datos
El diseño de sistemas microcontrolados implican en algunos casos la visualización de datos al
usuario, para este fin se pueden usar display 7 segmentos, display LCD de caracteres y display
gráficos LCD. En este capítulo se estudia y se ejemplarizan estos dispositivos.
5.1 Display de 7 segmentos
Un display 7 segmentos es un dispositivo que permite visualizar un número limitado de
caracteres esencialmente numéricos, sin embargo es posible visualizar unos pocos caracteres mas
como: b, d, E, A, o, F, C, -. Los display 7 segmentos son un arreglo de diodos LED, organizados
de tal forma que permiten visualizar los caracteres según los segmentos que estén activos. Los
displays de 7 segmentos tienen una designación estándar de cada segmento que es consecutiva de
la „a‟ hasta la „g‟. Está designación y la apariencia física de estos displays se pueden apreciar en
las siguientes figuras:



Figura 5-1

Los display de 7 segmentos son fabricados en dos formatos; de ánodo común y de cátodo común,
los display de 7 segmentos también existen en un formato dinámico, estos últimos usan dos o más
dígitos en un solo encapsulado conectando todos los segmentos en paralelo pero con los
terminales comunes por separado. Las siguientes figuras muestran displays de 7 segmentos en su
formato dinámico:


Figura 5-2
45
La terminal DP, que se puede apreciar en la figura anterior es un octavo segmento que en algunos
displays es implementado y corresponde al punto decimal, este se usa si la aplicación así lo
requiere.
5.1.1 Control de display de 7 segmentos
Para realizar el primer ejemplo con este display se bebe conocer como realizar la asignación de
pines del PIC, a cada uno de los segmentos del display se debe conectar un pin del PIC, para
hacer un diseño optimo se puede asignar los pines en orden consecutivo de un puerto por ejemplo
el segmento „a‟ se conecta al pin RB0, el segmento „b‟ al pin RB1, y así sucesivamente hasta el
segmento „g‟ al pin RB6. Sin embargo la designación de pines la puede hacer el desarrollador de
manera arbitraria. Es importante conocer una herramienta con la que cuenta el paquete de
software MikroC PRO, que permite editar los dígitos del display, para este fin se pica el ítem
Tools en el menú del MikroC PRO, dentro de este nuevo menú se pica el ítem Seven Segment
Editor. Con está acción emerge una nueva ventana que permite editar de manera simple los
segmentos del display de 7 segmentos. A medida que se edita el display aparece el valor
constante que debe ser usado en las salidas del puerto que se designe para su control. El editor
permite implementar las constantes para un display de ánodo o de cátodo común, de la misma
forma es posible usar las constantes en formato decimal o hexadecimal. La apariencia visual del
editor es la siguiente:


Figura 5-3

El uso de está herramienta, implica que todos los pines del display estén asignados a un mismo
puerto, y en orden consecutivo como se comentó anteriormente. Para la visualización de los
dígitos en el display se debe organizar la información de los dígitos en orden consecutivo, en el
caso de este ejemplo del 0 al 9. Para este fin la forma de mayor simplicidad es con el uso de una
cadena de datos que contenga los 10 códigos de los dígitos. En el siguiente ejemplo se declarará
un arreglo constante con los códigos de cada dígito, para este ejemplo se usará un display de
46
cátodo común. Observe la forma de declarar las constantes que deben ser insertadas antes de la
función main:

const unsigned short DIGITOS[] =
{
0x3F, //Código del dígito 0
0x06, //Código del dígito 1
0x5B, //Código del dígito 2
0x4F, //Código del dígito 3
0x66, //Código del dígito 4
0x6D, //Código del dígito 5
0x7D, //Código del dígito 6
0x07, //Código del dígito 7
0x7F, //Código del dígito 8
0x6F, //Código del dígito 9
};

Para visualizar los dígitos en el display de 7 segmentos, este ejemplo usará el puerto B del
microcontrolador PIC 16F84A, la visualización de los dígitos estárá controlada por una rutina
que cambia temporizadamente por medio de la función delay_ms que está predefinida por las
librerías del compilador. Está función tiene como parámetro de entrada un número entero que
representa el tiempo en milisegundos, durante los cuales el PIC ejecutará un retardo, de la misma
forma se puede usar la función delay_us, que es idéntica a delay_ms pero en micro segundos.

A continuación se muestra el código fuente del PIC, para este ejemplo:

const unsigned short DIGITOS[] =
{
0x3F, //Código del dígito 0
0x06, //Código del dígito 1
0x5B, //Código del dígito 2
0x4F, //Código del dígito 3
0x66, //Código del dígito 4
0x6D, //Código del dígito 5
0x7D, //Código del dígito 6
0x07, //Código del dígito 7
0x7F, //Código del dígito 8
0x6F, //Código del dígito 9
};

void main ( void )
{
unsigned short CONTADOR=0;
TRISB = 0; // Configura el puerto B como salida
while( 1 ) //Bucle infinito
{
PORTB = DIGITOS[CONTADOR]; //Se visualiza el digito correspondiente al
//número guardado en la variable CONTADOR.
CONTADOR++; //Se incrementa el valor del conteo.
47
delay_ms(1000); //Se hace un retardo de 1 segundo.
}
}

El paso siguiente es realizar la simulación en ISIS, para este fin se buscan los siguientes
dispositivos: 16F84A, RES, y 7SEG-COM-CATHODE, para la conexión entre el PIC, y el
display se deben usar resistencias de 330Ω. Posteriormente se arma el siguiente circuito:


Circuito 5-1

Al correr la simulación se debe ver un conteo en el display del 0 al 9, con una cadencia de un
segundo entre dígito y dígito.
5.1.2 Control de displays 7 segmentos dinámicos
La implementación de displays dinámicos usa la misma teoría de un solo display, la visualización
dinámica consiste en mostrar un solo dígito al mismo tiempo. Por ejemplo si se muestran cuatro
dígitos se activa el display de las unidades, después se apagan y se activa el dígito de las decenas,
posterior mente se apaga y se activa el dígito de las centenas, y por último se hace lo mismo con
las unidades de mil. Este proceso se debe hacer con una velocidad de tal manera que engañe al
ojo humano y así se verá como si todos los dígitos estuvieran activos. Este arreglo minimiza las
conexiones eléctricas y el consumo de energía, dado que en realidad solo un dígito está activo
para todo tiempo. A la vista del ojo humano los cambios deben ser de 25Hz o más, por lo tanto
todos los dígitos deben verse durante un periodo igual al inverso de 25Hz, en este caso es 40m
segundos. Para este ejemplo se usarán 4 displays por lo tanto el tiempo visible de cada dígito
debe ser la cuarta parte del periodo es decir 10m segundos. La forma más eficiente de mostrar los
números en el display es por medio de una función. Con 4 dígitos es posible visualizar un número
de 0 a 9999, por esto el número puede ser consignado en una variable de tipo entera. Para ver
cada dígito por separado se hace necesario calcular cada uno de los dígitos, por ejemplo para
deducir las unidades de mil vasta con dividir el número en mil, y luego restár las unidades de mil
del número completo, este proceso se repite hasta llegar a las unidades. La activación de los
displays se debe hacer por medio de otro puerto para este ejemplo se hará por el puerto A.
Para comprender este proceso observe y analice la siguiente función:

48
// Declaración de las constantes para el display.
const unsigned short DIGITOS[] =
{
0x3F, //Código del dígito 0
0x06, //Código del dígito 1
0x5B, //Código del dígito 2
0x4F, //Código del dígito 3
0x66, //Código del dígito 4
0x6D, //Código del dígito 5
0x7D, //Código del dígito 6
0x07, //Código del dígito 7
0x7F, //Código del dígito 8
0x6F, //Código del dígito 9
};

//Función para visualizar el display dinámico.
void VerDisplay( int Numero )
{
unsigned short U; //Variable para guardar las unidades.
unsigned short D; //Variable para guardar las decenas.
unsigned short C; //Variable para guardar las centenas.
unsigned short UM; //Variable para guardar las unidades de mil.
UM = Numero/1000; //Cálculo de las unidades de mil.
C = (Numero-UM*1000)/100; //Cálculo de las centenas.
D = (Numero-UM*1000-C*100)/10; //Cálculo de las decenas.
U = (Numero-UM*1000-C*100-D*10); //Cálculo de las unidades.

PORTB = DIGITOS[U]; //Visualiza las unidades.
PORTA.F0=1; //Activa en alto el primer display
delay_ms(10); //Retado de 10m segundos
PORTA=0; //Desactiva todos los displays.

PORTB = DIGITOS[D]; //Visualiza las decenas.
PORTA.F1=1; //Activa en alto el segundo display
delay_ms(10); //Retado de 10m segundos
PORTA=0; //Desactiva todos los displays.

PORTB = DIGITOS[C]; //Visualiza las centenas.
PORTA.F2=1; //Activa en alto el tercer display
delay_ms(10); //Retado de 10m segundos
PORTA=0; //Desactiva todos los displays.

PORTB = DIGITOS[UM]; //Visualiza las unidades de mil.
PORTA.F3=1; //Activa en alto el cuarto display
delay_ms(10); //Retado de 10m segundos
PORTA=0; //Desactiva todos los displays.
}

Por último se completa el código fuente con la función main de la siguiente forma:
49

void main ( void )
{
unsigned short N=0; //Variable de conteo.
int Numero=0;
TRISB = 0; //Configura el puerto B como salida
TRISA = 0; //Configura el puerto A como salida
PORTA = 0; //Se desactiva todos los displays
while( 1 ) //Bucle infinito
{
//Se visualiza el valor de Número.
VerDisplay( Numero ); //Está función dura aproximadamente 40m segundos.
//Se cuentan 12 incrementos en N para hacer un incremento
//en Número aproximadamente cada 500m segundos.
N++;
if( N==12 )
{
N=0; //Se reinicia el conteo de N.
Numero++; //Se incrementa el valor de Número.
if( Numero==10000 ) //Se evalúa si Número vale 10000
Numero=0; //y se reinicia en 0 si es 10000.
}
}
}

Al terminar y compilar el programa se realiza la simulación en ISIS, con los siguiente
dispositivos: 16F84A, RES, 7SEG-MPX4-CC, 7404. Finalmente se construye el siguiente
circuito:


Circuito 5-2
50
Para fines prácticos los inversores 7404 se pueden remplazar por un arreglo de transistores como
se ven en la siguiente imagen:

Circuito 5-3

Cuando la simulación este en marcha el circuito debe mostrar un conteo de 0 a 9999 con una
cadencia de 500m segundos entre cada incremento.
5.2 Display LCD de caracteres
Los display de caracteres LCD, son módulos prefabricados que contienen controladores
incluidos. Estos displays cuentan con un bus de datos y un bus de control, para el manejo de estos
dispositivos el compilador MikroC PRO, tiene una librería predefinida para el control de estos
LCD. La apariencia física y la vista en ISIS de estos LCD es la que se puede apreciar en las
siguientes gráficas:


Figura 5-4

Los displays LCD, permiten graficar los caracteres contemplados en el código ASCII. Además
del código ASCII, los displays LCD admiten graficar hasta 8 caracteres diseñados por el
desarrollador, otra característica fundamental de los LCD, es la conexión del bus de datos,
físicamente tienen 8 bits, pero es posible configurar las conexiones con solo 4 bits. La conexión
de 8 bits implica una mayor cantidad de cables para su uso, pero la velocidad de trabajo es
mayor, por consiguiente la conexión de 4 bits minimiza las conexiones pero disminuye la
velocidad de trabajo. La librería predefinida en MikroC PRO, funciona con la configuración de 4
bits.
51

Para ver la librería predefinida para este dispositivo y otros que tiene MikroC PRO, se debe picar
la paleta de herramientas ubicada en la parte derecha del programa, está paleta se identifica con
una pestáña que tiene el titulo: Library Manager. Al picar está pestáña se despliega un menú que
muestra las diferentes librerías habilitadas para el microcontrolador que se está trabajando. En
este nuevo menú se identifica el ítem Lcd, posteriormente se puede picar cualquiera de las
funciones que contiene la librería para ver la ayuda correspondiente. La apariencia visual de está
paleta se puede apreciar en la siguiente imagen:


Figura 5-5

La implementación del display LCD, requiere del uso de instrucciones y comandos secuénciales
para la configuración y desempeño del display, sin embargo la librería de MikroC PRO,
minimiza este trabajo dado que está se encarga de hacer todas estás configuraciones, haciendo
mucho más simple el trabajo del desarrollador. Como primer paso para el uso del LCD, se
requiere definir los pines de conexión y la ejecución de la función predefinida de inicio del LCD;
Lcd_Init(). La definición de los pines de conexión la asume el desarrollador de forma arbitraria
según su criterio. Para cumplir este objetivo se usa la siguiente declaración de constantes:

52
//Pines de salida para el LCD
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;

//Bits de configuración TRIS
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;

Para cambiar los pines solo se requiere cambiar el nombre de las declaraciones del ejemplo
anterior. Como se puede ver en la declaración inicial solo se necesitan 6 pines para gobernar el
LCD, con 4 bits de datos y 2 bits de control, el display cuenta con un tercer pin de control
denotado como WR, este pin permite leer si el display está ocupado realizando alguna operación
o permite escribir comandos y caracteres. Para fines de la librería este pin no es requerido, y
simplemente se conecta a referencia. Por último se debe invocar la función de inicio del display
dentro de la función main después de la configuración de puertos. Los pines de la LCD, no se
deben configurar, está tarea ya la realiza la función: Lcd_Init(). La declaración de la función main
debe quedar de la siguiente forma:

void main( void )
{
Lcd_Init(); //Inicio del LCD.
while(1) //Bucle infinito.
{
}
}

Terminada la edición del código anterior el LCD, se inicializa y debe quedar listo para empezar a
graficar caracteres, posicionándose en la primera fila y primera columna, mostrando el cursor
parpadeante.

Los displays de caracteres se fabrican en diversas formas y colores se pueden conseguir con
pantallas de color verde, azul, y amarillo. Se fabrican con distribuciones de caracteres de forma
matricial como 2 filas, 16 columnas estos se conocen como 2x16, y de la misma forma se pueden
encontrar de 1x16, 2x16, 2x8, 2x20, 4x20, entre otros. Para los ejemplos empleados en este
capítulo se usará el display de 4x20. Para iniciar la simulación del display LCD, se debe buscar el
dispositivo LM044L, y el PIC 16F84A en el simulador ISIS. La referencia LM044L en ISIS
corresponde a un LCD de 4x20. Finalmente se realizan las conexiones como se muestra en el
siguiente circuito:
53

Circuito 5-4

El display LCD cuenta con un pin denominado vee, este pin funciona como controlador de
contraste de la pantalla, pero para fines de la simulación no tiene efecto, este pin puede ser
conectado a referencia para generar el mayor contraste, algunas pantallas de gran tamaño
requieren de un voltaje negativo externo para el control de este contraste, en síntesis, cuanto más
negativo sea el voltaje en Vee más oscuro será el contraste en la pantalla. Para fines prácticos el
contraste se puede ajustar por medio de un potenciómetro como se ve en la siguiente circuito:


Circuito 5-5

El paso siguiente es imprimir en la pantalla información, para este objetivo se pueden usar cuatro
funciones. Dos de estas funciones permiten imprimir caracteres, y las otras dos imprimen cadenas
54
de texto. Para imprimir los caracteres se puede hacer de dos maneras, la primera simplemente
imprime los caracteres en el orden consecutivo que asume el display, y la siguiente función
imprime los caracteres en la fila y columna que se designe por el desarrollador.
5.2.1 Funciones para imprimir caracteres
La primera función de impresión de caracteres es: Lcd_Chr_Cp(char out_char); cuando está
función es invocada imprime en la pantalla el carácter correspondiente al código ASCII, que está
en el parámetro de entrada out_char. Con la impresión de un nuevo carácter la pantalla LCD,
incrementa automáticamente el cursor en una posición. Para contextualizar el funcionamiento de
esta función observe y analice el siguiente ejemplo:

void main( void )
{
Lcd_Init(); //Inicio del LCD.
Lcd_Chr_Cp(„H‟); //Estás funciones imprimen letra a letra la palabra “Hola”.
Lcd_Chr_Cp(„o‟);
Lcd_Chr_Cp(„l‟);
Lcd_Chr_Cp(„a‟);
while(1) //Bucle infinito.
{
}
}

Después de correr la simulación se debe observar lo siguiente en la pantalla del LCD:


Figura 5-6

Para realizar la impresión de caracteres por medio de una coordenada fila, columna, se
implementa la función: Lcd_Chr(char row, char column, char out_char); está función imprime
el carácter out_char, en la columna column y en la fila row. En el siguiente ejemplo se puede ver
cómo usar está función:

55
void main( void )
{
Lcd_Init(); //Inicio del LCD.
Lcd_Chr_Cp(„H‟); //Estás funciones imprimen letra a letra la palabra “Hola”.
Lcd_Chr_Cp(„o‟);
Lcd_Chr_Cp(„l‟);
Lcd_Chr_Cp(„a‟);
Lcd_Chr( 1, 6, „1‟); //Imprime el carácter 1, en la fila 1, columna 6
Lcd_Chr( 2, 7, „2‟); //Imprime el carácter 2, en la fila 2, columna 7
Lcd_Chr( 3, 8, „3‟); //Imprime el carácter 3, en la fila 3, columna 8
Lcd_Chr( 4, 9, „4‟); //Imprime el carácter 4, en la fila 4, columna 9
while(1) //Bucle infinito.
{
}
}

Después de ejecutar la simulación se debe tener la siguiente vista en la pantalla del LCD:


Figura 5-7
5.2.2 Funciones para imprimir cadenas de texto
El uso de cadenas de texto es similar a las dos funciones anteriores, para esto se pueden imprimir
cadenas de texto en el punto donde está el flujo de la impresión del LCD, o en un punto arbitrario
por medio de las coordenadas de filas y columnas. Para hacer la impresión de una cadena de texto
en el punto de impresión se utiliza la siguiente función: Lcd_Out_Cp(char *text); está cuenta con
un único parámetro de entrada que es un apuntador a la cadena de caracteres en este caso la
función la denota como text. Para el uso de está función observe y analice el siguiente ejemplo:

void main( void )
{
Lcd_Init(); //Inicio del LCD.
Lcd_Out_Cp(“Hola Mundo…”);
while(1) //Bucle infinito.
56
{
}
}

La implementación de está función puede hacerse con cadenas de caracteres constantes o
variables, las cadenas constantes se denotan por medio de las dobles comillas al inicio y al final
del texto por ejemplo: “Hola Mundo…”, el formato variable se declara con la forma: char
Text[20]=”Hola Mundo…”;. Después de correr la simulación se bebe ver una vista como la que
aparece en la siguiente figura:


Figura 5-8


Para imprimir una cadena de caracteres con una coordenada como punto de inicio se implementa
la función: Lcd_Out(char row, char column, char *text);. Está función trabaja de forma similar a
la anterior, con la diferencia que se incluyen los datos de row, y column, que hacen referencia a
las filas y las columnas respectivamente. Para comprender de forma clara el funcionamiento de
esta función observe y analice el siguiente ejemplo:

void main( void )
{
Lcd_Init(); //Inicio del LCD.
Lcd_Out( 1, 1, “Fila 1, Columna 1” );
Lcd_Out( 2, 2, “Fila 2, Columna 2” );
while(1) //Bucle infinito.
{
}
}

Después de compilar y simular este ejemplo se debe observar una vista en la pantalla del LCD
como la que se puede apreciar en la siguiente figura:

57

Figura 5-9
5.2.3 Impresión de valores numéricos
La impresión de valores numéricos es de vital importancia en muchos de los desarrollos. Por
ejemplo cuando se desea visualizar, el estádo de una variable, un sensor como: temperatura,
humedad, presión, o cualquier otro que se esté manipulando. Para lograr este objetivo se puede
recurrir a funciones de conversión predefinidas por el compilador. Para esto se puede utilizar la
librería: Conversions, está librería cuenta con funciones que hacen la conversión de un valor
numérico a una cadena de caracteres. Si se requiere visualizar un valor entero se usa la función:
IntToStr(int input, char *output);, está función tiene dos parámetros de entrada que son: input,
que es un valor entero a visualizar, y output, que es el apuntador a una cadena de caracteres
donde se quiere escribir el forma de texto el valor que contiene el valor intput. Para entender este
tipo de conversión observe y analice el siguiente ejemplo:


void main( void )
{
int ENTERO=123; //Declaración de una variable entera con valor inicial 123.
char Text[20]; //Cadena de caracteres para impresión de datos.
Lcd_Init(); //Inicio del LCD.
IntToStr( ENTERO,Text ); //Función que hace la conversión.
Lcd_Out_Cp(Text); //Impresión del texto en la pantalla LCD.
while(1) //Bucle infinito.
{
}
}


La impresión de los números enteros en la cadena de texto con está función siempre asume un
campo fijo de 7 caracteres, es decir que si el número tiene menos de 7 dígitos el resto del texto se
completa con espacios vacíos. Después de compilar y simular el programa se tendrá una vista en
ISIS, como la que se puede ver en la siguiente figura:

58

Figura 5-10


La impresión de números con punto decimal se puede hacer con la función: FloatToStr(float
fnum, char *str);. La filosofía de funcionamiento de esta función es idéntica a la anterior que
hace las conversiones de números enteros. Para realizar el siguiente ejemplo se debe cambiar la
referencia del microcontrolador, esto se debe a que la capacidad de memoria del PIC 16F84A, no
es suficiente para los siguientes ejercicios. A partir de este punto se usara el PIC 16F628A, que
tiene la misma distribución de pines del 16F84A, pero cuenta con una capacidad mayor de
memoria y con módulos integrados superiores como la USART. Para contextualizar su
funcionamiento observe el siguiente ejemplo:


void main( void )
{
int ENTERO=123; //Declaración de una variable entera con valor inicial 123.
float DECIMAL=12.76543; //Declaración de una variable con punto decimal
//inicializada en 12,76543.
char Text[20]; //Cadena de caracteres para impresión de datos.
Lcd_Init(); //Inicio del LCD.
IntToStr( ENTERO,Text ); //Función que hace la conversión entera.
Lcd_Out(1,1,Text); //Impresión del texto en la pantalla LCD.
FloatToStr( DECIMAL,Text ); //Función que hace la conversión decimal.
Lcd_Out(2,1,Text); //Impresión del texto en la pantalla LCD.
while(1) //Bucle infinito.
{
}
}


Terminada la simulación se de tener una vista como la que se aprecia en la figura siguiente:

59

Circuito 5-6

El mismo proceso se puede seguir para otros tipos de variables como son: short con la función:
ShortToStr(short input, char *output);
Las variables long con la función:
LongToStr(long input, char *output);
Las variables unsigned short con la función:
ByteToStr(unsigned short input, char *output);
Las variables unsigned long con la función:
LongWordToStr(unsigned long input, char *output);
Las variables unsigned int con la función:
WordToStr(unsigned int input, char *output);
5.2.4 Creación de caracteres propios
Las pantallas de caracteres LCD, cuentan con la capacidad de almacenar hasta 8 caracteres
diseñados por el desarrollador. Los valores ASCII, para los caracteres diseñados por el usuario
corresponde a los valores numéricos del 0 al 7. Por ejemplo, si se desea imprimir el primer
carácter diseñado por el usuario se hace con las funciones de impresión de caracteres:
Lcd_Chr_Cp(0); o Lcd_Chr(1,1,0);. La creación de caracteres propios creados por el usuario se
guardan en un campo de memoria RAM, de la pantalla LCD, por está razón deben ser
reprogramados cada vez que se inicializa la pantalla LCD. Los caracteres son un conjunto de
valores binarios que forman un mapa de bits, y dan como resultado la imagen en la pantalla con
una resolución de 5 x 7 píxeles. La edición y programación de estos datos resulta dispendioso y
cuidadoso con las órdenes de bajo nivel que se beben dar a la pantalla LCD. Sin embargo el
compilador MikroC PRO, cuenta dentro de sus herramientas con un editor para los caracteres
diseñados por el desarrollador. En este editor de forma simple se edita el mapa de bits del
carácter y este genera la fracción de código en lenguaje C, requerida para programar el carácter
en la pantalla. En conclusión se edita el carácter y posteriormente se pega el código en la fuente
60
del proyecto que se está desarrollando. Para usar este editor se busca el ítem Tools dentro del
menú superior del programa MikroC PRO, y dentro de este se pica el submenú: LCD Custom
character, está acción despliega la ventana del editor que tiene la apariencia que se puede ver en
la siguiente figura:


Figura 5-11

En este editor el usuario pica los cuadros de la cuadricula de color verde y al terminar la edición
del carácter, se selecciona el valor ASCII que se asignará al carácter por medio de la casilla
Char:, este valor debe ser un número entre 0 y 7. Terminado este proceso se pulsa el botón
GENERATE, y con está acción el editor mostrará la fracción de código para usar en el programa
que se está desarrollando. Las siguientes gráficas muestran la vista del editor cuando se hace un
carácter en la dirección ASCII 0:


Figura 5-12


Como último paso se debe integrar la información entregada por el editor y el programa del
desarrollador, para este fin observe el siguiente ejemplo:

61
//Constantes creadas por el editor, está contiene el mapa de bits del carácter.
const char character[] = {17,14,21,14,31,4,27,0};


//Pines de salida para el LCD
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;


//Bits de configuración TRIS
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;


//Función para guardar en la memoria RAN del LCD
//el carácter del editor.
void CustomChar(char pos_row, char pos_char)
{
char i;
LCD_Cmd(64); //Dirección del carácter 0.
//Bucle for para guardar el mapa de bits.
for (i = 0; i<=7; i++) LCD_Chr_Cp(character[i]);
//Para fines prácticos está función puede ser ignorada.
//LCD_Cmd(LCD_RETURN_HOME);
//Impression del codigo ASCII 0.
LCD_Chr(pos_row, pos_char, 0);
}


void main( void )
{
Lcd_Init(); //Inicio del LCD.
CustomChar(1, 1);//Impresión del Carácter 0.
while(1) //Bucle infinito.
{
}
}


Transcurrida la edición compilación y simulación se debe tener la siguiente vista en la simulación
de ISIS:
62

Figura 5-13

Para optimizar el uso de la generación de caracteres arbitrarios el desarrollador puede
simplemente grabar la memoria al principio de las configuraciones y después hacer la impresión
de los caracteres, editados del 0, al 7. Para esto se debe tener presente el arreglo constante de
datos que corresponden a los mapas de bits, la dirección inicial que muestra el editor, e invocar
las funciones de impresión de caracteres cuando sea necesario. Para formalizar está idea observe
y analice el siguiente ejemplo que imprime en la pantalla la imagen de 4 caracteres con forma de
extraterrestres:

//Declaración de constantes, representan los mapas de bits,
//de un marcianito animado. Estos código son el resultado del editor de caracteres de
//MikroC PRO.
const char Marciano1[] = {17,14,21,14,31,4,27,0}; //Dirección inicial 64.
const char Marciano2[] = {17,14,21,14,31,4,10,0}; //Dirección inicial 72.
const char Marciano3[] = {17,14,21,14,31,21,10,0}; //Dirección inicial 80.
const char Nave[] = {24,28,30,31,30,28,24,0}; //Dirección inicial 88.

//Pines de salida para el LCD
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;

//Bits de configuración TRIS
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;


63
//Declamación de función para guardar en RAM los caracteres.
//Está función es diseñada por el desarrollador.
void Lcd_RAM_Car( char IntDir, const char *Mapa )
{
char i;
LCD_Cmd(IntDir); //Dirección inicial.
//Bucle para guardar el mapa en la RAM del LCD.
for( i=0; i<8; i++ ) LCD_Chr_Cp( Mapa[i] );
}

void main( void )
{
Lcd_Init(); //Inicio del LCD.
Lcd_RAM_Car( 64, Marciano1 );//Se guarda el character 1
Lcd_RAM_Car( 72, Marciano2 );//Se guarda el character 2
Lcd_RAM_Car( 80, Marciano3 );//Se guarda el character 3
Lcd_RAM_Car( 88, Nave );//Se guarda el character 4
LCD_Chr(1, 1, 0);//Se imprime el carácter Marciano 1.
LCD_Chr(1, 2, 1); //Se imprime el carácter Marciano 2.
LCD_Chr(1, 3, 2); //Se imprime el carácter Marciano 3.
LCD_Chr(1, 4, 3); //Se imprime el carácter Nave.

while(1) //Bucle infinito.
{
}
}

Terminada la compilación y corrida la simulación se bebe tener una vista como la siguiente en el
simulador ISIS:


Figura 5-14

La impresión de información en la pantalla LCD, implica en algunos casos la necesidad de
cambiar las características del cursor. Para esto, es posible sustituir el cursor por uno que no es
sólido o desactivar el efecto parpadeante del mismo, incluso se puede simplemente desactivar el
cursor. Con el fin de realizar cambios de características en el LCD, se usa la función:
64
Lcd_Cmd(char out_char);, en la cual se ingresan los comandos de configuración, por medio del
parámetro: out_char. Para colocar un cursor no sólido, o raya al piso ( _ ), se usa el siguiente
comando: Lcd_Cmd(_LCD_UNDERLINE_ON);, para activar el cursor sólido parpadeante se
implementa el comando: Lcd_Cmd(_LCD_BLINK_CURSOR_ON); Para desactivar el cursor y
hacerlo no visible se puede invocar el comando: Lcd_Cmd(_LCD_CURSOR_OFF);.
5.3 Display LCD gráficos
Los display gráficos, son módulos similares a los LCD de caracteres, su particularidad principal
es que permiten graficar un mapa de bits de mayor resolución. Estos se pueden adquirir
comercialmente en tamaños como: 128x64, 128x128, 240x320, píxeles entre otros. Estos
dispositivos de manera similar a los displays de caracteres requieren un protocolo de
configuración, y para ello cuentan con un bus de datos y un bus de control. Los LCD gráficos,
permiten imprimir fragmentos de líneas, horizontales o verticales armando en conjunto una
imagen total. Para fines de estudio y análisis en este capítulo se demostrará el control sobre un
LCD gráfico de 128x64 píxeles. La apariencia física y la vista desde el simulador ISIS, se puede
apreciar en las siguientes imágenes:



Figura 5-15

Estos dispositivos cuentan con 20 pines de los cuales 8 son el bus de datos, 6 son de control, 2
son de polarización, y 2 son para ajustar el contraste. De la misma forma que el LCD de
caracteres para fines de simulación los pines de contraste son ignorados. Sin embargo para fines
prácticos los pines de contraste se deben instalar con un potenciómetro como se muestra en la
siguiente figura:

Figura 5-16
65
Para los ejemplos tratados en este capítulo, se creará un proyecto nuevo en MikroC PRO, con el
microcontrolador 16F877A. Este dispositivo es un PIC de 40 pines y 8K bytes de memoria de
programa, la cual es suficiente para realizar las primeras practicas con el LCD gráfico.
Nuevamente las bondades del compilador MikroC PRO, brindan una librería especializada en el
uso y control de este display gráfico. Para esto se cuenta con la librería Glcd, ubicada en la paleta
de librerías. Como primera medida para configurar el módulo gráfico, se definen de manera
arbitraria los pines por los cuales se conectará el display. Para esto se hace una declaración de
bits constantes similar a la que se hace con el LCD de caracteres. En el siguiente ejemplo se
puede apreciar está declaración:

// Declaración del puerto con el bus de datos.
char GLCD_DataPort at PORTD;
//Declaración de los pines de control.
sbit GLCD_CS1 at RB0_bit;
sbit GLCD_CS2 at RB1_bit;
sbit GLCD_RS at RB2_bit;
sbit GLCD_RW at RB3_bit;
sbit GLCD_EN at RB4_bit;
sbit GLCD_RST at RB5_bit;
//Declaración de los registros de TRIS de control.
sbit GLCD_CS1_Direction at TRISB0_bit;
sbit GLCD_CS2_Direction at TRISB1_bit;
sbit GLCD_RS_Direction at TRISB2_bit;
sbit GLCD_RW_Direction at TRISB3_bit;
sbit GLCD_EN_Direction at TRISB4_bit;
sbit GLCD_RST_Direction at TRISB5_bit;

Para inicializar el display se usa la función: Glcd_Init(); después de las configuraciones del PIC
dentro de la función main. El paso siguiente es cargar alguna gráfica, para este ejemplo se
dibujará en el display la siguiente imagen:




Figura 5-17


La configuración antes citada se debe respetar en las conexiones físicas con el display gráfico. En
la creación del circuito electrónico en el simulador ISIS, se implementan los dispositivos: PIC
16F877A, y el módulo gráfico: AMPIRE 128x64. Posteriormente se realizan las conexiones
respectivas como se puede apreciar en la siguiente gráfica:

66

Circuito 5-7

Las imágenes que se desean cargar deben ser editadas con anterioridad en algún editor de
imágenes como el Paint de Windows. Estos archivos deben respetar las dimensiones de la
pantalla gráfica en este caso de 128x64 píxeles. Por último el archivo debe ser guardado en
formato bmp, y color monocromático. Para el ingreso del mapa de bits, se puede usar un
herramienta incluida en el MikroC PRO, denominada: GLCD Bitmap Editor, este editor se
ejecuta buscando en el menú principal dentro del ítem Tools, y su submenú GLCD Bitmap Editor.
Al correr está aplicación se debe tener una vista como la siguiente:

Figura 5-18
67
En está aplicación se pica la pestáña: KS0108, y se asegura la selección circular de 128x64
(KS0108). Posteriormente se pulsa el botón Load BMP Picture, y se carga el archivo de imagen
previamente diseñado en el editor de imagen. Seguidamente se selecciona el círculo con la opción
mikroC Code, y se pulsa el botón: Create CODE. Está acción genera el código en lenguaje C,
para agregar al código de programa en el inicio en la parte de la declaración de constantes. Está
declaración es un arreglo de tipo: const unsigned char. Para el caso práctico de este ejemplo el
resultado es el siguiente:

// ------------------------------------------------------
// GLCD Picture name: Imagen.bmp
// GLCD Model: KS0108 128x64
// ------------------------------------------------------
unsigned char const Imagen_bmp[1024] = {
255, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
193,241,193, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,129,129,129,129,129, 65, 65, 65,
65, 65, 65, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
33, 33, 33, 33, 33, 33, 65, 65, 65, 65, 65, 65, 65,129,129,129,
129, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,255,
255, 0, 0, 0, 0, 16, 48,112,112,240,240,240,240,240,248,254,
255,255,255,254,248,240,240,240,240,240,112,112, 48, 16, 0, 0,
0, 0, 0, 0, 0, 0,128, 64, 64, 32, 32, 16, 16, 8, 8, 4,
4, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 2, 2, 2, 4, 4, 8, 8, 8, 16, 16, 32,
64, 64,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0,129,241,255,255,127,127,
63, 31, 63,127,127,255,255,241,129, 0, 0, 0, 0, 0, 0,192,
32, 16, 12, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,192,192,192,192,192,192,192,128,128, 0, 0, 0,192,192,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,192, 64,192,128,192, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 12, 16, 32,192, 0, 0, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0,240,240,240,
240,240,240,240,240, 0, 0, 1, 3, 0, 0, 0, 0,252, 3, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,255,255, 0, 0, 0, 0, 0,129,255,126, 0, 0,254,254,
0, 0,156,190, 54,118,102,238,204, 0, 0,248,252,182, 54, 54,
188,184, 0, 0,254,254, 12, 6, 6, 6,254,252, 0, 0,248,252,
142, 6, 6,142,252,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,248, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,255,255,
255,255,255,255,255, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 16,
96,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 3, 3, 3, 3, 3, 3, 3, 1, 1, 0, 0, 0, 3, 3,
68
0, 0, 1, 3, 3, 3, 3, 3, 1, 0, 0, 0, 1, 3, 3, 3,
1, 0, 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 0, 0, 0, 1,
3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,128, 64, 48, 14, 1, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48,240,240,255,255,255,
255,255,255,255,255,240,240, 48, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 4, 8, 8, 16, 32, 32, 64, 64,128,128, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,128, 64, 64, 32,
32, 16, 8, 8, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 31,255,255,
255,255,255,255, 31, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
30,226, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,128, 64,
64, 64, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 16, 16, 16, 16, 16, 16, 8,
8, 8, 8, 4, 4, 4, 2, 2, 2, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,
255,128,128,128,128,128,128,128,128,128,128,128,128,128,128,131,
159,159,131,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,135,152,136,136,136,132,132,130,130,129,129,129,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,255
};

La declaración anterior contiene codificada la información del mapa de bits cargado en el editor.
La vista final del generador debe ser la siguiente:

Figura 5-19
69
Después de editar, inicializar, y anexar el mapa de bits en el código fuente se tiene la siguiente
fuente total:

// Declaración del puerto con el bus de datos.
char GLCD_DataPort at PORTD;

//Declaración de los pines de control.
sbit GLCD_CS1 at RB0_bit;
sbit GLCD_CS2 at RB1_bit;
sbit GLCD_RS at RB2_bit;
sbit GLCD_RW at RB3_bit;
sbit GLCD_EN at RB4_bit;
sbit GLCD_RST at RB5_bit;

//Declaración de los registros de TRIS de control.
sbit GLCD_CS1_Direction at TRISB0_bit;
sbit GLCD_CS2_Direction at TRISB1_bit;
sbit GLCD_RS_Direction at TRISB2_bit;
sbit GLCD_RW_Direction at TRISB3_bit;
sbit GLCD_EN_Direction at TRISB4_bit;
sbit GLCD_RST_Direction at TRISB5_bit;

unsigned char const Imagen_bmp[1024] = {
255, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
193,241,193, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,129,129,129,129,129, 65, 65, 65,
65, 65, 65, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
33, 33, 33, 33, 33, 33, 65, 65, 65, 65, 65, 65, 65,129,129,129,
129, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,255,
255, 0, 0, 0, 0, 16, 48,112,112,240,240,240,240,240,248,254,
255,255,255,254,248,240,240,240,240,240,112,112, 48, 16, 0, 0,
0, 0, 0, 0, 0, 0,128, 64, 64, 32, 32, 16, 16, 8, 8, 4,
4, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 2, 2, 2, 4, 4, 8, 8, 8, 16, 16, 32,
64, 64,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0,129,241,255,255,127,127,
63, 31, 63,127,127,255,255,241,129, 0, 0, 0, 0, 0, 0,192,
32, 16, 12, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,192,192,192,192,192,192,192,128,128, 0, 0, 0,192,192,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,192, 64,192,128,192, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 12, 16, 32,192, 0, 0, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0,240,240,240,
240,240,240,240,240, 0, 0, 1, 3, 0, 0, 0, 0,252, 3, 0,
70
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,255,255, 0, 0, 0, 0, 0,129,255,126, 0, 0,254,254,
0, 0,156,190, 54,118,102,238,204, 0, 0,248,252,182, 54, 54,
188,184, 0, 0,254,254, 12, 6, 6, 6,254,252, 0, 0,248,252,
142, 6, 6,142,252,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,248, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,255,255,
255,255,255,255,255, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 16,
96,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 3, 3, 3, 3, 3, 3, 3, 1, 1, 0, 0, 0, 3, 3,
0, 0, 1, 3, 3, 3, 3, 3, 1, 0, 0, 0, 1, 3, 3, 3,
1, 0, 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 0, 0, 0, 1,
3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,128, 64, 48, 14, 1, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48,240,240,255,255,255,
255,255,255,255,255,240,240, 48, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 4, 8, 8, 16, 32, 32, 64, 64,128,128, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,128, 64, 64, 32,
32, 16, 8, 8, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0,255,
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 31,255,255,
255,255,255,255, 31, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
30,226, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,128, 64,
64, 64, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 16, 16, 16, 16, 16, 16, 8,
8, 8, 8, 4, 4, 4, 2, 2, 2, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,
255,128,128,128,128,128,128,128,128,128,128,128,128,128,128,131,
159,159,131,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,135,152,136,136,136,132,132,130,130,129,129,129,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,255
};

void main( void )
{
Glcd_Init(); //Inicialización del display gráfico.
Glcd_Fill(0);//Borra todo el display.
Glcd_Image(Imagen_bmp); //Graficación del mapa de bits.
while(1)//Bucle infinito.
{
}
}
71
La función: Glcd_Fill(0);, rellena el contenido de la pantalla con datos binarios iguales a 0, este
proceso equivale a borrar toda la pantalla del LCD. Finalizada la creación del código, y
compilado el proyecto la simulación en ISIS debe mostrar una vista como la siguiente:


Figura 5-20

La carga de mapas de bits, solo está limitada por el tamaño de las mismas, es decir que se pueden
anexar tantas imágenes como la memoria de programa del PIC, soporte. Si se requiere anexar
más imágenes y la memoria ya no es suficiente, se debe cambiar el microcontrolador por uno que
cuente con mas campo de memoria de programa, por ejemplo el PIC 18F452, este tiene la misma
distribución de pines del PIC 16F877A, pero cuenta con una memoria superior y puede procesar
su unidad hasta 40MHz, el PIC 16F877A se puede desempeñar hasta 20MHz.

Además de cargar mapas de bits, la librería del MikroC PRO, permite dibujar líneas, círculos, y
rectángulos. Por otra parte permite cargar una o más fuentes de texto previamente creadas para la
impresión de datos. Lamentablemente, el compilador MikroC PRO, no cuenta con un editor de
fuentes para este fin, pero es posible diseñarlas o incluso cargarlas de las fuentes convencionales
del sistema operativo Windows. Para lograr este cometido se requiere un editor de fuentes para
GLCD, como el software de la siguiente imagen:

Figura 5-21
72
Sin embargo la librería del MikroC PRO, contiene las siguientes fuentes de texto
predeterminadas:

- System3x5, definida como: Font_Glcd_System3x5
- FontSystem5x7_v2, definida como: Font_Glcd_System5x7
- font5x7, definida como: Font_Glcd_5x7
- Character8x7, definida como: Font_Glcd_Character8x7

Estás fuentes, en conjunto con las capacidades de graficación y la importación de mapas de bits
hacen de los módulos LCD gráficos, una poderosa herramienta para dar una presentación
profesional a los proyectos requeridos por el desarrollador.

Para el siguiente ejemplo se usarán las opciones de graficación por medio de puntos, líneas,
círculos, rectángulos, e impresión de textos.

Para hacer la impresión de puntos se implementa la función:
Glcd_Dot( unsigned short x_pos, unsigned short y_pos, unsigned short color );, está función
imprime píxeles, con el color especificado en los parámetros de la función. El parámetro color
puede valer: 0 para hacer un píxel de color blanco, 1 para hacer un píxel de color negro, o 2 para
invertir el color sobre el píxel dibujado.

Para dibujar líneas se utiliza la función:
Glcd_Line(int x_start, int y_start, int x_end, int y_end, unsigned short color);, está función
contempla como parámetros de entrada las coordenadas de inicio y final así como el parámetro
color este puede valer: 0 para hacer la línea de color blanco, 1 para hacer la línea de color negro,
o 2 para invertir el color sobre el cual se dibuja la línea.

La impresión de rectángulos se logra con la función:
Glcd_Rectangle(unsigned short x_upper_left, unsigned short y_upper_left, unsigned short
x_bottom_right, unsigned short y_bottom_right, unsigned short color);, Está función trabaja de
forma similar a la función de graficación de líneas, la diferencia radica en que las coordenadas
ingresadas son los puntos diagonales del rectángulo.

La impresión de rectángulos con relleno es posible por medio de la siguiente función:
Glcd_Box(unsigned short x_upper_left, unsigned short y_upper_left, unsigned short
x_bottom_right, unsigned short y_bottom_right, unsigned short color); Su funcionamiento es
equivalente a la función Glcd_Rectangle, con la diferencia en el parámetro del color que es
correspondiente a color de relleno.

La graficación de círculos se realiza por medio de la función:
Glcd_Circle(int x_center, int y_center, int radius, unsigned short color);, está función respeta
las mismas condiciones del parámetro del color de las anteriores funciones, tiene las coordenadas
del centro del circulo y el radio del mismo.

Para demostrar el funcionamiento de estás funciones observe y analice el siguiente ejemplo:

void main( void )
{
73
Glcd_Init(); //Inicialización del display gráfico.
Glcd_Fill(0); //Borra el display
Glcd_Set_Font( Font_Glcd_5x7 , 5, 7, 32);// Carga fuente de texto 5x7
delay_ms(500); //Retardo de 500ms
Glcd_Dot(4,4,1); //Se dibuja un píxel en la coordenada (4,4)
delay_ms(500); //Retardo de 500ms
Glcd_Line(6,6,30,30,1); //Dibuja una línea de (6,6) a (30,30)
delay_ms(500); //Retardo de 500ms
Glcd_Box(40,5,70,35,2);//Rectángulo relleno entre (40,5) y (70,35)
delay_ms(500); //Retardo de 500ms
Glcd_Circle(64, 32, 20, 2);// Circulo con centro en (64,32) y radio de 20
Glcd_Write_Text( "Hola mundo!", 3, 7, 1); //Impresión del texto en las coordenadas (3,7)

while(1)//Bucle infinito.
{
}
}

Lamentablemente los dispositivos LCD gráficos de Proteus tienen una falla de diseño que hace
que la simulación no funcione correctamente, este es un problema que se espera que sea
corregido en versiones futuras de ISIS. Sin embargo los resultados de la simulación son muy
cercanos al comportamiento real del dispositivo de graficación. El resultado en ISIS, sobre la
simulación y una vista del comportamiento real sobre el LCD gráfico, se pueden apreciar en las
siguientes figuras:


Figura 5-22
74
6 Teclados y sistemas de
entrada de datos
La interacción de los microcontrolados requiere de sistemas de entrada para los datos con el
usuario. Para este propósito se pueden usar dispositivos como pulsadores, teclados matriciales, o
paralelos, e incluso teclados PS2 como los que usan los ordenadores de escritorio. Este capítulo
se centra en el estudio de estos dispositivos.
6.1 Uso de pulsadores
La implementación de pulsadores es una de las alternativas de mayor popularidad en las
interfaces de acción con los usuarios. Los pulsadores son de uso simple y de costo económico en
la implementación. Los pulsadores pueden ser normalmente abiertos o normalmente cerrados al
efecto eléctrico ante el flujo de corriente. La implementación de estos dispositivos es propensa a
los efectos de los rebotes o ruidos de tensión eléctrica cuando cambian de estádo. Dado el veloz
procesamiento de los microcontroladores estos efectos de ruido, hacen que el PIC pueda detectar
cambios o estádos lógicos no definidos. Para comprender este concepto observe la siguiente
gráfica que muestra el comportamiento de este ruido:


Figura 6-1

Cuando los cambios de tensión cruzan la zona indefinida, generan cambios de alto a bajo y
viceversa que el microcontrolador detecta como un pulso. Para evitar el efecto del ruido o rebotes
se debe realizar una pausa en espera de la estábilidad del estádo lógico. La duración promedio de
un rebote es de 1 a 5 milisegundos, lo que indica que se debe hacer un retardo superior a este
tiempo para esperar la estábilidad. Un retardo adecuado para este efecto es un tiempo mayor o
igual a 10 milisegundos. Este retardo se debe aplicar después de detectar el primer cambio sobre
el pulsador.
Para el tratamiento de los pulsadores y la eliminación de los rebotes el compilador MikroC PRO
cuenta con la librería Button, que se puede encontrar en la paleta o pestáña de librerías. Está
librería tiene la única función: unsigned short Button(unsigned short *port, unsigned short pin,
unsigned short time, unsigned short active_state);, Donde port es el puerto donde se conecta el
pulsador, pin es el bit del puerto donde se conecta el pulsador, time es el tiempo en milisegundo
de espera del ruido, y active_state, es el estádo lógico para el cual se desea hacer la activación del
pulsador. La función retorna 0 si el pulsador no está activo y 255 si el mismo está activo. La
instalación de los pulsadores se puede hacer de dos formas, activo en alto o activo en bajo, en la
siguiente figura se puede apreciar la forma de configurar estás dos posibilidades:
75

Circuito 6-1
La decisión de usar la activación en alto o en bajo, depende del desarrollador quien debe analizar
la forma de mayor simplicidad en el momento de hacer el diseño. Cabe recordar que de todos
modos la activación final puede ser invertida con la función de la librería. Para analizar y estudiar
el uso de está librería se creará un nuevo proyecto con los siguientes dispositivos: PIC 16F877A,
BUTTON, RES, y LED-RED. El circuito correspondiente en ISIS, es el siguiente:

Circuito 6-2

El programa del microcontrolador respectivo es el siguiente:

void main( void )
{
//Configuración de puertos.
TRISB=0xF0;
PORTB=0;
while(1)//Bucle infinito.
76
{
if( Button(&PORTB, 7, 100, 0) )//Evalúa el estádo del pulsador por RB7,
//activado en bajo.
PORTB.F0=1; //Prende el LED si el pulsador está activo.
else
PORTB.F0=0; //Apaga el pulsador si el pulsador está no activo.
}
}

Al correr está simulación el LED debe prender cuando se pulsa el botón, y apagarse cuando se
suelta. El siguiente ejemplo conmuta el estádo del LED, para este se evalúa el estádo del pulsador
y se espera que se suelte el mismo:

void main( void )
{
//Configuración de puertos.
TRISB=0xF0;
PORTB=0;
while(1)//Bucle infinito.
{
if( Button(&PORTB, 7, 100, 0) )//Evalúa el estádo activo del pulsador.
{
if( PORTB.F0==1 )// Se conmuta el estádo del LED.
PORTB.F0=0;
else
PORTB.F0=1;
//Se espera a que el pulsador este no activo.
while( Button(&PORTB, 7, 100, 0) );
}
}
}
6.2 Uso de Dip-Switch
Los Dip-Switch, son dispositivos mecánicos que contienen múltiples interruptores en un solo
encapsulado. Estos dispositivos permiten configurar de forma simple las características binarias
de los sistemas microcontrolados. Los Dip-Switch, se consiguen comercialmente en tamaños,
colores, y número de interruptores diferentes. La apariencia física y la vista en el simulador ISIS,
de estos dispositivos es la siguiente:


Figura 6-2
77
El uso de los Dip-Switch es similar a los pulsadores y se puede configurar de la misma forma que
un pulsador con activación en alto o en bajo. Las configuraciones antes citadas se pueden
apreciar en el siguiente circuito:


Circuito 6-3


Para comprender y demostrar el uso de los Dip-Switch con los microcontroladores PIC, observe y
analice el siguiente ejemplo:


void main( void )
{
//Configuración de puertos.
TRISB = 0;
PORTB = 0;
TRISC = 255;
while(1) //Bucle infinito.
{
//Se guarda el valor complementado del puerto C en el puerto B.
PORTB = ~PORTC;
}
}


Para ejecutar la simulación en ISIS, se usan los siguientes dispositivos: 16F877A, RES, LED-
RED, DIPSW_8. Seguidamente se implementa el siguiente circuito en ISIS:

78

Circuito 6-4

Al correr la simulación los LEDs muestran el estádo del Dip-Switch.
6.3 Uso de teclados matriciales
Las aplicaciones con microcontroladores, requieren en algunos casos el uso de teclas de entrada
de datos, para datos numéricos, funciones e incluso caracteres de texto. La opción me mayor
practicidad es el uso de teclados matriciales, estos consisten en un arreglos de pulsadores
alineados en filas y columnas, minimizando el número de conexiones eléctricas. En las siguientes
imágenes se puede ver la apariencia física de un teclado matricial de 4x4 y su equivalente
esquemático:

Figura 6-3
79
Los teclados matriciales pueden tener dimensiones mayores en función de la necesidad del
desarrollador. Los teclados especiales pueden ser fabricados con membranas plásticas con la
cantidad de teclas y la distribución de las mismas, de tal manera que suplan las necesidades del
usuario. Sin embargo los teclados 4x4 permiten hacer una interfaz lo suficientemente completa
para muchas de las aplicaciones. Los teclados matriciales funcionan activando una de 4 columnas
y revisando cual de las filas se activan, este proceso determina cual es la tecla pulsada, de igual
manera el análisis se puede hacer invirtiendo las columnas con las filas. El compilador MikroC
PRO, contiene una librería que controla y lee un teclado de 4x4, está librería cuenta con tres
funciones para este propósito. La librería que permite el uso de teclados 4x4 es: Keypad4x4, y las
funciones que usa son: Keypad_Init(void);, está inicializa el uso del teclado en función del
puerto designado para este fin. La función: char Keypad_Key_Press(void);, Está función, retorna
un valor de 0 a 16, don de 0 representa el teclado totalmente inactivo, los resultados del 1 al 16
representan las 16 posibles teclas. Está función retorna inmediatamente el estádo del teclado cada
vez que es invocada. La última función de la librería para el teclado matricial es: char
Keypad_Key_Click(void);, Su comportamiento es idéntico a la función anterior, su diferencia
vital es que si la función detecta una tecla pulsada, solo retorna su valor hasta que está tecla es
soltada.
Para estudiar las características de este tipo de teclados, se mostrará un ejemplo con un nuevo
proyecto en ISIS, que requiere de los siguientes dispositivos: PIC 16F877A, RES, BUTTON,
LM016L, este último es un display de caracteres de 2x16. De la misma forma se debe crear el
proyecto nuevo en el compilador MikroC PRO.
Para iniciar el proyecto se debe construir un circuito en ISIS, como el visualizado en la siguiente
figura:

Circuito 6-5
80
El programa diseñado en MikroC PRO, leerá el teclado y mostrará en el LCD, el valor retornado
por el mismo. Para este fin observe y analice el siguiente ejemplo:

//Declaración del puerto para el teclado 4x4
char keypadPort at PORTC;
//Definición pines para el LCD.
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;

//Definición de registros TRIS para el LCD.
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;


void main( void )
{
//Declaración de variables.
unsigned short Tecla;
char Text[20];
//Configuración e inicialización del PIC.
Lcd_Init(); //Inicialización del LCD.
Lcd_Cmd(_LCD_CURSOR_OFF); //Se apaga el cursor.
Lcd_Out(1, 1,"Tecla:"); //Se imprime texto.
while(1)//Bucle infinito.
{
Tecla=Keypad_Key_Press(); //Se lee el teclado y se guarda el resultado en Tecla.
ByteToStr(Tecla, Text); //Conversión de entero a texto.
Lcd_Out(2,1,Text); //Visualización del valor retornado por el teclado.
}
}


Al correr la simulación de este programa en la pantalla se pueden ver los valores retornados por
el teclado. En la siguiente figura se puede apreciar un a vista de la simulación cuando se pulsa la
tecla rotulada con el número 8:

81

Circuito 6-6

Para el caso puntual de este ejemplo el teclado hace un barrido de izquierda a derecha y de arriba
hacia abajo, empezando con la tecla 1, y terminando con la tecla D. Esto significa que los
retornos de las funciones de lectura para el teclado son los siguientes datos con respecto a cada
una de las teclas:

Retorno de las funciones Equivalencia
0 Ninguna tecla pulsada
1 Tecla 1
2 Tecla 2
3 Tecla 3
4 Tecla A
5 Tecla 4
6 Tecla 5
7 Tecla 6
8 Tecla B
9 Tecla 7
10 Tecla 8
11 Tecla 9
12 Tecla C
13 Tecla *
14 Tecla 0
15 Tecla #
16 Tecla D

Tabla 6-1
82
Hasta este punto se pueden leer del teclado una serie de números pero estos no son equivalentes a
la tecla pulsada, para corregir este aspecto se debe implementar una función que decodifique los
valores entregados por el teclado y los convierta en el verdadero carácter que cada una de las
teclas representa. Para este fin observe la siguiente función que se hace por medio de una
estructura switch case para decodificar el teclado.

//Función para decodificar el teclado.
char LeerTeclado( void )
{
//Estructura switch case para evaluar los valores retornados
//por la librería del teclado.
switch(Keypad_Key_Press() )
{
case 1: return „1‟;
case 2: return „2‟;
case 3: return „3‟;
case 4: return „A‟;
case 5: return „4‟;
case 6: return „5‟;
case 7: return „6‟;
case 8: return „B‟;
case 9: return „7‟;
case 10: return „8‟;
case 11: return „9‟;
case 12: return „C‟;
case 13: return „*‟;
case 14: return „0‟;
case 15: return „#‟;
case 16: return „D‟;
default: return 0; //Tecla no pulsada.
}
}

El programa final de está aplicación es el siguiente:

//Declaración del puerto para el teclado 4x4
char keypadPort at PORTC;

//Definicion pines para el LCD.
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;

//Definicion de registros TRIS para el LCD.
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
83
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;

//Función para decodificar el teclado.
char LeerTeclado( void )
{
//Estructura switch case para evaluar los valores retornados
//por la librería del teclado.
switch( Keypad_Key_Press() )
{
case 1: return „1‟;
case 2: return „2‟;
case 3: return „3‟;
case 4: return „A‟;
case 5: return „4‟;
case 6: return „5‟;
case 7: return „6‟;
case 8: return „B‟;
case 9: return „7‟;
case 10: return „8‟;
case 11: return „9‟;
case 12: return „C‟;
case 13: return „*‟;
case 14: return „0‟;
case 15: return „#‟;
case 16: return „D‟;
default: return 0; //Tecla no pulsada.
}
}

void main( void )
{
//Declaración de variables.
char Tecla;
//Configuración e inicialización del PIC.
Lcd_Init(); //Inicializa el LCD.
Lcd_Cmd(_LCD_CURSOR_OFF); //Se apaga el cursor.
Lcd_Out(1, 1,"Tecla:"); //Se imprime texto.
while(1)//Bucle infinito.
{
Tecla=LeerTeclado(); //Se lee el teclado y su resultado se guarda en Tecla.
Lcd_Chr(2,1,Tecla); //Visualización el valor retornado por el teclado.
}
}

Una vez editado, compilado y simulado este programa, se debe ver en la simulación del display
LCD, el carácter correspondiente a la tecla que se está pulsando sobre el teclado.
84
6.4 Uso de teclados PS2 o Din
Los teclados PS2 o Din son de uso común en los ordenadores de escritorio, e incluso existen
algunos que son flexibles, y de fácil transporte. El compilador MikroC PRO, posee una librería
especializada en el uso de estos teclados, lamentablemente estos teclados no son simuladles con
el programa ISIS. Por está razón el ejemplo que se expone en este capítulo debe ser probado con
los elementos de forma real. La apariencia física de estos teclados es la siguiente:

Figura 6-4

La lectura de estos teclados es hace por medio de un protocolo serial que la librería ya tiene
implementada. Los teclados cuentan con un terminal PS2 o DIN, en los cuales se pueden
identificar 4 pines fundamentales, el Vcc o poder, el Gnd o referencia, un pin de datos seriales, y
un pin de reloj. La comunicación de estos dispositivos es de forma síncrona, lo que hace
necesario la señal de reloj.
La distribución de pines de estos terminales es la que se puede apreciar en la siguiente gráfica:


Figura 6-5

En la siguiente tabla se puede apreciar la distribución de pines de la figura anterior:

Pin PS2 Finalidad Pin DIN
1 Datos 2
3 Referencia GND 4
4 Vcc, +5V 5
5 Reloj 1

Tabla 6-2
La implementación de este tipo de teclados se hace por medio de dos funciones contenidas en la
librería PS2, de MikroC PRO. La primera función es: Ps2_Config();, Está función debe ser
invocada en la configuración del PIC, dentro de la función main, la función configura e inicializa
el teclado para poder ser leído en función de los pines que sean definidos con anterioridad como
pin de reloj y pin de datos. La otra función usada es: unsigned short Ps2_Key_Read(unsigned
short *value, unsigned short *special, unsigned short *pressed);, está función cuenta con un
parámetro de salida que retorna 1, cuando una tecla es leída con éxito, y retorna 0, cuando no se
85
detecta ninguna tecla pulsada. La función posee tres parámetros de entrada que permiten
identificar las siguientes características: value, este es un apuntador a una variable de tipo
unsigned short en la cual se guarda el valor de la tecla pulsada en código ASCII.
El parámetro special, es un apuntador a una variable de tipo unsigned short que especifica si la
tecla que se está pulsando es de origen especial como: F1, F2, ESC, ENTER, etc. Si está variable
retorna un 1, significa que el valor codificado en value, no es un código ASCII, si no un código
asignado a la tecla especial que está regido por la siguiente tabla:
Tecla pulsada Retorno en value
F1 1
F2 2
F3 3
F4 4
F5 5
F6 6
F7 7
F8 8
F9 9
F10 10
F11 11
F12 12
Enter 13
Page Up 14
Page Down 15
Backspace 16
Insert 17
Delete 18
Windows 19
Ctrl 20
Shift 21
Alt 22
Print Screen 23
Pause 24
Caps Lock 25
End 26
Home 27
Scroll Lock 28
Num Lock 29
Left Arrow 30
Right Arrow 31
Up Arrow 32
Down Arrow 33
Escape 34
Tab 35

Tabla 6-3
86
Si el valor retornado es 0 el código consignado en value es un carácter ASCII.
Por último el parámetro: pressed, determina si la tecla denotado por los anteriores parámetros es
pulsada o soltada, si el valor retornado en pressed, es 1 la tecla fue pulsada, si retorna 0 la tecla
fue soltada.
Para el próximo ejemplo se debe tener presente el siguiente circuito electrónico de prueba:

Circuito 6-7

El código de programa para este ejercicio es el siguiente, observe y analice:

//Definición de pines para el teclado PS2.
sbit PS2_Data at RC2_bit;
sbit PS2_Clock at RC3_bit;
sbit PS2_Data_Direction at TRISC2_bit;
sbit PS2_Clock_Direction at TRISC3_bit;

//Definición pines para el LCD.
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;

87
//Definición de registros TRIS para el LCD.
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;

void main( void )
{
//Declaración de variables.
unsigned short Tecla = 0, Especial = 0, Pulsada_soltada = 0;

//Configuración e inicialización del PIC.
Lcd_Init(); //Inicializa el LCD.
Lcd_Cmd(_LCD_CURSOR_OFF); //Se apaga el cursor.
Lcd_Out(1, 1,"Tecla:"); //Se imprime texto.
while(1)//Bucle infinito.
{
//Este ciclo while espera a que una tecla sea activada.
while( Ps2_Key_Read( &Tecla, &Especial, &Pulsada_soltada)==0 );

//La sentencia if evalúa el estádo de Pulsada y muestra en el LCD
//el resultado.
if( Pulsada_soltada==1 )
Lcd_Out(1, 7,"Pulsada");
else
Lcd_Out(1, 7,"Soltada");

//La sentencia if evalúa el estádo de Especial y muestra
//su resultado en el LCD.
if( Especial==1 )
Lcd_Out(2, 3,"Especial");
else
Lcd_Out(2, 3,"ASCII ");

//Visualización el valor retornado por el teclado.
Lcd_Chr(2,1,Tecla);
}
}

Después de realizar la prueba de este ejemplo en display LCD, mostrar los datos enviados por el
teclado, y el estádo de las teclas especiales.
88
7 Comunicaciones seriales
Los desarrollos con microcontroladores requieren en algunos casos la implementación de
comunicaciones seriales para establecer transporte de datos con otros dispositivos tales como:
memorias, sensores, ordenadores, e incluso otros microcontroladores. Con el fin de realizar las
comunicaciones seriales, algunos microcontroladores cuentan con módulos seriales como: I²C,
SPI, USART, y USB. Cada uno de estos formatos de comunicación, permiten establecer
comunicaciones con módulos diferentes. El módulo I²C, es ideal para la comunicación con
memorias seriales como la 24LC64, 24LC128, 24LC512, entre otras. El protocolo SPI, permite
establecer comunicaciones con unidades de almacenamiento masivo como las memorias SD. El
módulo USART es uno de los más utilizados; este módulo permite hacer comunicaciones con
dispositivos como sensores, módulos de transmisión y recepción XBee, ordenadores personales,
módulos GPS, y otros micros. Por último el módulo USB, que está incorporado en unos pocos
micros, como el 18F2550, y el 18F4550, permite hacer la comunicación con un ordenador
personal por medio de un puerto USB, definido como HID o dispositivo de interfaz humana.
7.1 Modulo serial I²C
Algunos microcontroladores poseen este módulo y de la misma forma el compilador MikroC
PRO cuenta con una librería especializada para el uso fácil de este protocolo. La librería se
reconoce como I2C. Está librería cuenta con siete funciones que lideran el funcionamiento del
módulo. El protocolo I²C, se caracteriza por tener una línea de datos bidireccional y una línea de
reloj con drenador abierto. Por este motivo en estos dos pines se debe usar una resistencia de
pull-up, generalmente de 10KΩ. Este usa una condición de inicio, un bloque de dirección para los
dispositivos en la red, un bloque de datos y una condición de fin. De igual manera usa las
condiciones de repetición de inicio y un bit de reconocimiento o acknowledge denotado como
ACK. El protocolo I²C se puede usar para conectar diferentes dispositivos con direcciones
diferentes en una topología en forma de bus. Por último este protocolo debe tener una velocidad
de comunicación que está dada en bits por segundo.

La librería de MikroC PRO, cuenta con las siguientes funciones que hacen posible el dominio del
protocolo I²C:

La función: I2C1_Init(const unsigned long clock);, inicializa los pines de comunicación en el
PIC, y ajusta la velocidad de comunicación a la rata que denote el parámetro clock.
La función: unsigned short I2C1_Start(void);, está función genera la condición de inicio y
retorna 0 si no hay ningún error en la transmisión, de otro forma retorna un valor diferente de 0.
La función: void I2C1_Repeated_Start(void);, está función genera la repetición de inicio.
La función: unsigned short I2C1_Is_Idle(void);, está función se usa para identificar si el bus de
datos está ocupado, y retorna 1, si el bus está disponible o retorna 0, si está ocupado.
La función: unsigned short I2C1_Rd(unsigned short ack);, Está función lee un dato del bus de
datos y envía la confirmación ack por el bus. Si ack vale 1 la confirmación es positiva y si es 0 la
confirmación es negativa o NO ACK.
89
La función: unsigned short I2C1_Wr(unsigned short data );, está función envía el dato data por
el bus y retorna 0 si la transmisión fue exitosa, o algo diferente de 0 si suceden errores.
La función: void I2C1_Stop(void);, Genera la condición de final en el bus de comunicación.
Para entender y estudiar el funcionamiento de este módulo, el siguiente ejemplo se concentra en
la lectura y escritura de una memoria serial 24LC00, que tiene una capacidad de 16 direcciones.
En las siguientes gráficas se puede apreciar el protocolo de comunicación con la memoria
24LC00 para ser leída y escrita:



Protocolo de escritura


Figura 7-1


Protocolo de lectura

Figura 7-2


Para probar el ejemplo se debe implementar en ISIS, un circuito con los dispositivos: 16F877A,
BUTTON, RES, LED-RED, 24LC00, DIPSWC_8, DIPSWC_4, y el instrumento de análisis del
protocolo I2CDEBUGGER, este último se puede buscar en la paleta de herramientas ubicada en
la parte izquierda del programa MikroC PRO, en el icono de instrumentos: .



El circuito para armar es el siguiente:

90

Circuito 7-1

El código de programa correspondiente para está simulación es el siguiente:

void main( void )
{
//Declaración de variables.
unsigned short DIRECCION;
unsigned short DATO;
//Configuración de puertos.
ADCON1 = 6;
TRISE = 7;
TRISB = 0;
PORTB = 0;
//Inicio del módulo I2C a 100K bps.
I2C1_Init(100000);
while(1) //Bucle infinito.
{
DIRECCION = (~PORTA)&0x0F; //Captura de la dirección.
//Protocolo de lectura de la dirección DIRECCION.
I2C1_Start();
I2C1_Wr(0b10100000);
I2C1_Wr(DIRECCION);
I2C1_Repeated_Start();
I2C1_Wr(0b10100001);
DATO=I2C1_Rd(0);
91
I2C1_Stop();
//Visualización del DATO en el puerto B.
PORTB = DATO;
delay_ms(100); //Retardo de 100 ms para graficar.

//Sentencia if para evaluar si se pulsa el botón de grabar.
if( Button( &PORTA, 4, 50, 0) )
{
DATO = ~PORTD; //Captura del valor del dato.
//Protocolo de grabación.
I2C1_Start();
I2C1_Wr(0b10100000);
I2C1_Wr(DIRECCION);
I2C1_Wr(DATO);
I2C1_Stop();
//Retardo para esperar que la memoria grabe el dato.
delay_ms(50);
}
}
}


Terminada la edición y la simulación en ISIS, se puede apreciar los valores consignados en la
dirección que indica el DIP-SWITCH DIRECCION, sobre los LEDs. Cuando el botón GRABAR
es pulsado, el valor del DIP-SWITCH, DATOS es grabado en la dirección actual. Los pines de
Reloj, y Datos para este PIC, ya están predefinidos por el fabricante por los pines RC3, y RC4
respectivamente.
7.2 Módulo USART
La USART, es un módulo de comunicación serial estándar, de forma asíncrona, está
característica lo hace muy apetecido dado que requiere un solo medio de transmisión para enviar
información, y no requiere un medio para el reloj. La señal de reloj, o sincronismo lo deben
asumir, independiente mente cada uno de los elementos, el transmisor y el receptor. Otra ventaja
de este módulo es que cuenta con comunicación full-duplex, es decir que puede transmitir y
recibir información al mismo tiempo. Para este propósito se usan dos medios de transmisión
dedicados, uno solo para transmitir y uno solo para recibir.

La comunicación síncrona cuenta con las siguientes características: un bit de inicio o de start, que
siempre es un 0 lógico, 8 o 9 bits de datos para el caso puntual de los PIC, y 1, 1.5 o 2 bits de fin
o stop. Por último la velocidad de transmisión que debe estár definida con el mismo valor en los
dos dispositivos que se comunican, está por defecto en casi toda comunicación es de 9600 bps,
sin embargo esto no es una regla puede ser mayor o menor.

En la siguiente gráfica se puede apreciar el comportamiento de la transmisión de un dato con este
protocolo:

92


Figura 7-3

Este protocolo es utilizado por los ordenadores personales y otros dispositivos, y se conoce como
RS232, para el caso puntual de este los niveles de tensión eléctrica son diferentes al
microcontrolador. El protocolo RS232, representa el valor de un 0 lógico con una tensión de +12
voltios, y el valor de un 1 lógico con -12 voltios. Los ordenadores personales y los demás
dispositivos que implementan el puerto RS232, usan un conector DB9, que tiene 9 pines, los
cuales se identifican en la siguiente figura:



Figura 7-4

Pin Uso
1 DCD
2 RXD
3 TXD
4 DTR
5 GND
6 DSR
7 RTS
8 CTS
9 No usado

Tabla 7-1

Para fines de comunicación asíncrona se debe configurar las conexiones de la siguiente forma:



Figura 7-5
93
Para estáblecer y acoplar la comunicación de un PIC, con un dispositivo RS232, se debe usar un
convertidor conocido como MAX232. La implementación de este, se debe hacer como se aprecia
en la siguiente figura:


Circuito 7-2

Para el uso de este protocolo de comunicación el compilador MikroC PRO, cuenta con la librería:
UART, está disponible en la paleta de librerías y cuenta con las siguientes funciones para su uso:

UART1_Init(const unsigned long baud_rate);, Está función inicializa el módulo USART, y
estáblece la velocidad de comunicación definida en el parámetro: baud_rate.
La función: char UART1_Data_Ready();, está función determina si hay un dato listo para ser
leído, en el búfer de llegada del módulo, si la función retorna 1 el dato puede ser leído, de lo
contrario no hay datos nuevos en el búfer.
La función: char UART1_Tx_Idle();, está función estáblece si el búfer de transmisión se
encuentra ocupado enviando un dato, para esto retorna 1 si el búfer está ocupado, o 0 si el
módulo está disponible para enviar un nuevo dato.
La función: char UART1_Read();, está función retorna el valor del búfer de entrada, es decir que
sirve para leer un dato de entrada.
La función: UART1_Read_Text(char *Output, char *Delimiter, char Attempts);, está función lee
una cadena de caracteres y la guarda en el apuntador Output, el apuntador Delimiter, es un
apuntador a una cadena de caracteres que contiene el texto definido como fin de la cadena de
texto de entrada, el parámetro Attempts, define la longitud de la cadena de caracteres del
delimitador de fin de cadena Delimiter.
La función: UART1_Write(char _data);, transmite el dato _data ingresado en el parámetro de
entrada, por la USART.
94
La función: UART1_Write_Text(char * UART_text);, Transmite una cadena de texto finalizada
con el carácter nulo o 0. Está cadena de texto es entregada por medio del parámetro UART_text.
Para realizar el siguiente ejemplo se debe implementar en ISIS, el circuito con los siguientes
dispositivos: PIC 16F877A, y el instrumento virtual: VIRTUAL TERMINAL. Con estos dos
elementos se construye el siguiente circuito:

Circuito 7-3
El instrumento VIRTUAL TERMINAL, es un simulador de comunicación serial y tiene un
comportamiento similar a la herramienta HiperTerminal de Windows. Este instrumento permite
editar las características de la comunicación como: el número de bits de datos, la velocidad de
transmisión, el número de bits de parada, entre otros. Sin embargo el instrumento está
configurado por defecto con una velocidad de 9600 bps, y características listas para usar con la
USART del PIC.
Para comprobar el funcionamiento de este módulo se puede compilar y simular el siguiente
ejemplo:

void main( void )
{
//Declaración de variables.
char DATO;
UART1_Init(9600); //Inicio del módulo USART.
//Se transmite el texto: de bienvenida.
UART1_Write_Text("Bienvenido al simulador:");
UART1_Write(13); //Se transmite el ASCII del ENTER.
UART1_Write(10); //Se transmite el ASCII del retroceso del carro.
//Se trasmite el texto de pulsar tecla.
UART1_Write_Text("Pulse una tecla!...");
95
UART1_Write(13); //Se transmite el ASCII del ENTER.
UART1_Write(10); //Se transmite el ASCII del retroceso del carro.
while(1)//Bucle infinito.
{
//La sentencia if evalúa si un dato está listo para leer.
if(UART1_Data_Ready()==1)
{
//Se lee el DATO del bufer.
DATO = UART1_Read();
//Se imprime el texto de realimentación.
UART1_Write_Text("Usted pulso la tecla: ");
UART1_Write(DATO); //Se transmite el DATO recibido.
UART1_Write(13); //Se transmite el ASCII del ENTER.
UART1_Write(10); //Se transmite el ASCII del retroceso del carro.
}
}
}

Durante la simulación el usuario puede enviar datos por el terminal virtual y ver la realimentación
que el PIC, emite.
7.3 Módulo USB
La comunicación USB, es de gran utilidad para realizar aplicaciones que impliquen la
transmisión de datos con un ordenador personal. La desventaja más notable de este módulo es
que solo está disponible en pocos microcontroladores como los PIC 18F2550 y 18F4550. Sin
embargo estos microcontroladores tienen características poderosas que los hacen ideales para la
mayoría de las aplicaciones.

En las siguientes gráficas se puede apreciar las terminales de los conectores USB:


Figura 7-6

Pin Uso
1 Vcc +5 Voltios
2 Dato -
3 Dato +
4 Gnd

Tabla 7-2
96
Una ventaja de la conexión USB, es que puede entregar una alimentación de 5 voltios con una
corriente máxima de 500mA, esto significa que si el desarrollo no supera este consumo, no es
necesario implementar una fuente de poder externa.

El paso siguiente es realizar un circuito en ISIS, que permita correr la simulación del ejemplo.
Para este caso se usa un PIC 18F2550, para este fin se requiere este dispositivo, el puerto virtual
USBCONN, un LED LED-RED, y RES. Para usar este tipo de simulación se debe asegurar la
instalación de los controladores USB, del simulador ISIS. Para hacer está instalación se busca el
directorio de instalación de Proteus, y correr la instalación de los controladores. La ubicación por
defecto de estos controladores es la siguiente:
C:/labcenter Electronics/Proteus 7 Professional/USB Drivers/installer.exe
Posteriormente se debe hacer el siguiente circuito:


Circuito 7-4

Para fines prácticos en el terminal VUSB, se debe instalar un capacitor de 33uF conectado a
referencia. Y se debe usar un cristal de cuarzo adecuado a las características de diseño. La opción
de edición para los fusibles del PIC 18F2550, es de suma complejidad en comparación con los
micros de baja gama. La configuración se edita picando el menú: proyect en el compilador
MikroC PRO, y dentro de este menú se pica el submenú, Edit Proyect… está acción despliega
una ventana de edición la cual debe quedar de la siguiente forma:



97


Figura 7-7

98
Para fines prácticos se debe asegurar en la simulación una fuente de reloj de 48Mhz y en un
circuito real se usa un cristal de 4Mhz, esto se debe a que el microcontrolador posee un PLL
interno que multiplica la frecuencia.

El código fuente de MikroC PRO, es el siguiente:

//Declaración de variables globales
//como búfer de entrada y salida en el USB.
unsigned short Br[64];
unsigned short Bw[64];

//Función de rutina USB
//dentro de las interrupciones.
void HID_Inte( void )
{
asm CALL _Hid_InterruptProc
asm nop
}

//Función de interrupciones.
void interrupt( void )
{
HID_Inte();
}

void main( void )
{
//Declaración de variables.
unsigned short n;
char DATO;
//Configuración del PIC, e interrupciones.
INTCON = 0;
INTCON2 = 0xF5;
INTCON3 = 0xC0;
RCON.IPEN = 0;
PIE1 = 0;
PIE2 = 0;
PIR1 = 0;
PIR2 = 0;
//Configuración de puertos.
TRISB = 0;
PORTB = 0;

Hid_Enable(&Br,&Bw);//Función de inicio para el puerto USB.
//Ciclo for para inicializar el búfer en 0.
for(n=0; n<64; n++)Bw[n]=0;

while(1) //Bucle infinito.
{
99
//Función para verificar los datos de entrada.
n=Hid_Read();
//Sentencia if para verificar el número bytes de entrada.
if( n!=0 )
{
PORTB = 255; //Se prende todo el puerto B
DATO = Br[0]; //Se lee el dato de entrada.
//Se copia el texto, en el búfer de salida.
strcpy( Bw, "Se recibio el dato: " );
//Se copia el dato de entrada en el búfer.
Bw[20]=DATO;
//Se anexa el código ASCCI del ENTER en el búfer de salida.
Bw[21]=13;
//Se anexa el código ASCCI del retroceso del carro en el búfer de salida.
Bw[22]=10;
//Se envían los datos por el USB.
HID_Write(Bw,64);
delay_ms(100); //Retardo para el parpadeo del LED.
PORTB = 0; //Se apaga el puerto B.
}
}
}

La configuración de los proyectos que usen la librería USB, es tediosa pero finalmente solo
recurren a cuatro funciones para su uso. Estás funciones son las siguientes:

Hid_Enable(unsigned *readbuff, unsigned *writebuff);, está función configura el módulo USB,
y estáblece la comunicación con la computadora. Incluye los parámetros de entrada readbuff y
writebuff, que son apuntadores a los arreglos de memoria que guardan y transmiten la
información por el puerto USB.
La función: unsigned char Hid_Read(void);, está función retorna la cantidad de datos listos para
ser leídos del puerto USB.
La función: unsigned short Hid_Write(unsigned *writebuff, unsigned short len);, está función
escribe en el puerto USB, la cantidad de bytes definidos en el parámetro len, que están
consignados en el apuntador writebuff.
La función: void Hid_Disable(void);, está función deshabilita el uso del puerto USB.
Para verificar el funcionamiento de este ejemplo se debe correr la simulación y esperar a que el
dispositivo USB, se instale automáticamente, dado que es un dispositivo de interfaz humana,
compatible con Windows. Para enviar datos a la simulación se requiere correr una aplicación que
viene con las herramientas de MikroC PRO. Está herramienta se encuentra en Tools, y se pica el
submenú, HID Terminal. Para garantizar la correcta compilación del proyecto, y el uso de la
librería USB, se requieren tres archivos adicionales: USBdsc.c, Definit.h, y VARs.h. El archivo
USBdsc.c contiene las definiciones con las que el ordenador reconoce el dispositivo HID. Tiene
parámetros como el nombre del fabricante, el nombre del producto, los identificadores numéricos
de fabricante y producto: VIP y PID. También contiene el tamaño de los búfer de entrada y
100
salida. El HID Terminal cuenta con una pestáña descriptor. En está pestáña se editan las
características del dispositivo HID, y luego se pulsa el botón Save Descriptor. Está acción
permite guardar el archivo que deben ser archivados en el directorio donde se guarda todo el
proyecto, además debe ser anexado al proyecto por medio del siguiente icono: de la misma
forma se deben pegar en el directorio los archivos: Definit.h, y VARs.h, estos no requieren ser
anexados solo deben ser pegados. Estos archivos contienen el siguiente código:

Definit.h:

#defineEP0_PACKET_SIZE 8 // EP0 In & Out transfer size
#defineHID_PACKET_SIZE 64 // EP1 In & Out transfer size

//******************************************
//
// Definitions
//
//********************************************
#defineUSB_BUS_ATTACHED 1
#defineUSB_BUS_DETACHED 0

#defineUSB_DEVICE_DESCRIPTOR_LEN 0x12
#defineUSB_CONFIG_DESCRIPTOR_LEN 0x09
#defineUSB_INTERF_DESCRIPTOR_LEN 0x09
#defineUSB_ENDP_DESCRIPTOR_LEN 0x07
#defineUSB_HID_DESCRIPTOR_LEN 0x09

#defineUSB_DEVICE_DESCRIPTOR_TYPE 0x01
#defineUSB_CONFIG_DESCRIPTOR_TYPE 0x02
#defineUSB_STRING_DESCRIPTOR_TYPE 0x03
#defineUSB_INTERFACE_DESCRIPTOR_TYPE 0x04
#defineUSB_ENDPOINT_DESCRIPTOR_TYPE 0x05
#defineUSB_POWER_DESCRIPTOR_TYPE 0x06
#defineUSB_HID_DESCRIPTOR_TYPE 0x21

#defineUSB_ENDPOINT_TYPE_CONTROL 0x00
#defineUSB_ENDPOINT_TYPE_ISOCHRONOUS 0x01
#defineUSB_ENDPOINT_TYPE_BULK 0x02
#defineUSB_ENDPOINT_TYPE_INTERRUPT 0x03

#defineDSC_DEV 0x01
#defineDSC_CFG 0x02
#defineDSC_STR 0x03
#defineDSC_INTF 0x04
#defineDSC_EP 0x05



//******************************************
//
101
// Definitions (added 19.06.2007)
//
//*******************************************************
#define ConfigDescr_OFFS 0x24 // 0x12
#define HID_Descriptor_OFFS 0x48 // 0x24
#define HID_ReportDesc_OFFS 0x76 // 0x3B

#define USB_DEVICE_DESCRIPTOR_ALL_LEN 0x6A



//******************************************************
//
// USBdrv.h
//
//***************************************
// * UCFG Initialization Parameters
#define _PPBM0 0x00 // Pingpong Buffer Mode 0
#define _PPBM1 0x01 // Pingpong Buffer Mode 1
#define _PPBM2 0x02 // Pingpong Buffer Mode 2
#define _LS 0x00 // Use Low-Speed USB Mode
#define _FS 0x04 // Use Full-Speed USB Mode
#define _TRINT 0x00 // Use internal transceiver
#define _TREXT 0x08 // Use external transceiver
#define _PUEN 0x10 // Use internal pull-up resistor
#define _OEMON 0x40 // Use SIE output indicator
#define _UTEYE 0x80 // Use Eye-Pattern test

// * UEPn Initialization Parameters
#defineEP_CTRL 0x06 // Cfg Control pipe for this ep
#defineEP_OUT 0x0C // Cfg OUT only pipe for this ep
#defineEP_IN 0x0A // Cfg IN only pipe for this ep
#defineEP_OUT_IN 0x0E // Cfg both OUT & IN pipes for this ep
#defineHSHK_EN 0x10 // Enable handshake packet

#defineOUT 0
#defineIN 1

#definePIC_EP_NUM_MASK 0x78
#definePIC_EP_DIR_MASK 0x04

#defineEP00_OUT 0x00 // (0x00<<3) | (OUT<<2)
#defineEP00_IN 0x04 // (0x00<<3) | ( IN<<2)
#defineEP01_OUT 0x08 // (0x01<<3) | (OUT<<2)
#defineEP01_IN 0x0C // (0x01<<3) | ( IN<<2)
#defineEP02_OUT 0x10 // (0x02<<3) | (OUT<<2)
#defineEP02_IN 0x14 // (0x02<<3) | ( IN<<2)
#defineEP03_OUT 0x18 // (0x03<<3) | (OUT<<2)
#defineEP03_IN 0x1C // (0x03<<3) | ( IN<<2)
102
#defineEP04_OUT 0x20 // (0x04<<3) | (OUT<<2)
#defineEP04_IN 0x24 // (0x04<<3) | ( IN<<2)
#defineEP05_OUT 0x28 // (0x05<<3) | (OUT<<2)
#defineEP05_IN 0x2C // (0x05<<3) | ( IN<<2)
#defineEP06_OUT 0x30 // (0x06<<3) | (OUT<<2)
#defineEP06_IN 0x34 // (0x06<<3) | ( IN<<2)
#defineEP07_OUT 0x38 // (0x07<<3) | (OUT<<2)
#defineEP07_IN 0x3C // (0x07<<3) | ( IN<<2)
#defineEP08_OUT 0x40 // (0x08<<3) | (OUT<<2)
#defineEP08_IN 0x44 // (0x08<<3) | ( IN<<2)
#defineEP09_OUT 0x48 // (0x09<<3) | (OUT<<2)
#defineEP09_IN 0x4C // (0x09<<3) | ( IN<<2)
#defineEP10_OUT 0x50 // (0x0A<<3) | (OUT<<2)
#defineEP10_IN 0x54 // (0x0A<<3) | ( IN<<2)
#defineEP11_OUT 0x58 // (0x0B<<3) | (OUT<<2)
#defineEP11_IN 0x5C // (0x0B<<3) | ( IN<<2)
#defineEP12_OUT 0x60 // (0x0C<<3) | (OUT<<2)
#defineEP12_IN 0x64 // (0x0C<<3) | ( IN<<2)
#defineEP13_OUT 0x68 // (0x0D<<3) | (OUT<<2)
#defineEP13_IN 0x6C // (0x0D<<3) | ( IN<<2)
#defineEP14_OUT 0x70 // (0x0E<<3) | (OUT<<2)
#defineEP14_IN 0x74 // (0x0E<<3) | ( IN<<2)
#defineEP15_OUT 0x78 // (0x0F<<3) | (OUT<<2)
#defineEP15_IN 0x7C // (0x0F<<3) | ( IN<<2)
//****************************************
//
// USBmmap.h
//
//***********************************************
// * Buffer Descriptor Status Register Initialization Parameters
#define_BC89 0x03 // Byte count bits 8 and 9
#define_BSTALL 0x04 // Buffer Stall enable
#define_DTSEN 0x08 // Data Toggle Synch enable
#define_INCDIS 0x10 // Address increment disable
#define_KEN 0x20 // SIE keeps buff descriptors enable
#define_DAT0 0x00 // DATA0 packet expected next
#define_DAT1 0x40 // DATA1 packet expected next
#define_DTSMASK 0x40 // DTS Mask
#define_USIE 0x80 // SIE owns buffer
#define_UCPU 0x00 // CPU owns buffer
// * USB Device States - To be used with [byte usb_device_state]
#defineDETACHED_STATE 0
#defineATTACHED_STATE 1
#definePOWERED_STATE 2
#defineDEFAULT_STATE 3
#defineADR_PENDING_STATE 4
#defineADDRESS_STATE 5
#defineCONFIGURED_STATE 6
// * Memory Types for Control Transfer - used in USB_DEVICE_STATUS
103
#define_RAM 0
#define_ROM 1
#defineRemoteWakeup 0
#definectrl_trf_mem 1
//*****************************************
//
// USBctrlTrf.h
//
//***************************************
// * Control Transfer States
#define WAIT_SETUP 0
#define CTRL_TRF_TX 1
#define CTRL_TRF_RX 2
// * USB PID: Token Types - See chapter 8 in the USB specification
#define SETUP_TOKEN 0x0D
#define OUT_TOKEN 0x01
#define IN_TOKEN 0x09
// * bmRequestType Definitions
#define HOST_TO_DEV 0
#define DEV_TO_HOST 1

#define STANDARD 0x00
#define CLASS 0x01
#define VENDOR 0x02

#define RCPT_DEV 0
#define RCPT_INTF 1
#define RCPT_EP 2
#define RCPT_OTH 3
//******************************
//
// USBcfg.h
//
//***********************************************************
// * MUID = Microchip USB Class ID
// * Used to identify which of the USB classes owns
// the current session of control transfer over EP0
#defineMUID_NULL 0
#defineMUID_USB9 1
#defineMUID_HID 2
//***************************************************
//
// USB9.h
//
//*****************************************************
// * Standard Request Codes, USB 2.0 Spec Ref Table 9-4
#defineGET_STATUS 0
#defineCLR_FEATURE 1
#defineSET_FEATURE 3
104
#defineSET_ADR 5
#defineGET_DSC 6
#defineSET_DSC 7
#defineGET_CFG 8
#defineSET_CFG 9
#defineGET_INTF 10
#defineSET_INTF 11
#defineSYNCH_FRAME 12
// * Standard Feature Selectors
#define DEVICE_REMOTE_WAKEUP 0x01
#define ENDPOINT_HALT 0x00
//********************************************
//
// HID.h
//
//********************************************
#defineHID_INTF_ID 0x00
// * Class-Specific Requests
#defineGET_REPORT 0x01
#defineGET_IDLE 0x02
#defineGET_PROTOCOL 0x03
#defineSET_REPORT 0x09
#defineSET_IDLE 0x0A
#defineSET_PROTOCOL 0x0B
// * Class Descriptor Types
#defineDSC_HID 0x21
#defineDSC_RPT 0x22
#defineDSC_PHY 0x23
//********************************************

VARs.h:

//*********************************************
//
// Bank 4 GPR Variables in region 0x400 - 0x4FF
//
//**********************************************
extern unsigned char BDT[16];
extern unsigned char SetupPkt[8];
extern unsigned char CtrlTrfData[8];
extern unsigned char hid_report_feature[8];

extern unsigned char Byte_tmp_0[2];
extern unsigned char Byte_tmp_1[2];
extern unsigned char param_Len;
extern unsigned char param_buffer[2];
extern unsigned char USTAT_latch;
extern unsigned char usb_device_state;
extern unsigned char usb_active_cfg;
105
extern unsigned char usb_alt_intf[2];
extern unsigned char usb_stat;
extern unsigned char idle_rate;
extern unsigned char active_protocol;
extern unsigned char hid_rpt_rx_len;
extern unsigned char ctrl_trf_state;
extern unsigned char ctrl_trf_session_owner;
extern unsigned char pSrc[2];
extern unsigned char pDst[2];
extern unsigned char wCount[2];
extern unsigned char byte_to_send[2];
extern unsigned char byte_to_read[2];
extern unsigned char number_of_bytes_read;

extern unsigned char USB_CD_Ptr[4];
extern unsigned char USB_SD_Ptr[8];

extern char FSR0reg[2];
extern char FSR1reg[2];
extern char FSR2reg[2];

extern unsigned int HID_ReadBuff_Ptr;
extern unsigned int HID_WriteBuff_Ptr;

extern unsigned char hid_report_out[64];
extern unsigned char hid_report_in[64];

//********************************************
// Setup packet structures in EP0 Out Buffer
//********************************************
extern unsigned char SetupPkt_bmRequestType;
extern unsigned char SetupPkt_bRequest;
extern unsigned int SetupPkt_wValue;
extern unsigned int SetupPkt_wIndex;
extern unsigned int SetupPkt_wLength;
extern unsigned int SetupPkt_W_Value;
extern unsigned int SetupPkt_W_Index;
extern unsigned int SetupPkt_W_Length;
extern unsigned char SetupPkt_Recipient;
extern unsigned char SetupPkt_RequestType;
extern unsigned char SetupPkt_DataDir;
extern unsigned char SetupPkt_bFeature;
extern unsigned char SetupPkt_bDscIndex;
extern unsigned char SetupPkt_bDscType;
extern unsigned int SetupPkt_wLangID;
extern unsigned char SetupPkt_bDevADR;
extern unsigned char SetupPkt_bDevADRH;
extern unsigned char SetupPkt_bCfgValue;
extern unsigned char SetupPkt_bCfgRSD;
106
extern unsigned char SetupPkt_bAltID;
extern unsigned char SetupPkt_bAltID_H;
extern unsigned char SetupPkt_bIntfID;
extern unsigned char SetupPkt_bIntfID_H;
extern unsigned char SetupPkt_bEPID;
extern unsigned char SetupPkt_bEPID_H;
extern unsigned char SetupPkt_EPNum;
extern unsigned char SetupPkt_EPDir;
//**************************************
// Buffer Descriptors Table
//**************************************
extern unsigned char BD0STAT;
extern unsigned char BD0CNT;
extern unsigned char BD0ADRL;
extern unsigned char BD0ADRH;
extern unsigned char BD1STAT;
extern unsigned char BD1CNT;
extern unsigned char BD1ADRL;
extern unsigned char BD1ADRH;
extern unsigned char BD2STAT;
extern unsigned char BD2CNT;
extern unsigned char BD2ADRL;
extern unsigned char BD2ADRH;
extern unsigned char BD3STAT;
extern unsigned char BD3CNT;
extern unsigned char BD3ADRL;
extern unsigned char BD3ADRH;
extern unsigned char BD4STAT;
extern unsigned char BD4CNT;
extern unsigned char BD4ADRL;
extern unsigned char BD4ADRH;
extern unsigned char BD5STAT;
extern unsigned char BD5CNT;
extern unsigned char BD5ADRL;
extern unsigned char BD5ADRH;
extern unsigned char BD6STAT;
extern unsigned char BD6CNT;
extern unsigned char BD6ADRL;
extern unsigned char BD6ADRH;
extern unsigned char BD7STAT;
extern unsigned char BD7CNT;
extern unsigned char BD7ADRL;
extern unsigned char BD7ADRH;
//***********************************
//***********************************
// Initialization Function
//***********************************
void InitVARs();
//***********************************
107
Para la creación del proyecto el editor HID Terminal debe tener una vista como la siguiente antes
de realizar la creación del archivo USBdsc.c:

Figura 7-8

Después de correr la simulación se debe correr el HID Terminal, y seleccionar la pestáña:
Terminal. Está aplicación permite enviar y recibir datos por medio del puerto USB. Para este
propósito, se debe seleccionar el dispositivo, Picando el nombre del dispositivo en la lista HID
Devices, para este caso el nombre del dispositivo es: Prueba USB. Posteriormente se escribe un
carácter en la casilla de envió de datos y se pulsa el botón Send. Seguidamente el PIC retransmite
un texto con el carácter enviado, y el LED del circuito hace un parpadeo. Una vista de está acción
en el HID Terminal tiene la siguiente apariencia:

Figura 7-9
108
De igual manera si se pulsa el botón: info, el HID Terminal muestra en una ventana las
características con las que está configurado en dispositivo HID, una vista de está acción se puede
apreciar en la siguiente figura:


Figura 7-10



109
8 Conversión AD y DA
La conversión análogo digital y digital análogo es un proceso por el cual se puede tomar o
entregar muestras de una señal continua de voltaje. El uso de estás conversiones es de gran
utilidad para realizar procesamiento digital de señales. La conversión análogo digital, o ADC, se
puede realizar con algunos microcontroladores que tienen implícito un convertidor de este estilo.
El proceso de conversión digital análogo es posible con elementos externos de fácil
implementación o, incluso, es posible realizar esta conversión con los módulos PWM
incorporados en algunos microcontroladores.
8.1 Conversión AD, o ADC
Este proceso se realiza con el convertidor interno de los microcontroladores. Este módulo está
incorporado en la mayoría de los microcontroladores de gama media y alta. La conversión
implementada por los PICMicro cuenta con una resolución de 10 bits, lo que permite obtener un
número con un rango de 0 a 1023, que es proporcional a los valores de referencia, que por
defecto son 0 voltios y 5 voltios. Esto significa que si una entrada análoga, tiene una tensión de 0
voltios su resultado es 0, y si la tensión es de 5 voltios el resultado de la conversión es 1023 de
igual manera si la tensión es de 2.5 voltios, el resultado será 512. Dependiendo de la complejidad
del microcontrolador un PIC puede tener hasta 8 entradas de señal análoga. Sin embargo cabe
denotar que el módulo de conversión interna de los microcontroladores es sólo uno, y los
múltiples canales se pueden leer pero no al mismo tiempo. Para realizar este tipo de conversiones
el compilador MikroC PRO cuenta con una librería definida: ADC para hacer la conversión. Está
librería cuenta con una sola función denominada unsigned int ADC_Read(unsigned short
channel). Esta función retorna el resultado de la conversión del canal especificado por el
parámetro channel. Para contextualizar el uso de está librería se puede observar y analizar el
siguiente ejemplo:
void main( void )
{
//Declaración de variables.
unsigned int Dato;
//Inicialización de puertos.
TRISB = 0;
TRISC = 0;
PORTB = 0;
PORTC = 0;
while(1) //Bucle infinito.
{
Dato = ADC_Read(0); //Se hace conversión sobre el canal 0.
//Se muestran los 8 bits de menor peso por el puerto B.
PORTB = Dato&0xFF;
//Se muestran los 2 bits de mayor peso por el puerto C.
PORTC = (Dato>>8)&0x03;
}
}
110
Para la simulación de este ejemplo se deben implementar los dispositivos: 16F877A, RES, LED-
RED, POT-HG, en el siguiente circuito de ISIS:


Circuito 8-1

Después de correr la simulación los LEDs muestran en código binario del valor de la conversión
análogo digital, de la diferencia de potencial que entrega el potenciómetro.

8.2 Conversión DA o DAC
Este tipo de conversión es posible por medio de dos estrategias, la primera es usar el módulo
PWM, del microcontrolador, y la segunda es la implementación de un arreglo externo de
resistencias para obtener la diferencia de potencial.

8.2.1 Conversión DA con PWM
La conversión digital análogo, con el módulo PWM, consiste en tomar la señal modulada por
ancho de pulso y realizar la demodulación por medio de un filtro pasa bajas. Este filtro debe tener
como frecuencia de corte un valor mucho menor a la frecuencia de muestreo en una proporción
cercana a 10 veces, dado que su objetivo es eliminar la frecuencia de muestreo. El cálculo de la
frecuencia de corte de este filtro está regido por la siguiente fórmula:


RC
Fc
t 2
1
=
Ecuación 8-1


El circuito a implementa de un filtro pasa bajas de primer orden es el siguiente:

111

Circuito 8-2

Implementación del filtro pasa bajas de segundo orden:

Circuito 8-3

La ecuación para el cálculo en este filtro es:
2 2
1
RC
Fc
t
= Ecuación 8-2

La frecuencia que se asigne a la portadora de modulación PWM, debe ser mucho mayor a la
frecuencia de muestreo de la señal moduladora. Para demostrar el funcionamiento en está
conversión se designarán 20 muestras que corresponden a un ciclo de una señal seno. Las
muestras y la forma de onda se pueden apreciar en la siguiente figura:

Figura 8-1
112
La implementación de la librería PWM, se hace por medio de cuatro funciones que son:
PWM1_Init(const long freq);, está función inicializa el módulo PWM, a la frecuencia de la
portadora freq. La función: PWM1_Set_Duty(unsigned short duty_ratio);, está función estáblece
el ciclo útil por medio del parámetro duty_ratio, este parámetro puede tomar valores de 0 a 255,
donde 0 representa el 0%, y 255 el 100% del ciclo útil. La siguiente imagen ilustra el
comportamiento de una señal PWM, en función del ciclo útil, la relación Fpwm y Tpwm:



Figura 8-2

Por último se cuenta con las funciones: PWM1_Start(); y PWM1_Stop();, Las cuales activan y
desactivan respectivamente la señal PWM.

Para realizar la simulación se debe hacer un proyecto en MikroC PRO, con el siguiente código
fuente:


//Declaración de constantes
//para la señal seno.
const unsigned short Seno[20] =
{
127, 146, 163, 177, 185, 189, 185,
177, 163, 146, 127, 107, 90, 76,
68, 65, 68, 76, 90, 107
};


void main( void )
{
//Declaración de variables.
unsigned short n=0;
//Configuración del módulo PWM a Fpwm=15.625K Hz.
PWM1_Init(15625);
//Inicio de señal PWM.
PWM1_Start();
while(1) //Bucle infinito.
{
//Bucle para recorres las 20 muestras
//de un ciclo para la onda seno.
for( n=0; n<20; n++ )
113
{
//Cambio del ciclo útil del PWM.
PWM1_Set_Duty( Seno[n] );
//Retardo de 50u seg.
delay_us(50);
}
}
}


La ejecución de la simulación requiere de los dispositivos: 16F877A, RES, CAP, OP1P, y el
instrumento virtual: OSCILLOSCOPE. Este último es un osciloscopio virtual de 4 canales
simultáneos, ideal para visualizar señales análogas e incluso digitales.

Este circuito implementa un filtro pasa bajas para eliminar la frecuencia Fpwm portadora que en
este caso es de 15.6KHz, el filtro cuenta con una frecuencia de corte de 1.5KHz
aproximadamente.

En futuras aplicaciones se recomienda para los cálculos de los filtros, definir un valor arbitrario
para el condensador C, entre 100nF y 100pF y calcular el valor de R, por medio de las ecuaciones
antes mencionadas.
Con los anteriores elementos se construye el siguiente circuito:


Circuito 8-4

Sobre la marcha de la simulación se debe apreciar como emerge el osciloscopio virtual
mostrando la señal análoga de forma seno producto de la reconstrucción digital del PIC, por
medio del módulo PWM, y el filtro pasa bajas, la apariencia visual del osciloscopio es la que se
puede ver en la siguiente figura:

114

Figura 8-3
8.2.2 Conversión DA con arreglo R-2R
El arreglo de resistencias R-2R, permite hacer la conversión de un número binario a un valor
proporcional de voltaje. Este arreglo permite implementar un número indeterminado de bits, en la
conversión a diferencia del módulo PWM, que se limita a 8 bits. Esto quiere decir que con el
arreglo R-2R, se pueden hacer conversiones de 8, 16, 32, 64, o un número n de bits en función de
la cantidad de pines disponibles en un microcontrolador. La implementación del convertidor R-
2R, es de fácil desarrollo dado que consiste en un arreglo de conexiones de resistencias, donde
una es el doble de la otra, de ahí su nombre R-2R.
Cabe resaltar que cuantos más bits, posea la conversión mayor será su resolución y por
consiguiente mayor la calidad de la señal reconstruida. Las desventajas notables de este arreglo
son, el incremento del hardware requerido y el uso de una cantidad mayor de pines de los puertos.
En la siguiente gráfica se puede apreciar la configuración de las resistencias para una conversión
de 8 bits:


Circuito 8-5

Este arreglo puede ser expandido con la misma arquitectura para conseguir hacer un convertidor
de mayor resolución, aumentando el número de entadas D, o lo que es igual el número de bits.
Otra característica de este tipo de conversión es que no requiere de librerías especializadas, solo
basta con colocar el valor numérico a convertir en un puerto, y el valor análogo hará presencia en
la salida. De la misma forma que se hace la conversión por PWM, es importante suprimir la
frecuencia de muestreo para evitar componentes de señal impuras.

115
Una forma completa del convertidor con un acople de impedancia y su respectivo filtro pasa
bajas de 1.5K Hz, es el siguiente:


Circuito 8-6

Para demostrar la aplicación de está técnica de conversión se modificará el ejemplo de
conversión con PWM, para este fin observe y analice el siguiente código fuente:

//Declaración de constantes
//para la señal seno.
const unsigned short Seno[20] =
{
127, 146, 163, 177, 185, 189, 185,
177, 163, 146, 127, 107, 90, 76,
68, 65, 68, 76, 90, 107
};

void main( void )
{
//Declaración de variables.
unsigned short n=0;
//Configuración de puertos.
TRISB = 0;
PORTB = 127;
while(1) //Bucle infinito.
{
//Bucle para recorres las 20 muestras
//de un ciclo para la onda seno.
for( n=0; n<20; n++ )
{
//Cambio de muestra en el puerto B.
116
PORTB = Seno[n];
//Retardo de 50u seg.
delay_us(50);
}
}
}

Editado y compilado este programa se procede a simular en ISIS, con el circuito que se puede
apreciar en el siguiente circuito:


Circuito 8-7

El resultado esperado en está simulación es igual a la simulación del convertidor con PWM, la
salida del osciloscopio debe mostrar una gráfica similar.
117
9 Memorias EEPROM y
FLASH
Las memorias EEPROM y FLASH de los microcontroladores son espacios de memoria que se
pueden programar y borrar eléctricamente, estás dos memorias son campos de información no
volátil; es decir, que si la energía de alimentación del microcontrolador se desconecta la
información no se pierde. Está información puede ser reprogramada alrededor de 100000 veces
para el caso de la memoria FLASH y algo cercano a 1000000 de veces para el caso de las
memorias EEPROM. De igual manera su información puede ser garantizada hasta por 40 años sin
energía eléctrica.
9.1 Memoria EEPROM
La memoria EEPROM es un campo relativamente pequeño que tienen los microcontroladores
para que el desarrollador guarde información como configuraciones y datos variables que no
deben perderse. La capacidad de memoria varía de un microcontrolador a otro dependiendo de su
gama y tamaño. La manipulación de esta memoria se puede hacer con MikroC PRO, por medio
de una librería llamada: EEPROM, está cuenta con dos funciones que permiten leer y escribir una
dirección de memoria. Las funciones son: unsigned short EEPROM_Read(unsigned int
address);, Está función lee y retorna el valor guardado en la dirección especificada en el
parámetro address.
La función: EEPROM_Write(unsigned int address, unsigned short data);, está función graba la
dirección definida en el parámetro address, con el valor entregado en el parámetro data.

Para entender de forma concreta el funcionamiento de está librería se puede observar y analizar el
siguiente código fuente:

void main( void )
{
//Configuración de puertos.
TRISB = 0;
PORTB = 0;
while(1) //Bucle infinito.
{
//Se muestra en el puerto B el valor
//de la dirección 0 de la memoria EEPROM.
PORTB = EEPROM_Read(0);
//La sentencia if evalúa si se pulsa el botón.
if( !PORTC.F0 )
{
//Se incrementa el valor del puerto B.
PORTB++;
//Se guarda el nuevo valor del puerto
//en la dirección 0 de la EERPOM.
EEPROM_Write(0,PORTB);
118
//Se espera a que se suelte el botón.
while( !PORTC.F0 );
}
}
}

La simulación de este ejemplo se realiza con los dispositivos: 16F877A, RES, LED-RED, y
BUTTON. Para correr la simulación se debe construir el siguiente circuito en ISIS:


Circuito 9-1

El circuito al ser simulado, mostrará en los LEDs el valor de la dirección 0 de la memoria
EEPROM, este valor es incrementado cada vez que se pulsa el botón, y este es actualizado en la
memoria EEPROM. Durante la simulación se puede detener la misma, para ver el efecto de la
suspensión de energía, de tal forma que al correr de nuevo la simulación se puede ver como el
valor de la información se conserva.
9.2 Memoria FLASH
La memoria FLASH es el campo de información de mayor capacidad de los microcontroladores
es en está memoria donde se guarda las instrucciones de todo el programa del PIC. Esto significa
que es de sumo cuidado usar está memoria ya que es posible dañar el programa principal si se
altera alguna de sus instrucciones. La programación de la memoria FLASH es de utilidad para
guardar segmentos de información de tamaño relativamente grande. En está memoria se puede
guardar pequeños archivos del orden de 1 o más kilo bytes dependiendo de la capacidad de
memoria de cada uno de los microcontroladores, y del tamaño total del programa, dado que a
medida que el tamaño del programa aumenta, la posibilidad de usar la memoria FLASH, se
reduce.

La implementación de está librería se fundamenta en dos funciones similares a las funciones de la
memoria EEPROM, estás funciones son: FLASH_Write(unsigned address, unsigned int* data);,
en está función se ingresa el parámetro address, el cual contiene la dirección de la primera
119
dirección a grabar, y el apuntador data, que contiene los datos que serán grabados en la memoria
FLASH. La siguiente función es: unsigned FLASH_Read(unsigned address);, está función
retorna el valor guardado en la dirección definida por el parámetro address.

Para contextualizar el uso de está librería se puede crear y analizar el siguiente código de
programa:

void main( void )
{
//Declaración de variables.
char Dato;
unsigned int buf[10];
unsigned int Dir;
//Configuración de puertos.
TRISB = 0;
PORTB = 0;
PORTB = 1;
//Configuración del puerto serial.
UART1_Init(9600);
//Bienvenida al programa.
UART1_Write_Text("Digite su archivo FLASH");
UART1_Write(13);
UART1_Write(10);
//Se configura la dirección inicial de la FLASH.
Dir=0x0200;
while(1) //Bucle infinito.
{
//Sentencia if para evaluar si hay datos para leer
//en el puerto serial.
if( UART1_Data_Ready() )
{
//Se lee el puerto serial.
Dato = UART1_Read();
//Estructura Switch case para evaluar los datos de entrada.
switch( Dato )
{
//Caso de solicitud de envió del archivo
//con el carácter #
case '#': UART1_Write(13);
UART1_Write(10);
UART1_Write_Text("INIDIO DEL ARCHIVO:");
UART1_Write(13);
UART1_Write(10);
//Se configura la dirección inicial de la FLASH.
Dir=0x0200;
//Bucle do while para leer el archivo
//y enviar la información.
do
{
120
//Lectura de la dirección en FLASH.
buf[0]=FLASH_Read(Dir++);
Dato = buf[0];
//Envió del Dato por el puerto serial.
UART1_Write(Dato);
}while( Dato!=0 );
UART1_Write(13);
UART1_Write(10);
UART1_Write_Text("FIN DEL ARCHIVO.");
UART1_Write(13);
UART1_Write(10);
//Se configura la dirección inicial de la FLASH.
Dir=0x0200;
break;

default: //Caso por defecto en donde se guardan
//los datos en la FLASH.
Buf[0]=Dato;
Buf[1]=0; //Carácter fin de cadena NULL.
//Envió de eco por el puerto serial.
UART1_Write(Dato);
//Se guarda en FLASH el dato.
FLASH_Write( Dir, Buf );
//Incremento de la dirección.
Dir++;
}
}
}
}

Para la simulación en ISIS, se debe implementar los dispositivos 16F877A, y el VIRTUAL
TERMINAL, en un circuito como el que se puede apreciar en la siguiente figura:

Circuito 9-2
121
10 Módulos Timer
Los módulos Timer son unidades que permiten hacer el conteo de tiempos precisos, la cantidad
de módulos Timer en cada microcontrolador depende de la gama del PIC, algunos pueden tener
hasta 4 Timers, sin embargo la mayoría de los microcontroladores posee al menos uno que
generalmente se denomina Timer 0. El uso y configuración de cada módulo Timer depende del
desarrollador y del tamaño del conteo, este conteo puede ser de 8 o 16 bits. El intervalo de tiempo
con el cual se incrementa el Timer, depende de la arquitectura y configuración que se asigne en
los registros de control del Timer. Para el caso del PIC 16F877A, la arquitectura del Timer 0 es
como se puede apreciar en la siguiente figura:


Figura 10-1

Este módulo Timer, puede tener dos fuentes de pulsos de reloj, uno es el reloj propio del
procesador, o una fuente externa de reloj, por medio del pin RA4. De igual manera la señal de
reloj puede usar una subdivisión, para crear tiempo de reloj menor. Las subdivisiones posibles
para este timer son las siguientes: 1, 2, 4, 8, 16, 32, 64, 128, y 256. Para el caso de el Timer 0 su
registro es de 8 bits, esto quiere decir que puede hacer un conteo de 0 a 255, y cuando el timer
pasa de 255 a 0, se dice que el Timer se desborda o se dispara.

En función de estás características el tiempo de desborde o disparo se puede calcular con la
siguiente relación: P = 256x(Subdivisión)x4xTosc, donde Tosc es el periodo de oscilación del
reloj del procesador, si el Timer 0 se configura con la fuente externa la relación es:
P = 256x(Subdivisión)xText, donde Text, es el periodo de oscilación del reloj externo.
Para un reloj de 4MHz, el mayor tiempo puede ser: P=256x256x4x1u, P=65,536m Seg.

122
La configuración del Timer 0 se logra con los bits: T0CS, PSA, PS0, PS1, y PS2. La calibración
de la subdivisión del Timer 0 se hace con los bits, PS0, PS1, y PS2, está configuración obedece a
la siguiente relación:
Subdivisión Timer 0
PS2:PS1:PS0 Subdivisión
000 2
001 4
010 8
011 16
100 32
101 64
110 128
111 256

Tabla 10-1

Para seleccionar la subdivisión 1, se debe configura el bit PSA, el cual asumen la subdivisión 1,
cuando este bit vale 1, y a sume la subdivisión de la tabla anterior, cuando este bit vale 0.

La configuración de los bits PS0 al PS2, se realiza sobre el registro OPTION_REG, en los tres
bits de menor peso, el bit PSA, corresponde al cuarto bit del registro OPTION_REG, y el bit
T0SC corresponde al sexto bit del mismo registro. El mapa de bits del registro OPTION_REG, es
el siguiente:

OPTION_REG
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
RBPU INTEDG T0CS T0SE PSA PS2 PS1 PS0

Para demostrar el uso del Timer 0, se puede observar y analizar el siguiente código fuente:

void main( void )
{
TRISB = 0;//Configuración de puertos.
PORTB = 0;
OPTION_REG=0b11000111; //Configuración del Timer 0.
while(1) //Bucle infinito.
{
if(TMR0==0) //Se evalúa si el Timer 0 vale 0.
{
if(PORTB==0)//Se conmuta el valor del puerto B.
PORTB=1;
else
PORTB=0;
while(TMR0==0);//Se espera a que el Timer 0 cambie de valor.
}
}
}
123
Para la simulación de este circuito en ISIS, se implementan los dispositivos 16F877A, y el
osciloscopio virtual, OSCILLOSCOPE, en el siguiente arreglo:


Circuito 10-1

Al correr la simulación se puede apreciar en el osciloscopio, que hay un cambio de estado en el
pin RB0, cada 65.536m Seg. La vista del osciloscopio debe ser la siguiente:



Figura 10-2
124
11 Interrupciones
Las interrupciones en los microcontroladores son eventos programados que hacen que el PIC
suspenda su rutina y ejecute un fragmento de programa asociado al evento. Cuando la rutina de la
interrupción se termina el programa del PIC continúa ejecutando el flujo del programa en el
mismo punto en donde se suspendió la ejecución. Las interrupciones pueden tener fuentes
variadas que dependen de la gama y complejidad del microcontrolador. Por ejemplo el PIC
16F877A cuenta con fuentes de interrupción como el Timer0, cuando el timer se desborda,
externa cuando se hace un cambio de flanco sobre el pin RB0, serial cuando un dato llega por la
USART, ADC, cuando una conversión termina, entre otras. Para la ejecución de las
interrupciones el compilador MikroC PRO, cuenta con una función predefinida llamada: void
interrupt(void); está función debe ser declarada antes de la función main. Cuando una
interrupción se dispara la función interrupt es invocada automáticamente por el programa, dentro
de está función se debe evaluar cual de las interrupciones se dispara por medio de las banderas de
cada interrupción.

Para la configuración de las interrupciones se deben activar los bits correspondientes en los
registros de interrupción En el caso particular del PIC 16F877A se usan los registros: INTCON,
PIR1, PIR2, PIE1, y PIE2. Los mapas de bits de estos registros en el PIC 16F877A son los
siguientes:


INTCON
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
GIE PEIE TMR0IE INTE RBIE TMR0IF INTF RBIF


PIR1
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF


PIR2
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
- CMIF - EEIF BCLIF - - CCP2IF


PIE1
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE


PIE2
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
- CMIE - EEIE BCLIE - - CCP2IE

125
La implementación de las interrupciones implica seguir un protocolo para la correcta
configuración de las mismas. Primero se deben activar las interrupciones que se desean usar, por
medio de los bit IE, como: TMR0IE, INTE, RCIE, entre otros. Las banderas de disparo de las
interrupciones debe ser desactivadas, como: TMR0IF, INTF, RCIF, entre otras. Se deben activar
las interrupciones periféricas, cuando está se requieran, por ejemplo las que están citadas en el
registro: PIE1, y PIE2. Por último se debe activar el bit de interrupciones en general, que es: GIE.
Para el siguiente ejemplo se usarán las interrupciones por: recepción serial, Timer 0, y externa,
para este fin se puede observar y analizar el siguiente código fuente:


//Declaración de la función de interrupciones.
void interrupt ( void )
{
//Declaración de variables usadas en
//la función de interrupciones.
char Dato;
//Se evalúa si la interrupción disparada es
//por Timer 0, TMR0IF
if( INTCON.F2==1 )
{
//Se complementa el valor del bit RB1.
if(PORTB.F1==1)
PORTB.F1=0;
else
PORTB.F1=1;
//Se apaga la bandera de Timer 0.
INTCON.F2=0;
}


//Se evalúa si la interrupción disparada es externa.
// INTF
if( INTCON.F1==1 )
{
//Se complementa el valor del bit RB2.
if(PORTB.F2==1)
PORTB.F2=0;
else
PORTB.F2=1;
//Se apaga la bandera de interrupción externa.
INTCON.F1=0;
}


//Se evalúa si la interrupción disparada por
//recepción serial.
if( PIR1.F5 )
{
//Se lee el dato de entrada.
126
Dato = UART1_Read();
//Se envía información de confirmación.
UART1_Write_Text("Dato de entrada: ");
//Se envía el dato recibido.
UART1_Write(Dato);
UART1_Write(13); //Se envía código ASCII del enter.
UART1_Write(10); //Se envía código de retroceso del carro.
//Se apara la bandera por recepción serial.
PIR1.F5 = 0;
}
}


void main( void )
{
//Se configuran los puertos.
TRISB = 0b00000001;
PORTB = 0;
//Se activa la interrupción por
//recepción serial.
PIE1 = 0b00100000;
//Se desactivan las demás fuentes de interrupción.
PIE2 = 0;
//Se apagan las banderas de interrupción.
PIR2 = 0;
PIR1 = 0;
//Configuración del Timer 0 a 65,535m Seg.
OPTION_REG=0b11000111;
//Configuración del puerto serial a 9600 bps.
UART1_Init(9600);
//Se activan las interrupciones globales,
//por Timer 0, y externa.
INTCON = 0b11110000;
while(1) //Bucle infinito.
{

}

}


Para la simulación de este ejemplo en ISIS, se implementarán los siguientes dispositivos:
16F877A, RES, BUTTON, LED-RED, y los instrumentos virtuales: VIRTUAL TERMINAL y
OSCILLOSCOPE. Después de correr la simulación se debe poder interactuar con el virtual
terminal, al enviar datos, con el pulsador se debe conmutar el estádo del LED, y la salida del
osciloscopio debe mostrar una señal cuadrada con cambios cada 65,535m Segundos, de acuerdo
con el Timer 0. Para correr la simulación se debe hacer el siguiente circuito:


127


Circuito 11-1
128
12 Sensores
La implementación de sensores en sistemas microcontrolados es de gran utilidad en muchas de
sus aplicaciones, estos permiten obtener lecturas de variables como temperatura, aceleración,
presión, humedad, velocidad, luminosidad, contraste, entre otras. Su aplicación es importante en
sistemas de control, robótica, e instrumentación.
12.1 Sensor de temperatura LM35
El sensor LM35, es un dispositivo activo de 3 terminales que permite adquirir la temperatura
ambiente en rangos de -55 a 150 grados Celsius o centígrados. Este dispositivo es de fácil
implementación dado que solo cuenta con dos terminales de polarización, y una salida de voltaje
directamente proporcional a la temperatura. Este sensor puede ser polarizado de 4 a 30 voltios y
tiene una salida de 10m voltios por cada grado Celsius. La apariencia física del sensor y su
distribución de pines, así como la vista en ISIS, son las que se pueden ver en las siguientes
figuras:

Figura 12-1

Para realizar la lectura del voltaje de salida del sensor se implementa en el microcontrolador el
módulo ADC. La máxima salida del sensor es 1,5 voltios, cuando la temperatura es 150 grados
Celsius. Por esto es importante cambiar el valor de referencia positiva del convertidor análogo
digital, con el fin de mejorar la resolución de la medida de voltaje. Para el ejemplo de este
capítulo se configurará el voltaje de referencia positivo del ADC, en 2,5 voltios. Cambiando la
referencia positiva a 2,5 voltios el convertidor entregará un resultado binario de 1023 cuando el
voltaje a convertir es de 2,5 voltios. Para el caso de este sensor, se verá definido por las siguientes
relaciones:
Vadc
Radc
V
=
5 , 2
1023
Ecuación 12-1

Donde Radc es el resultado binario de la conversión AD. De está ecuación se puede deducir que
el voltaje Vadc, leído por el convertidor AD, es:

1023
) )( 5 , 2 ( Radc V
Vadc = Ecuación 12-2

Trabajando con la relación del sensor que es: 10m voltios por cada grado Celsius, se puede
plantear la siguiente ecuación:

129
C n
Vadc
C
mV
o o
=
1
10
Ecuación 12-3

Donde n es la temperatura en grados Celsius, que está registrando el sensor, de está ecuación se
puede deducir que la temperatura n es:

mV
Vadc C
C n
o
o
10
) )( 1 (
= Ecuación 12-4

Remplazando la ecuación (12.2), en (12.4), se obtiene la siguiente relación:

Radc
Radc
C n
o
244 , 0
23 , 10
) )( 5 , 2 (
= = Ecuación 12-5

Está relación debe ser implementada en la conversión AD, en el programa del PIC.

Para contextualizar el uso de este sensor se puede observar y analizar el siguiente código fuente
para un PIC 16F877A:

//Definición de pines del LCD
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;
//Definición de los TRIS del LCD
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;
void main( void )
{
//Declaración de variables.
unsigned int Radc, TemI;
float Tem;
char Text[16];
//Configura el módulo ADC con el pin AN3
//como voltaje de referencia positiva.
ADCON1 = 0b11000001;
//Inicio del LCD.
Lcd_Init();
//Borrado del cursor.
Lcd_Cmd(_LCD_CURSOR_OFF);
//Impresión de texto.
Lcd_Out( 1, 1, "Temperatura:");
130
while(1) //Bucle infinito.
{
//Lectura del canal 0 del ADC.
Radc = ADC_Read(0);
//Uso de la ecuación (13.5).
Tem = 0.244*Radc;
//Se convierte el resultado a un número entero.
TemI = Tem;
//Se convierte el número entero a una cadena de caracteres.
IntToStr( TemI, Text );
//Se imprime el resultado.
Lcd_Out( 2, 1, Text);
//Retardo de 100m segundos.
delay_ms(100);
}
}

Terminada la edición y compilación del programa se debe construir un circuito en ISIS con los
siguientes dispositivos: 16F877A, RES, LM35, y LM016L, este se puede apreciar en la siguiente
figura:

Circuito 12-1
131
El arreglo de resistencias de 330Ω permite hacer un divisor de voltaje para crear la referencia de
2,5 voltios. Durante la marcha de la simulación se puede cambiar el valor de la temperatura en el
sensor LM35, para ver su funcionamiento.
12.2 Sensores de presión
La presión es una variable física, representada por un vector que está definido como la fuerza
aplicada en una superficie específica. La presión se denota en diferentes unidades dependiendo
del sistema, la presión se puede dar en: psi, Pascal, Atmósferas, centímetros de mercurio, etc.
Para los fines prácticos de este capítulo se trabajará con el sensor de presión MPX4115, este
sensor está caracterizado en Kilo Pascal, y permite medir presiones entre 15 y 115 KPa, o entre
2,18 y 16,7 psi. La apariencia física, la distribución de pines y la vista en ISIS, de este dispositivo
es la siguiente:

Figura 12-2

Pin Función
1 Vout
2 GND, Referencia
3 Vs, Poder
4, 5, 6 No implementado

Tabla 12-1

Independiente del sensor que se implemente, cada uno debe contar con una relación de salida o
función de transferencia, en el caso particular del sensor MPX4115 la función de transferencia
especificada por el fabricante es:

) 095 , 0 009 , 0 ( ÷ = P s V Vout Ecuación 12-6

Donde Vout es el voltaje de salida del sensor, P es la presión en Kilo Pascal, y Vs el voltaje de
alimentación del sensor. Despejando de la ecuación (13.6), la presión P, se obtiene:


555 , 10
111 , 111
+ =
Vs
Vout
P Ecuación 12-7

132
En función del convertidor AD, del PIC configurado con 10 bits de resolución se puede concluir
que:
Radc
nV V
=
1023
5
Ecuación 12-8

Por lo tanto despejando el voltaje n de la ecuación (12.8) y remplazando en la ecuación (12.7), se
puede obtener la relación o ecuación que se debe usar en el microcontrolador para hacer la lectura
del sensor, está relación es la siguiente:

555 , 10
54306 , 0
+ =
Vs
Radc
P Ecuación 12-9

Para el caso particular de este ejemplo se usará una fuente de poder de 5 voltios la cual alimenta
el sensor y el micro, por lo tanto Vs es igual a 5 voltios, y la relación final queda de la siguiente
forma:
555 , 10 10861 , 0 + = Radc P Ecuación 12-10

Para demostrar el funcionamiento práctico de este tipo de sensores se puede observar y analizar el
siguiente código fuente:

//Definición de pines del LCD
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;

//Definición de los TRIS del LCD
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;

void main( void )
{
//Declaración de variables.
unsigned int Radc, PreI;
float Pre;
char Text[16];
//Inicio del LCD.
Lcd_Init();
//Borrado del cursor.
Lcd_Cmd(_LCD_CURSOR_OFF);
//Impresión de texto.
Lcd_Out( 1, 1, "Presion en KPa:");
133
while(1) //Bucle infinito.
{
//Lectura del canal 0 del ADC.
Radc = ADC_Read(0);
//Uso de la ecuación (13.9).
Pre = 0.10861*Radc+10,5555;
//Se forza el resultado a la parte entera.
PreI = Pre;
//Se convierte el número entero a una cadena de caracteres.
IntToStr( PreI, Text );
//Se imprime el resultado.
Lcd_Out( 2, 1, Text);
//Retardo de 100m segundos.
delay_ms(100);
}
}

Terminada la edición y compilación del programa se puede implementar en el simulador ISIS los
dispositivos: 16F877A, MPX4115, y LM016L, para realizar la simulación del sistema, el circuito
debe tener la siguiente apariencia:

Circuito 12-2
134
12.3 Sensores de distancia
Los sensores de distancia permiten medir una longitud desde el punto de ubicación del sensor
hasta un obstáculo puesto en un punto dentro del rango de trabajo del sensor. La implementación
de este tipo de dispositivos es de utilidad en sistemas de control, y en proyectos de robótica. Para
el caso puntual de este capítulo se trabajará con el sensor GP2D12, este dispositivo usa como
estrategia de medida un rayo de luz infrarroja y su reflejo para determinar la longitud. La
apariencia física la distribución de pines y la vista en el simulador ISIS del sensor son los
siguientes:


Figura 12-3

Pin Función
1 Vout
2 GND, Referencia
3 Vcc, Poder

Tabla 12-2

Este tipo de sensores cuentan con un comportamiento no lineal, esto quiere decir que la salida no
obedece a una función de transferencia lineal. Para comprender de forma clara este concepto se
puede observar y analizar la siguiente gráfica que muestra el comportamiento de la salida en
función de la distancia:



Figura 12-4
135
Este comportamiento en un sensor es de difícil aplicación, dado que en algunos casos los
fabricantes no entregan una función de transferencia útil de tal forma que pueda ser usada en una
sola ecuación. Sin embargo no es un obstáculo para el uso del sensor. Como se puede apreciar en
la hoja técnica el rango de acción es de 10 a 80 centímetros tal como se puede apreciar en la
gráfica. La gráfica mostrada está contenida en la hoja técnica del dispositivo y está es realizada
experimentalmente. Para poder caracterizar el comportamiento del sensor y realizar una función
de transferencia es indispensable realizar un proceso matemático conocido como linealización.
Para este fin se hace una interpolación con una cantidad finita de puntos conocidos de la función,
estos puntos se toman de las medidas experimentales. Cuantos más puntos se evalúen más precisa
será la función de transferencia.

Para iniciar este proceso se escogen 2 puntos, que estén equitativamente distribuidos. Por ejemplo
el punto a 10cm, y 80cm. Para cada uno de los puntos se realiza una ecuación de orden igual al
número de puntos menos uno, en este caso serán ecuaciones de primer orden. Se asume como
variable independiente x los datos de entrada que en este caso son el voltaje de salida del sensor,
como variable dependiente y, la distancia en centímetros. Para comprender este concepto observe
la siguiente ecuación que representa el comportamiento exponencial del sensor:

B
x
A
y + = Ecuación 12-11

Recordando que y, representa la distancia y que x, el voltaje de salida del sensor, se usan los datos
de dos puntos y se remplazan en las ecuaciones de la siguiente forma:

Distancia(y) Vo, Salida sensor(x)
10cm 2,35V
80cm 0,41V

Tabla 12-3
Sistema de ecuaciones:

B
A
B
A
+ =
+ =
41 , 0
80
35 , 2
10
Ecuación 12-12

Sistema de ecuaciones con dos incógnitas:

B A
B A
+ =
+ =
43902 , 2 80
425531 , 0 10
Ecuación 12-13

Solucionando el sistema de ecuaciones se puede determinar que las constantes son:

A=34,76546392
B=-4,793814433

De está forma se puede formalizar la función de transferencia del sensor con la ecuación:
136
793814433 , 4
76546392 , 34
+ =
x
y Ecuación 12-14

Como la variable x representa el voltaje de salida del sensor y teniendo presente que el valor del
sensor no supera 2,5 voltios, la referencia del convertidor AD, se puede ajustar a 2,5 voltios. Para
estáblecer la conversión se puede usar la ecuación: (12.15)

1023
5 , 2 Radc
Vadc = Ecuación 12-15

Remplazando la ecuación (12.15) en la ecuación (12.14), obtenemos la siguiente relación o
función de transferencia:

793814433 , 4
02784 , 14226
÷ =
Radc
y Ecuación 12-16

El paso siguiente es implementar un código fuente que use la función de transferencia y que
posteriormente visualice el valor capturado, para este fin se puede observar y analizar el siguiente
código fuente:

//Definición de pines del LCD
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;

//Definición de los TRIS del LCD
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;

void main( void )
{
//Declaración de variables.
unsigned int Radc, DisI;
float Dis;
char Text[16];
//Configura el módulo ADC con el pin AN3
//como voltaje de referencia positiva.
ADCON1 = 0b11000001;
//Inicio del LCD.
Lcd_Init();
//Borrado del cursor.
137
Lcd_Cmd(_LCD_CURSOR_OFF);
//Impresión de texto.
Lcd_Out( 1, 1, "Distancia:");

while(1) //Bucle infinito.
{
//Lectura del canal análogo.
Radc=ADC_Read(0);
//Implementación de la función de transferencia (13.15).
Dis = (14226.02784/Radc)-4.793814433;
//Se para el resultado a un valor entero.
DisI=Dis;
//Se convierte el valor entero en cadena de texto.
IntToStr( DisI, Text );
//Se imprime la lectura del sensor.
Lcd_Out( 2, 1, Text );
//Retardo de 100m segundos.
delay_ms(100);
}
}

Por último para verificar el comportamiento del sistema se implementa en ISIS, los dispositivos:
16F877A, RES, LM016L, y el sensor GP2D12, en el siguiente circuito:


Circuito 12-3
138
12.4 Sensores LDR
Los sensores LDR, son dispositivos que cambian su resistencia en función de la intensidad de luz,
cuanto mayor sea la intensidad de la luz, menor es la resistencia que ofrece la LDR. Estos
dispositivos son útiles para determinar, la presencia o ausencia de luz en el ambiente, es posible
concebir con estos dispositivos controles de luminosidad. La apariencia física y las vistas
posibles en el simulador ISIS, son las siguientes:

Figura 12-5

Las características eléctricas de estos dispositivos no son fácil de conseguir dado su simple
funcionamiento y gran variedad de fabricantes, las LDR, o fotoresistencias, se adquieren
comercialmente en tamaños diversos, esto implica que el rango de resistencia cambia en función
del tamaño, una LDR, de tamaño grande tiene rangos de cambio menor que una LDR pequeña.
Incluso los cambios de resistencia no son exactamente iguales en dos LDR, del mismo tamaño.
Estás razones hacen que la forma de mayor simplicidad para su uso sea la lectura de su
resistencia, o el voltaje que se desempeña en sus terminales. Para usar una LDR, la forma más
simple es realizar un divisor de voltaje con una resistencia fija, para entender este concepto se
puede observar el siguiente arreglo o circuito:


Circuito 12-4

Asumiendo la teoría básica de circuitos eléctricos para un divisor de voltaje se puede
implementar la siguiente ecuación:

R LDR
Vcc LDR
Vadc
+
=
) (
Ecuación 12-17

139
Despejando de la ecuación (12.17), el valor de la foto resistencia, LDR se obtiene la siguiente
ecuación:
Vout Vcc
R Vout
LDR
÷
=
) )( (
Ecuación 12-18

Dado que el valor de salida Vout se puede ingresar a una entrada AD, del microcontrolador se
puede asumir como Vadc, con la siguiente relación y un voltaje de referencia positivo de 5
voltios:
004887585 , 0 ) (
1023
) 5 )( (
Radc
Radc
Vadc = = Ecuación 12-19

Remplazando la ecuación (13.19) en la ecuación (13.18), y asumiendo que Vcc es de 5 voltios,
obtenemos la siguiente relación:

) 004887585 , 0 )( ( 5
) )( 04887585 , 0 )( (
Radc
R Radc
LDR
÷
= Ecuación 12-20

Para el caso particular de la simulación de este ejercicio la resistencia del divisor de voltaje será
de 10kΩ de está manera la ecuación para calcular la resistencia de la LDR, es:

) 004887585 , 0 )( ( 5
) 87585 , 48 )( (
Radc
Radc
LDR
÷
= Ecuación 12-21

Para la compilación y edición del programa se puede observar y analizar el siguiente código
fuente:

//Definición de pines del LCD
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D4 at RB0_bit;
//Definición de los TRIS del LCD
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D7_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB0_bit;

void main( void )
{
//Declaración de variables.
unsigned long Radc, DisI;
char Text[16];
//Inicio del LCD.
140
Lcd_Init();
//Borrado del cursor.
Lcd_Cmd(_LCD_CURSOR_OFF);
//Impresión de texto.
Lcd_Out( 1, 1, "Resistencia:");
while(1) //Bucle infinito.
{
//Lectura del canal análogo.
Radc=ADC_Read(0);
//Implementación del cálculo de la LDR, ecuación (13.20).
DisI = (Radc*48.87585533)/(5.0-Radc*0.004887585);
//Se convierte el valor entero largo, en cadena de texto.
LongToStr( DisI, Text );
//Se imprime la lectura del sensor.
Lcd_Out( 2, 1, Text );
//Retardo de 100m segundos.
delay_ms(100);
}
}

Para realizar la simulación en ISIS, se implementan los siguiente dispositivos: 16F877A, RES,
LM016L, TORCH_LDR, en el siguiente circuito electrónico:


Circuito 12-5
141
12.5 Sensores de humedad y temperatura
La humedad y la temperatura son dos variables físicas de amplio uso en la industria y para su
medida se pueden usar dispositivos que integran las dos lecturas simultáneamente, en un solo
encapsulado. La familia de sensores SHT7x permiten hacer la lectura de ambos parámetros, con
una calibración de fábrica que garantiza la fidelidad de las lecturas. La apariencia física y la vista
de estos dispositivos en ISIS, así como su distribución de pines, es la siguiente:


Figura 12-6

Pin Función
1 Reloj
2 Vdd, Poder
3 Gnd, Referencia
4 Datos

Tabla 12-4

Estos sensores entregan la información por medio de un puerto serial similar al protocolo I²C, sin
embargo no es exactamente igual, y por está razón no es posible usarlo. Esto implica realizar por
medio del código de programa, el protocolo de comunicación especificado en la hoja técnica del
dispositivo. El protocolo incluye una condición de inicio, bloque de datos tanto de lectura como
de escritura, y un bit de conformación o ACK. La comunicación se basa en dos pines uno de reloj
que siempre debe ser de salida y un pin de datos bidireccional, el cual es de drenador abierto, lo
que hace necesario implementar una resistencia pull-up en está línea a Vcc de 10kΩ.

Para realizar la comunicación con el sensor, el desarrollador debe implementar su propia librería
para el enlace de datos, a continuación se mostrará paso a paso un ejemplo que permite cumplir
con este cometido:

//Declaración de pines de comunicación.
sbit SHT_Reloj at RC0_bit;
sbit SHT_Datos at RC1_bit;

//Declaración de TRIS de los pines de comunicación.
sbit SHT_Reloj_Dire at TRISC0_bit;
sbit SHT_Datos_Dire at TRISC1_bit;

//Declaración de constantes.
const unsigned short RH_8bits_TEM_12bits = 1;
142
const unsigned short RH_12bits_TEM_14bits = 0;
const unsigned short Peso_bits[8] = {1,2,4,8,16,32,64,128};

//Bandera de resolución.
unsigned short RH_TEM;

//Función para estáblecer un estádo
//lógico en el pin de datos
void SHT_Bus( unsigned short e )
{
//Si el estádo es 1 se impone en alta
//impedancia el pin de datos.
if( e )
SHT_Datos_Dire = 1;
else
{
//Si el estádo es un 0 lógico
//se estáblece la salida en 0.
SHT_Datos_Dire = 0;
SHT_Datos = 0;
}
}

//Función para estáblecer el estádo del reloj.
void SHT_Reloj_Salida( unsigned short e )
{
//Se estáblece el estádo lógico en el reloj.
if( e )
SHT_Reloj = 1;
else
SHT_Reloj = 0;
//Periodo del reloj, para frecuencia de 1MHz.
delay_us(1);
}

//Función para crear la condición de inicio.
void SHT_Inicio( void )
{
//Secuencia de inicio.
SHT_Reloj_Salida(1);
SHT_Bus(0);
SHT_Reloj_Salida(0);
SHT_Reloj_Salida(1);
SHT_Bus(1);
SHT_Reloj_Salida(0);
delay_ms(1);
}


143
//Función para escribir un dato en el bus.
void SHT_Escribe_Bus( unsigned short d )
{
unsigned short n;
//Bucle para el envió serial.
for( n=7; n!=255; n-- )
{
//Se asigna el bit según su peso.
if( (d>>n)&1 )
SHT_Bus( 1 );
else
SHT_Bus( 0 );
//Generación de pulsos de reloj.
SHT_Reloj_Salida( 1 );
SHT_Reloj_Salida( 0 );
}
//Generación de bit ACK.
SHT_Bus( 1 );
SHT_Reloj_Salida( 1 );
SHT_Reloj_Salida( 0 );
}

//Función para leer un dato en el bus.
unsigned short SHT_Leer_Bus( void )
{
unsigned short n, Dato_SHT=0;
//Se configura el bus de entrada.
SHT_Bus( 1 );
//Bucle para recibir los bits de forma serial.
for( n=7; n!=255; n-- )
{
//Pulso de reloj.
SHT_Reloj_Salida( 1 );
//Se reciben los bits según su peso.
if( SHT_Datos )
Dato_SHT += Peso_bits[n];
SHT_Reloj_Salida( 0 );
}
//Se transmite la condición de ACK.
SHT_Bus( 0 );
SHT_Reloj_Salida( 1 );
SHT_Reloj_Salida( 0 );
//Se deja el bus de entrada.
SHT_Bus( 1 );
//Se retorna el valor de la lectura.
return Dato_SHT;
}

//Función para grabar el registro Status.
144
void SHT_Status_Graba( unsigned short s )
{
//Se transmite el valor del status.
SHT_Inicio();
SHT_Escribe_Bus( 0b00000110 );
SHT_Escribe_Bus( s );
}

//Función para leer el valor del Status.
unsigned short SHT_Status_Leer( void )
{
unsigned short st;
SHT_Inicio();
SHT_Escribe_Bus( 0b00000111 );
st=SHT_Leer_Bus();
SHT_Leer_Bus();
return st;
}

//Función para leer la temperatura.
float SHT_Leer_Tem( void )
{
float tem;
unsigned int LEC=0;
unsigned short DH, DL;
//Se aplica el protocolo de lectura.
SHT_Inicio();
SHT_Escribe_Bus( 0b00000011 );
//Espera para hacer la lectura.
delay_ms(310);
DH=SHT_Leer_Bus();
DL=SHT_Leer_Bus();
SHT_Leer_Bus();
LEC = DL + DH*256;
//Se implementan las ecuaciones para
//la temperatura.
if( RH_TEM )
tem = -40.1 + 0.04*LEC;
else
tem = -40.1 + 0.01*LEC;
//Se retorna la temperatura.
return tem;
}

float SHT_Leer_Hum( void )
{
float hum;
unsigned int LEC=0;
unsigned short DH, DL;
145
//Se aplica el protocolo de lectura.
SHT_Inicio();
SHT_Escribe_Bus( 0b00000101 );
//Espera para hacer la lectura.
delay_ms(310);
DH=SHT_Leer_Bus();
DL=SHT_Leer_Bus();
SHT_Leer_Bus();
LEC = DL + DH*256;
//Se implemetan las ecuacion para
//la humedad.
if( RH_TEM )
hum = -4.3468 + 0.5872*LEC - 0.00040845*LEC*LEC;
else
hum = -4.3468 + 0.0367*LEC - 0.0000015955*LEC*LEC;
//Se retorna la humedad.
return hum;
}

//Función para inicializar el sensor.
void Inicio_SHT7x( unsigned short St )
{
unsigned short n;
//Configuración de pines,
//y estádo inicial del bus de datos.
SHT_Reloj_Dire = 0;
//Se estáble el pin de datos en 1.
SHT_Bus(1);
//Se inicia el reloj en 0.
SHT_Reloj_Salida(0);
delay_ms(100);
//Se generan 9 ciclos de reloj, para reiniciar el sensor.
for(n=0; n<9; n++)
{
SHT_Reloj_Salida(1);
SHT_Reloj_Salida(0);
}
//Se configura el registro Status.
SHT_Inicio();
SHT_Status_Graba( St );
delay_ms(1);
RH_TEM = St;
}

Para el caso puntual de este ejemplo se complementa el código con la siguiente función main:

void main( void )
{
//Declaración de variables.
146
float Sen;
char Text[16];
//Inicio del LCD.
Lcd_Init();
//Se inicializa el sensor SHT7x.
Inicio_SHT7x(RH_12bits_TEM_14bits);
//Borrado del cursor.
Lcd_Cmd(_LCD_CURSOR_OFF);
//Impresión de texto.
Lcd_Out( 1, 1, "Tem:");
Lcd_Out( 2, 1, "Hum:");
while(1) //Bucle infinito.
{
//Se lee la temperatura y se imprime en el LCD.
Sen = SHT_Leer_Tem();
FloatToStr( Sen, Text );
Lcd_Out( 1, 6, Text );
//Se lee la humedad y se imprime en el LCD.
Sen = SHT_Leer_hum();
FloatToStr( Sen, Text );
Lcd_Out( 2, 6, Text );
}
}

Finalizada la edición y compilación del código fuente, se procede a realizar la simulación en
ISIS, con los dispositivos: 16F877A, LM016L, RES, y SHT71, en el siguiente circuito
electrónico:

Circuito 12-6
147
13 Comunicación con
dispositivos
La comunicación con diversos dispositivos se hace indispensable para realizar tareas como la
lectura de sensores, el sistema de control, y lectura de módulos como: relojes en tiempo real,
GPS, puntos inalámbricos, entre otros. En función de la necesidad y de las capacidades del
desarrollador, también es posible crear sus propios aplicativos de software para ordenadores
personales, estás aplicaciones se pueden desarrollar en paquetes, como Visual Basic, Java, C++,
etc. Para poder simular la conectividad con aplicativos propios e incluso con paquetes de
software como Hyper Terminal, se puede recurrir a aplicativos que crean en el ordenador puertos
seriales virtuales. De la misma forma el simulador ISIS permite implementar un puerto virtual
con conectividad a los puertos seriales físicos del ordenador. Uno de los aplicativos que permite
crear los puertos virtuales es Virtual Serial Port Kit, del cual se puede descargar una versión
demostrativa en la página www.virtual-serial-port.com La apariencia visual de este paquete es la
siguiente:

Figura 13-1

Sin embargo este es tan solo uno de los diversos aplicativos que cumplen con la misma función.
13.1 Módulos GPS
Los módulos GPS son dispositivos de comunicación inalámbrica que toman tramas de
información suministrada por los satélites artificiales geoestácionarios del planeta. Los módulos
GPS deben contar con la lectura de un mínimo de tres satélites para realizar un cálculo conocido
como triangulación; este consiste en medir la diferencia en tiempo entre las lecturas para
determinar la posición del GPS sobre la superficie de la tierra. La calidad de la lectura dependerá
del número de satélites simultáneos conectados al GPS; la lectura será de mayor precisión
cuantos más satélites estén conectados; sin embargo los GPS comerciales que se consiguen tienen
un margen de error aproximado de 3 metros, lo que implica que con respecto al diámetro del
globo terráqueo, el margen de error es casi del 0%. Los módulos GPS se pueden adquirir
148
comercialmente en varias presentaciones con encapsulado, o simplemente un circuito impreso
con el módulo. La apariencia física de ellos se puede apreciar en las siguientes figuras:


Figura 13-2

Los módulos GPS cuentan con puertos seriales que transmiten periódicamente la lectura de las
coordenadas geográficas leídas por el GPS. El periodo por defecto de los GPS, es un segundo. La
lectura de un GPS, es el conjunto de coordenadas esféricas sobre el globo terráqueo, usando
como referencia de medida el meridiano de Greenwich, y el paralelo del Ecuador. Para
contextualizar este concepto se puede observar el siguiente gráfico:



Figura 13-3

Longitud ángulo λ, Latitud ángulo φ.

Las coordenadas esféricas usan tres parámetros; el primero es la longitud, que es el ángulo λ,
representado en la gráfica y se mide a partir del meridiano de Greenwich, este ángulo puede ser
Este, u Oeste, y su magnitud puede variar de 0 a 90 grados. El segundo parámetro es la latitud,
que es el ángulo φ, representado en la gráfica, su punto de referencia es el paralelo del Ecuador y
puede ser Norte, o Sur, de la misma forma su magnitud varia de 0 a 90 grados. El último
parámetro es conocido como altitud, este determina la altura del punto coordenado con respecto
al nivel del mar, y su magnitud está definida en metros.

Los Módulos GPS, suministran una variedad de datos a demás de las coordenadas esféricas,
como: Número de satélites detectados, especificaciones mínimas del GPS, y características de
curso y velocidad, entre otras. Sin embargo para realizar la lectura de las coordenadas solo se
149
requiere la trama de información con el encabezado: $GPGGA. El siguiente ejemplo muestra la
lectura de las coordenadas:
Latitud: 30° 47‟ 43,47” Sur.
Longitud: 39° 57‟ 33,36” Oeste.
Altura: 2600 metros.

$GPGGA,000000.000,3047.724500,S,03957.556000,W,1,8,0,2600,M,0,M,,*7B

Información de Latitud:
$GPGGA,000000.000,3047.724500,S,03957.556000,W,1,8,0,2600,M,0,M,,*7B
Donde 3047.724500,S representa: 30° 47‟ 43,47” Sur. Es decir 30 grados, 47.7245 minutos, que
es equivalente a 47 minutos y 43,47 segundos.

Información de Longitud:
$GPGGA,000000.000,3047.724500,S,03957.556000,W,1,8,0,2600,M,0,M,,*7B
Donde: 03957.556000,W representa: 39° 57‟ 33,36” Oeste. Es decir 39 grados, 57.556 minutos,
que es equivalente a 57 minutos y 33,36 segundos.

Información del número de satélites, conectados:
$GPGGA,000000.000,3047.724500,S,03957.556000,W,1,8,0,2600,M,0,M,,*7B
Donde este campo 8, representa el número de satélites conectados al GPS, en este caso 8.

Información de altitud:
$GPGGA,000000.000,3047.724500,S,03957.556000,W,1,8,0,2600,M,0,M,,*7B
Donde 2600,M representa la magnitud en metros y la altitud de 2600 metros.

Los campos de información siempre son fijos y estás separados por una coma (,) entre si.

El siguiente ejemplo muestra una trama completa de datos entregados por un GPS:

$GPZDA,000000.000,30,12,1899,00,00*5F
$GPGGA,000000.000,3354.673167,S,04125.539667,W,1,8,0,2600,M,0,M,,*78
$GPGLL,3354.673167,S,04125.539667,W,000000.000,A,A*5F
$GPVTG,9,T,0,M,4.86,N,9.00500277932185,K,A*2A
$GPRMC,000000.000,A,3354.673167,S,04125.539667,W,4.86,9,301299,0,E,A*30
$GPGSA,A,3,1,2,3,4,5,6,17,22,,,,,0,0,0*2D
$GPGSV,2,1,8,1,0,24,0,2,0,24,0,3,0,24,0,4,0,24,0*46
$GPGSV,2,2,8,5,0,24,0,6,0,24,0,17,0,24,0,22,0,24,0*44
$GPRTE,1,1,C,0,*0B

Las tramas de información entregadas por un GPS, son de forma texto, por medio de caracteres
ASCII, y finalizadas con los caracteres ASCII, enter: 13, y retroceso del carro 10.

Para fines de diseño es importante identificar la velocidad de transmisión del módulo GPS y
ajustar la misma velocidad en el módulo USART del PIC.

Para iniciar el proceso de diseño con el microcontrolador es importante crear una rutina que
identifique las tramas y los encabezados para poder filtrar los datos de interés, que para este caso
son las coordenadas esféricas o geográficas. Es indispensable usar el módulo USART del PIC, en
150
este caso, y se establecerá una velocidad de transmisión de 4800 bits por segundo. Para evitar la
pérdida de datos se implementará la lectura de los datos seriales por medio de las interrupciones.
En primera instancia se hace un programa para el PIC 18F452, que este en capacidad de recibir y
almacenar las tramas en un búfer de datos:

char Dato;
char Bufer[50];
unsigned short Pos=0, Bandera=0;

//Declaración de la función de interrupciones.
void interrupt ( void )
{
//Se evalúa si la interrupción disparada por
//recepción serial.
if( PIR1.F5 )
{
//Se lee el dato de entrada.
Dato = UART1_Read();
switch( Dato )
{
case 13: Pos=0; //Se recibe el carácter Enter.
Bandera = 1;
break;
case 10:break; //Se recibe el retroceso del carro.
default:Bufer[Pos++]=Dato; //Se guardan los datos en el búfer.
Bufer[Pos]=0; //Se estáblece el fin de cadena.
break;
}

}
}

void main( void )
{
//Se configuran los puertos.
//
//Se activa la interrupción por
//recepción serial.
PIE1 = 0b00100000;
//Se desactivan las demás fuentes de interrupción.
PIE2 = 0;
//Se apagan las banderas de interrupción.
PIR2 = 0;
PIR1 = 0;
//Configuración del puerto serial a 4800 bps.
UART1_Init(4800);
//Se activan las interrupciones globales,
//y periféricas.
INTCON = 0b11000000;
151
while(1) //Bucle infinito.
{
//Se evalúa si una trama ha llegado.
if( Bandera )
{
//Aquí se debe analizar el contenido de la información del búfer.
Bandera=0; //Se apaga la bandera de llegada.
}
}
}

El procedimiento siguiente es implementar una función que identifique la trama de información y
los datos contenidos en ella, es importante recordar que los campos de información son fijos pero
no necesariamente el contenido de cada campo. Para estáblecer la información de la lectura se
usa una variable o estructura diseñada por el desarrollador:

typedef struct
{
short Grados_longitud;
float Minutos_longitud;
char Longitud;
short Grados_latitud;
float Minutos_latitud;
char Latitud;
float Altitud;
short Satelites;
}Coordenada;

A continuación se mostrará una función capaz de validar una trama para la lectura de las
coordenadas:

//Función para detectar la trama de coordenadas.
short EsGPGGA( char *trama )
{
//Está función retorna 1 si la trama es válida y 0 de lo contrario.
if( trama[0]=='$' && trama[1]=='G' && trama[2]=='P'
&& trama[3]=='G' && trama[4]=='G' && trama[5]=='A' )
return 1;
else
return 0;
}

A continuación se muestra un ejemplo de una función diseñada para leer los datos de las
coordenadas:

//Función para adquirir los datos del campo.
Coordenada LecturaGPS( char *trama )
{
//Declaración de variables.
152
Coordenada C;
char Texto[50];
unsigned short j,k;
//Valor inicial de las variables.
C.Grados_longitud=0;
C.Minutos_longitud=0;
C.Longitud=0;
C.Grados_latitud=0;
C.Minutos_latitud=0;
C.Latitud=0;
C.Altitud=0;
C.Satelites=0;

//Se verifica que el encabezado contenga el texto $GPGGA.
if( !EsGPGGA( trama ) )return C;
//Se filtra el campo 9 que contiene la altitud.
BuscarCampo( Trama, Texto, 9 );
//Se convierte el texto en el valor numérico.
C.Altitud = atof(Texto);
//Se filtra el campo 7 que contiene el número de satélites detectados.
BuscarCampo( Trama, Texto, 7 );
//Se convierte el texto en el valor numérico.
C.Satelites = atoi(Texto);
//Se filtra el campo 2 que contiene los minutos de la latitud.
BuscarCampo( Trama, Texto, 2 );
//Se convierte el texto en el valor numérico.
C.Minutos_latitud = atof( Texto + 2 );
//Se truncan los minutos de la latitud.
Texto[2]=0;
//Se convierte el texto en el valor numérico de los grados de la latitud.
C.Grados_latitud = atoi(Texto);
//Se filtra el campo 3 que contiene la orientación de la latitud.
BuscarCampo( Trama, Texto, 3 );
C.Latitud = Texto[0];
//Se filtra el campo 4 que contiene los minutos de la longitud.
BuscarCampo( Trama, Texto, 4 );
//Se convierte el texto en el valor numérico los minutos de la longitud.
C.Minutos_longitud = atof( Texto + 3 );
Texto[3]=0;
//Se convierte el texto en el valor numérico de los grados de la longitud.
C.Grados_longitud = atoi( Texto );
//Se filtra el campo 2 que contiene.
BuscarCampo( Trama, Texto, 5 );
C.Longitud = Texto[0];
return C;
}

La siguiente función permite buscar dentro de la trama enviada por el GPS, un campo de
información determinado:
153

//Función para buscar y filtrar un campo en la trama.
void BuscarCampo( char *Fuente, char *Destino, short campo )
{
unsigned short j=0,k=0;
//Bucle para buscar el campo.
while( j<campo )if( Fuente[k++]==',' )j++;
j=0;
//Bucle para copiar el campo.
while( !(Fuente[k]==',' || Fuente[k]=='*') )
{
Destino[j++]=Fuente[k++];
Destino[j]=0;
}
}

Está última función permite convertir la información de una lectura en una cadena de
coordenadas entendible en grados, minutos, segundos, y orientación:

//Función para encadenar los valores de una coordenada.
void Convertir_coordenada( char *coor, int grados, float minutos, char ll )
{
//Declaración de variables de la función.
char Text[20];
short j=0,k=0;
short g;
float s;

//Se pega en la cadena de texto, el valor de los grados.
IntToStr( grados, Text );
while( Text[j]!=0 )
{
if( Text[j]!=' ' )
coor[k++]=Text[j];
j++;
}
coor[k++]=':';
//Se separan los segundos de los minutos.
g = (short)minutos;
s = minutos - (float)g;
//Se pega en la cadena de texto, el valor de los minutos.
IntToStr( g, Text );
j=0;
while( Text[j]!=0 )
{
if( Text[j]!=' ' )
coor[k++]=Text[j];
j++;
}
154
coor[k++]=':';
//Se calculan los segundos, de la fracción de minutos.
s *= 60.0;
//Se pega en la cadena de texto, el valor de los segundos.
FloatToStr( s, Text );
j=0;
while( Text[j]!=0 )
{
if( Text[j]!=' ' )
coor[k++]=Text[j];
j++;
}
//Se pega en la cadena de texto, la orientación.
coor[k++]=' '; coor[k++]=ll; coor[k]=0;
}

Complementando las funciones antes citadas y la estructura final del programa con un display
LCD de caracteres y la comunicación del puerto serial se tiene el siguiente código fuente final:

// Definición de las conexiones del LCD.
sbit LCD_RS at RD4_bit;
sbit LCD_EN at RD5_bit;
sbit LCD_D4 at RD0_bit;
sbit LCD_D5 at RD1_bit;
sbit LCD_D6 at RD2_bit;
sbit LCD_D7 at RD3_bit;

//Definición de los registros TRIS del LCD.
sbit LCD_RS_Direction at TRISD4_bit;
sbit LCD_EN_Direction at TRISD5_bit;
sbit LCD_D4_Direction at TRISD0_bit;
sbit LCD_D5_Direction at TRISD1_bit;
sbit LCD_D6_Direction at TRISD2_bit;
sbit LCD_D7_Direction at TRISD3_bit;

//Estructura para la lectura de una coordenada esférica.
typedef struct
{
short Grados_longitud;
float Minutos_longitud;
char Longitud;
short Grados_latitud;
float Minutos_latitud;
char Latitud;
float Altitud;
short Satelites;
}Coordenada;

//Declaración de variables.
155
char Dato;
char Bufer[100];
unsigned short Pos=0;
unsigned short Bandera=0;
//Función para detectar la trama de coordenadas.
short EsGPGGA( char *trama )
{
//Está función retorna 1 si la trama es valida y 0 de lo contrario.
if( trama[0]=='$' && trama[1]=='G' && trama[2]=='P'
&& trama[3]=='G' && trama[4]=='G' && trama[5]=='A' )
return 1;
else
return 0;
}

//Función para buscar y filtrar un campo en la trama.
void BuscarCampo( char *Fuente, char *Destino, short campo )
{
unsigned short j=0,k=0;
//Bucle para buscar el campo.
while( j<campo )if( Fuente[k++]==',' )j++;
j=0;
//Bucle para copiar el campo.
while( !(Fuente[k]==',' || Fuente[k]=='*') )
{
Destino[j++]=Fuente[k++];
Destino[j]=0;
}
}

//Función para adquirir los datos del campo.
Coordenada LecturaGPS( char *trama )
{
//Declaración de variables.
Coordenada C;
char Texto[50];
unsigned short j,k;
//Valor inicial de las variables.
C.Grados_longitud=0;
C.Minutos_longitud=0;
C.Longitud=0;
C.Grados_latitud=0;
C.Minutos_latitud=0;
C.Latitud=0;
C.Altitud=0;
C.Satelites=0;

//Se verifica que el encabezado contenga el texto $GPGGA.
if( !EsGPGGA( trama ) )return C;
156
//Se filtra el campo 9 que contiene la altitud.
BuscarCampo( trama, Texto, 9 );
//Se convierte el texto en el valor numérico.
C.Altitud = atof(Texto);
//Se filtra el campo 7 que contiene el número de satélites detectados.
BuscarCampo( trama, Texto, 7 );
//Se convierte el texto en el valor numérico.
C.Satelites = atoi(Texto);
//Se filtra el campo 2 que contiene los minutos de la latitud.
BuscarCampo( trama, Texto, 2 );
//Se convierte el texto en el valor numérico.
C.Minutos_latitud = atof( Texto + 2 );
//Se truncan los minutos de la latitud.
Texto[2]=0;
//Se convierte el texto en el valor numérico de los grados de la latitud.
C.Grados_latitud = atoi(Texto);
//Se filtra el campo 3 que contiene la orientación de la latitud.
BuscarCampo( trama, Texto, 3 );
C.Latitud = Texto[0];
//Se filtra el campo 4 que contiene los minutos de la longitud.
BuscarCampo( trama, Texto, 4 );
//Se convierte el texto en el valor numérico los minutos de la longitud.
C.Minutos_longitud = atof( Texto + 3 );
Texto[3]=0;
//Se convierte el texto en el valor numérico de los grados de la longitud.
C.Grados_longitud = atoi( Texto );
//Se filtra el campo 2 que contiene.
BuscarCampo( trama, Texto, 5 );
C.Longitud = Texto[0];
return C;
}

//Función para encadenar los valores de una coordenada.
void Convertir_coordenada( char *coor, int grados, float minutos, char ll )
{
//Declaración de variables de la función.
char Text[20];
short j=0,k=0;
short g;
float s;

//Se pega en la cadena de texto, el valor de los grados.
IntToStr( grados, Text );
while( Text[j]!=0 )
{
if( Text[j]!=' ' )
coor[k++]=Text[j];
j++;
}
157
coor[k++]=':';
//Se separan los segundos de los minutos.
g = (short)minutos;
s = minutos - (float)g;
//Se pega en la cadena de texto, el valor de los minutos.
IntToStr( g, Text );
j=0;
while( Text[j]!=0 )
{
if( Text[j]!=' ' )
coor[k++]=Text[j];
j++;
}
coor[k++]=':';
//Se calculan los segundos, de la fracción de minutos.
s *= 60.0;
//Se pega en la cadena de texto, el valor de los segundos.
FloatToStr( s, Text );
j=0;
while( Text[j]!=0 )
{
if( Text[j]!=' ' )
coor[k++]=Text[j];
j++;
}
//Se pega en la cadena de texto, la orientación.
coor[k++]=' '; coor[k++]=ll; coor[k]=0;
}

//Declaración de la función de interrupciones.
void interrupt ( void )
{
//Se evalúa si la interrupción disparada por
//recepción serial.
if( PIR1.F5 )
{
//Se lee el dato de entrada.
Dato = UART1_Read();
switch( Dato )
{
case 13: Pos=0; //Se recibe el carácter Enter.
Bandera = 1;
break;
case 10:break; //Se recibe el retroceso del carro.
default:Bufer[Pos++]=Dato; //Se guardan los datos en el búfer.
Bufer[Pos]=0; //Se estáblece el fin de cadena.
break;
}
}
158
}

void main( void )
{
Coordenada C;
char Texto[20];
//Se configuran los puertos.
TRISB = 0;
PORTB = 0;
//Se activa la interrupción por
//recepción serial.
PIE1 = 0b00100000;
//Se desactivan las demás fuentes de interrupción.
PIE2 = 0;
//Se apagan las banderas de interrupción.
PIR2 = 0;
PIR1 = 0;
//Configuración del puerto serial a 4800 bps.
UART1_Init(4800);
//Se activan las interrupciones globales,
//y periféricas.
INTCON = 0b11000000;
//Inicialización del display LCD.
Lcd_Init();
Lcd_Cmd(_LCD_CURSOR_OFF);
while(1) //Bucle infinito.
{
//Se evalúa si una trama ha llegado.
if( Bandera )
{
//Se verifica que la trama sea valida para las coordenadas.
if( EsGPGGA( Bufer ) )
{
//Se leen los datos de las coordenadas y se guardan en la variable C.
C=LecturaGPS( Bufer );
//Se transcriben los datos de la latitud.
Convertir_coordenada( Texto, C.Grados_latitud, C.Minutos_latitud, C.Latitud );
//Se imprimen los datos de la latitud en el display LCD.
Lcd_Out(1,1,"Lat ");
Lcd_Out(1,5,Texto);
//Se transcriben los datos de la longitud.
Convertir_coordenada(Texto,C.Grados_longitud,C.Minutos_longitud,C.Longitud);
//Se imprimen los datos de la longitud en el display LCD.
Lcd_Out(2,1,"Lon ");
Lcd_Out(2,5,Texto);
//Se transcriben el valor de la altitud.
FloatToStr(C.Altitud,Texto);
//Se imprimen el valor de la altitud en el display LCD.
Lcd_Out(3,1,"Altitud: ");
159
Lcd_Out(3,9,Texto);
//Se transcriben el valor del número de satélites conectados.
IntToStr(C.Satelites,Texto);
//Se imprimen el número de satélites en el display LCD.
Lcd_Out(4,1,"Satelites: ");
Lcd_Out(4,11,Texto);
}
Bandera=0; //Se apaga la bandera de llegada.
}
}
}

Después de editar y compilar el programa con fuente de reloj de 20 Mhz, se procede a crear en
ISIS, un circuito con los siguientes dispositivos: PIC 18F452, LM044L, y el puerto virtual,
COMPIM. Este último se debe configurar a 4800 bps, tanto en el puerto virtual como el puerto
real, la vista de configuración del puerto virtual COMPIM debe ser la siguiente:

Figura 13-4

La configuración del puerto físico que en este caso está determinado como COM1, puede variar
en función de los puertos disponibles en el ordenador.
160
La construcción del circuito en ISIS debe ser como se aprecia en la siguiente gráfica:

Circuito 13-1

La conexión del GPS, en este caso se puede hacer por medio de un simulador de GPS, o por
medio de un GPS real conectado al puerto físico. Para la simulación del GPS, se puede usar el
paquete de software: Virtual GPS, o cualquier otro que funcione de la misma forma, la vista del
simulador de GPS, es la siguiente:

Figura 13-5
161
En este caso se pueden comparar los datos emitidos por el simulador y su verificación en el
circuito hecho en ISIS. De la misma forma se pueden cambiar los datos enviados por el simulador
de GPS. Se debe recordar hacer la creación de dos puertos virtuales cruzados, si se desea hacer la
simulación de todo el sistema con un simulador de GPS.
13.2 Módulos inalámbricos unidireccionales
Las comunicaciones inalámbricas entre dos puntos hacen parte fundamental de muchas
aplicaciones en el ámbito del control y el monitoreo de variables o estádos. La lectura de un
sensor, o el control de actuadores son posibles por medio de enlaces inalámbricos. La
comunicación usada por estos dispositivos es unidireccional o simplex. Los módulos
inalámbricos útiles para este fin funcionan con modulación ASK, o FSK, y tienen velocidades de
transmisión hasta 9600 bits por segundo. Este tipo de dispositivos se pueden conseguir de
fabricantes como Laipac Tech, o Linx Technologies, entre otros. La apariencia física de estos
dispositivos es la siguiente:


Figura 13-6

Estos dispositivos solo son un medio de transmisión inalámbrico por lo cual no tienen ningún
sistema de corrección ni detección de errores de transmisión, por está razón es importante hacer
dentro de la programación alguna rutina de detección de errores. Para fines de este ejemplo se
implementarán los radios Laipac. De igual manera las frecuencias portadoras de estos
dispositivos son variadas, y funcionan dentro del espectro de bandas libres para enlaces de
comunicación. La distribución de pines de estos dispositivos es la siguiente:




Figura 13-7


162
TLP434A
PIN Función
1 GND, Referencia
2 Entrada serial
3 Vcc, de 5 a 12 V
4 Antena
RLP434A
PIN Función
1 GND, Referencia
2 Salida Serial
3 Salida de prueba
4 Vcc
5 Vcc 5 V
6 GND, Referencia
7 GND, Referencia
8 Antena

Tabla 13-1

Para el envió de bits este ejemplo usa la modulación por ancho de pulso o PWM. El programa
representa un 1 lógico con un pulso en alto de 2m segundos de duración seguido por 1m segundo
en bajo, el 0 lógico se transmite con un pulso en alto de 1m segundo y 1m segundo en bajo, y
como estrategia de sincronismo se implementa un pulso en alto de 500u segundos, y 500u
segundos en bajo. Para este ejemplo se envían 16 pulsos de sincronismo, posteriormente se
envían 8 bits con el byte que se desea transmitir, y por último se transmiten 4 bits con un número
de 0 a 8 que contiene el número de unos que contiene el byte que se está transmitiendo como
estrategia de verificación. Este sistema no está en capacidad de corregir errores de transmisión,
pero si permite verificar el dato recibido, en otras palabras el sistema entrega un dato que es
recibido correctamente y si el dato se detecta con errores se descarta. Cuando un dato es recibido
correctamente se entrega un byte que puede valer de 0 a 255, y si el dato está dañado se entrega
un valor igual a -1.

Para la demostración de está comunicación se implementará una librería que transmite bytes con
las reglas antes mencionadas, de igual manera los microcontroladores implementados en está
librería son los PIC 16F628A con frecuencia de reloj de 4M Hz, sin embargo puede ser usada en
cualquier otro microcontrolador, la librería básica es la siguiente:


//Declaración de pines de comunicación.
sbit RXPIN at RB7_bit;
sbit RXPIN_TRIS at TRISB7_bit;
sbit TXPIN at RB6_bit;
sbit TXPIN_TRIS at TRISB6_bit;
//Declaración de constantes de peso binario.
const unsigned int Peso[12] = {1,2,4,8,16,32,64,128,256,512,1024,2048};


//Función para iniciar los pines de comunicación.
163
void Inicio_Radio_Tx( void )
{
TXPIN_TRIS=0;
TXPIN=0;
}

//Función para iniciar los pines de comunicación.
void Inicio_Radio_Rx( void )
{
RXPIN_TRIS=1;
}

//Función para determinar que bit se está recibiendo.
char Entra_Bit( void )
{
unsigned short T=0;
while( !RXPIN ); //Se espera el estádo 1 lógico.
//Se mide el tiempo en alto de la modulación.
while( RXPIN ){ delay_us(50); T++; }
//Se clasifica el bit recibido en función de su tiempo.
if( T > 38 && T < 42 )return '1'; //Aprox. 2m Seg.
if( T > 18 && T < 22 )return '0'; //Aprox. 1m Seg.
if( T > 8 && T < 12 )return 'S'; //Aprox. 500u Seg.
return 'E'; // Tiempo errado.
}

//Función para enviar un byte.
int Resibir_Dato( void )
{
unsigned short D, BIT, N_Bits;
int DATO;
DATO=0;
BIT=0;
//Se espera un bit de sincronismo.
while( Entra_Bit()!='S' );
//Se espera el último BIT de sincronismo.
while( (D=Entra_Bit())=='S' );
//Bucle para archivar el byte de entrada.
while( 1 )
{
//Se evalúa el BIT que llego.
switch( D )
{
case '1': DATO += Peso[BIT++];
break;
case '0': BIT++;
break;
case 'E':
case 'S': return -1;
164
}
//Si el número bits recibidos es 12, se
//evalúan los errores.
if( BIT==12 )
{
//Se cuentan los bits del dato de información.
N_Bits=0;
for( BIT=0; BIT<8; BIT++ )
if( (DATO>>BIT)&1 )N_Bits++;
//Se verifica que el número de unos sea igual.
//y se retorna el byte de entrada.
if( N_Bits == (DATO>>8)&0x0F )return DATO&0x00FF;
//Ocurre un error y se espera una nueva trama.
return -1;
}
//Se lee el siguiente bit.
D=Entra_Bit();
}
}

//Función para transmitir un byte.
void Enviar_Dato( unsigned short d )
{
unsigned short BITS, N_Bits=0;
//Se cuentan el número de bits a enviar.
for( BITS=0; BITS<8; BITS++ )
if( (d>>BITS)&1 )N_Bits++;

//Se envían 16 bits de sincronismo.
for( BITS=0; BITS<16; BITS++ )
{
TXPIN = 1; delay_us(500);
TXPIN = 0; delay_us(500);
}

//Se envían 8 bits de datos, el byte.
for( BITS=0; BITS<8; BITS++ )
{
TXPIN=1;
if( (d>>BITS)&1 )
delay_ms(2);
else
delay_ms(1);
TXPIN=0;
delay_ms(1);
}

//Se envían 4 bits de verificación
//con el número de unos en el byte.
165
for( BITS=0; BITS<4; BITS++ )
{
TXPIN=1;
if( (N_Bits>>BITS)&1 )
delay_ms(2);
else
delay_ms(1);
TXPIN=0;
delay_ms(1);
}
//Retardo final.
delay_ms(3);
}


Para concretar el ejemplo se realizan dos programas, uno para la transmisión, y otro para la
recepción, este sistema transmite el estádo de 4 pulsadores y se puede visualizar en el receptor
por medio de 4 LEDs. De igual manera la simulación puede soportar los dos microcontroladores
al mismo tiempo, pero no puede simular los radios. Para fines de simulación se implementa una
conexión directa, sin embargo el desarrollador puede posteriormente hacer una prueba real con
los radios.


Cada uno de los programas debe tener anexa la librería anteriormente citada, y anexar las
siguientes funciones main, en cada uno de los códigos fuente:


Función main para el transmisor:

void main( void )
{
//Declaración de variables.
unsigned char DATO;
//Configuración de puertos.
TRISB = 0xFF;
//Activación de las resistencias PULL-UP del puerto B.
OPTION_REG = 0;
//Inicio del puerto de transmisión.
Inicio_Radio_Tx();

while(1) //Bucle infinito.
{
//Adquisición de estádos de los pulsadores.
DATO = (~PORTB)&0x0F;
//Transmisión del byte.
Enviar_Dato( DATO );
}

}
166

Función main para el receptor:

void main( void )
{

//Declaración de variables.
int DATO;
//Configuración de puertos.
TRISB = 0;
PORTB = 0;
//Inicio del puerto de recepción.
Inicio_Radio_Rx();

while(1) //Bucle infinito.
{
//Lectura del byte de entrada.
DATO = Resibir_Dato();
//Verificación de errores, y visualización del byte.
if( DATO!=-1 )
PORTB =DATO;
}

}


Para realizar la simulación se deben usar los siguientes dispositivos: PIC 16F628A, RES, LED-
RED, y BUTTON, en el circuito que se puede ver a continuación:



Circuito 13-2

167
Para fines prácticos con los radios Laipac, se bebe hacer un par de circuitos como los que se
pueden ver en la siguiente gráfica:


Circuito 13-3
13.3 Módulos inalámbricos bidireccionales
En algunos casos las comunicaciones deben ser imperativamente bidireccionales, como el caso de
los controles realimentados y los sistemas de comunicación full duplex. Para cumplir con el
propósito de realizar la comunicación full duplex se pueden implementar módulos de radio
prediseñados como los XBee, o XBee PRO, estos dispositivos son módulos MODEM, que
trabajan sobre la banda de 2,4GHz, e implementan el estándar IEEE 801.15.4. Estos son
dispositivos que trabajan con tecnología de 3 voltios de bajo consumo, y velocidades de
transmisión de 1200 a 115200 bits por segundo. El alcance de los radios depende de la antena y
de la referencia de los mismos. Los módulos XBee tienen un alcance de 100 metros con una línea
de vista directa y 1000 metros en los módulos XBee PRO. Los módulos XBee, son de fácil
instalación y programación por medio de un paquete de software, suministrado por el fabricante,
este se denomina: X-CTU, este aplicativo se puede descargar gratuitamente en la página:
www.digi.com, este programa estáblece con el radio la comunicación para su programación por
medio de un puerto serial COM. La simulación de estos radios no es posible con ISIS; pero dado
el óptimo desempeño de los mismos es equivalente en ISIS a realizar una conexión cruzada entre
Tx y Rx, de los microcontroladores. Los módulos XBee incorporan dentro de su hardware y
software un procedimiento de detección y corrección de errores en la transmisión. De la misma
forma es posible estáblecer una conexión inalámbrica entre un microcontrolador con un
ordenador personal por medio de un puerto serial COM. Para trabajar y programar los radios
XBee es necesario crear o implementar un acople de hardware para hacer la conexión con un
puerto serial COM, para este proposito se debe tener presente el siguiente circuito electrónico:


168
Módulo de acople con un ordenador personal:


Circuito 13-4

El anterior circuito electrónico puede ser construido en una protoborad, sin embargo se puede
construir un circuito impreso como el que se puede apreciar en las siguientes gráficas:





Figura 13-8


169
La vista de este circuito impreso finalizado y del módulo XBee, es la siguiente:



Figura 13-9

Los módulos XBee por defecto vienen programados para establecer una comunicación a 9600
bits por segundo. Sin embargo se puede cambiar por medio del programa X-CTU. La
reprogramación del radio XBee, permite cambiar los parámetros de velocidad, canal de
comunicación entre 16 posibles, se puede asignar un identificador del radio y asignar dirección de
trabajo en una red Zigbee.

Para hacer una demostración del funcionamiento con los módulos XBee, se puede editar y
analizar el siguiente código fuente:

//Declaración de pines de control para LCD.
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;
sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;


void main( void )
{
//Declaración de variables.
char Texto[30];
char Dato;
unsigned short N=0;
//Inicio de los módulos USART, y LCD.
Lcd_Init();
UART1_Init(9600);
170
//Se imprimen los textos de inicio.
Lcd_Out(1,1,"Datos de entrada:");
Lcd_Out(2,1,"");
while(1) //Bucle infinito.
{
//Se evalúa si un dato llega por el puerto serial.
if( UART1_Data_Ready() )
{
//Se lee el dato de entrada.
Dato = UART1_Read();
//Se evalúa que Dato llega por el puerto serial.
switch( Dato )
{
//Se evalúa la llegada de un enter.
case 13: N=0;
//Se limpian las líneas del LCD.
Lcd_Out(2,1," "); //Se imprimen 20 espacios vacíos.
Lcd_Out(2,1," "); //Se imprimen 20 espacios vacíos.
Lcd_Out(4,1," "); //Se imprimen 20 espacios vacíos.
//Se posiciona el cursor en la segunda línea.
Lcd_Out(2,1,"");
//Se transmite un enter y un retroceso del carro.
UART1_Write(13);
UART1_Write(10);
//Se transmite el texto: Texto Xbee.
UART1_Write_Text("Texto Xbee: ");
//Se retransmite la cadena recibida.
UART1_Write_Text(Texto);
//Se transmite un enter y un retroceso del carro.
UART1_Write(13);
UART1_Write(10);
break;

default: //Se guardan los datos entrantes en la cadena de caracteres Texto.
Texto[N++]=Dato;
Texto[N]=0;
//Se refresca la información en el LCD.
Lcd_Out(2,1,Texto);
//Se retransmite eco del dato de entrada.
UART1_Write(Dato);
break;
}
}
}
}

Para fines de simulación se puede implementar un circuito con los dispositivos: 16F877A,
LM044L, y el puerto virtual VIRTUAL TERMINAL, como se puede apreciar en el siguiente
esquema:
171

Circuito 13-5

Para fines prácticos se puede usar el módulo de acople con un ordenador personal, antes citado
con una sesión de Hyper terminal de Windows. Como parte complementaria se puede hacer un
circuito real como se puede ver en la siguiente gráfica:


Circuito 13-6
172
13.4 Comunicación RS 485
El protocolo RS 485, es un formato de comunicación serial que usa como estrategia de
transmisión, la diferencia de potencial entre dos terminales denominadas A, y B. Cuando la
diferencia de potencial es positiva el protocolo representa un 1 lógico y cuando la diferencia es
negativa representa un 0 lógico. La comunicación RS 485, implementa un par trenzado de cobre
para la transmisión de datos, como características notables de este protocolo se tiene que puede
estáblecer la comunicación en una topología en forma de bus, con un alcance máximo de 1000
metros sin la necesidad de implementar dispositivos de repetición. De igual forma es posible
conectar al bus de datos un máximo de 32 nodos, con una conectividad half duplex, esto quiere
decir que la comunicación es bidireccional pero no al mismo tiempo. El protocolo RS 485
permite estáblecer comunicaciones con una velocidad de hasta 2,5M bits por segundo. Para la
implementación de este tipo de comunicación se usan convertidores integrados que permiten
hacer un enlace de niveles TTL, a RS 485, el circuito integrado más popular para estás
aplicaciones es el MAX485, que es un integrado de 8 pines. La distribución de pines y la
apariencia física de este dispositivo es la siguiente:


Figura 13-10

Los pines A, y B, son el bus de datos bidireccional, los pines DE, y RE, son los habilitadores de
los búfer de transmisión y recepción respectivamente. Los pines DI, y RO, son respectivamente
los terminales de transmisión y recepción. Como medida adicional se deben implementar un par
de resistencias de 120Ω en paralelo a los pines A, y B, en el primer y en el último terminal de la
red RS 485.

Para demostrar una comunicación con este tipo de redes se realizará en este capítulo una
comunicación de half duplex entre dos microcontroladores, para este fin se usarán los módulos
USART, y un control adicional con los pines de habilitación. Para contextualizar el uso de la
comunicación RS 485, se puede observar y analizar el siguiente código de programa que permite
enviar el estádo de un potenciómetro para controlar la intensidad de luz de una lámpara en el
terminal opuesto:

void main( void )
{
//Declaración de variables.
unsigned short DATO;
//Configuración de puertos.
TRISB = 0b11111110;
PORTB = 0; //Se Configura la MAX485 como receptor.
173
//Se activan las resistencias PULL-UP.
OPTION_REG = 0;
//Se configura el módulo USART a 250000 bps.
UART1_Init(250000);
//Se configura el módulo PWM con una frecuencia de 1K Hz.
PWM1_Init(1000);
//Se inicia el módulo PWM con 0%.
PWM1_Start();
PWM1_Set_Duty(0);
while(1) //Bucle infinito.
{
//Se evalúa si un dato ha llegado.
if( UART1_Data_Ready() )
{
DATO = UART1_Read();
//Se evalúa si el dato de llegada vale 170.
if( DATO==170 )
{
//Se espera el siguiente dato.
while( !UART1_Data_Ready() );
DATO = UART1_Read();
//Se actualiza el ciclo útil del PWM.
PWM1_Set_Duty(DATO);
}
}
//Se evalúa si se ha pulsado el botón.
if( Button(&PORTB, 1, 50, 0) )
{
PORTB = 1; //Se Configura la MAX485 como transmisor.
//Se lee el canal análogo con el voltaje del
//potenciómetro, y se guardan los 8 bits de mayor peso
//en el la variable DATO.
DATO = (ADC_Read(0)>>2)&0xFF;
//Se transmite la bandera 170, para sincronizar la comunicación.
UART1_Write(170);
//Se hace una pausa de 1m segundos entre datos.
delay_ms(1);
//Se transmite el valor de la variable DATO.
UART1_Write(DATO);
//Se espera a que se suelte el botón.
while( Button(&PORTB, 1, 50, 0) );
PORTB = 0; //Se Configura la MAX485 como receptor.
}
}
}

El anterior programa es simétrico, es decir que los dos microcontroladores de la simulación se
cargan con el mismo programa. El circuito electrónico recurre a una lámpara incandescente con
el fin de mostrar la intensidad de la luz, la diferencia de potencial se obtiene del módulo PWM,
174
con la conversión digital análogo. Para fines ideales de la simulación las características por
defecto de la lámpara son reconfiguradas cambiando la impedancia de 24Ω a 100KΩ y su voltaje
nominal de 12V a 5V. Para realizar la simulación se deben implementar los siguientes
dispositivos: 16F877A, RES, MAX487, LAMP, BUTTON, CAP, y POT-HG. La teoría vista en
este capítulo cita el dispositivo MAX485, sin embargo para fines de simulación se usa el
MAX487, que tiene características y funcionamiento similar. El circuito a implementar es el
siguiente:


Circuito 13-7

Cuando la simulación está corriendo, cada uno de los módulos MAX, están configurados como
receptores, y en el momento que se pulsa un botón el microcontrolador hace la conversión
análoga digital, y transmite está información para configurar la intensidad de luz en el módulo
opuesto.
13.5 Módulos inalámbricos infrarrojos
La comunicación infrarroja es una estrategia práctica para sistemas que no requieren una gran
velocidad de transmisión, y que admiten línea de vista directa. El alcance de un enlace infrarrojo
depende de la potencia del emisor. Sin embargo este estándar es utilizado en la comunicación de
control de los electrodomésticos convencionales como: televisores, equipos de sonido,
reproductores de video, aires acondicionados entre otros, tiene un alcance promedio de 10
metros.

La transmisión de datos se realiza por medio de diodos emisores de luz infrarroja, con una
modulación digital por amplitud, ASK. Los estándares de comunicación para los controles
infrarrojos implementan frecuencias portadoras de 36, 38, y 40 K Hz entre otras. La recepción de
los datos se hace por medio de un demodulador ASK, que puede recibir datos hasta los 2,4K bits
por segundo. Los demoduladores ASK, infrarrojos son dispositivos de carácter integrado que se
pueden conseguir comercialmente. Estos poseen tres terminales que corresponden a dos de
polarización, Vcc o poder, y referencia, y una salida demodulada en amplitud, con colector
abierto, esto quiere decir que se requiere el uso de una resistencia PULL-UP, a Vcc, en este
último terminal. Algunos fabricantes incorporan está resistencia en la integración del dispositivo.
175
Para fines de diseño es importante tener presente que la salida del demodulador está invertida, es
decir que cuando el dispositivo detecta la señal portadora la salida del demodulador se activa en
bajo y se activa en alto cuando se detecta la ausencia de la misma. La apariencia física de estos
dispositivos es la siguiente:

Figura 13-11

La distribución de pines puede variar de un fabricante a otro, sin embargo siempre serán las
mismas tres, Vcc, Referencia, y Salida, para fines prácticos se hace imperativo consultar la ficha
técnica del dispositivo que se use.

Para la simulación de este tipo de enlaces se puede usar en ISIS el módulo virtual: IRLINK, el
cual tiene la siguiente apariencia en el simulador:


Figura 13-12

Este dispositivo virtual simula el enlace inalámbrico por medio del LED infrarrojo, y el
dispositivo demodulador, de igual manera permite editar la frecuencia de la señal portadora ASK.
Para contextualizar el desempeño de estos enlaces se debe observar y analizar el siguiente código
fuente, que usa el módulo PWM, como fuente portadora, y el módulo USART, como señal
moduladora, la información transmitida son datos ASCII, con textos fijos que se pueden ver en
un hyper terminal virtual:

void main( void )
{
//Configuración del módulo USART a 2400 bps.
UART1_Init(2400);
//Configuración de PWM para crear la frecuencia portadora a 38K Hz.
PWM1_Init(38000);
PWM1_Start();
176
//Se configura el ciclo útil del PWM al 50%.
PWM1_Set_Duty(127);

while(1) //Bucle infinito.
{
//Se transmite información en un texto fijo.
UART1_Write_Text("Transmision de datos infrarrojos.");
//Se envía los caracteres enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
//Se transmite información en un texto fijo.
UART1_Write_Text("Está emision está modulada a 38K Hz.");
UART1_Write(13);
UART1_Write(10);
//Se transmite información en un texto fijo.
UART1_Write_Text("Con una velocidad de transmision de 2400 bps.");
//Se envía los caracteres enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
//Se envía los caracteres enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
//Se realiza una pausa de 200m segundos.
delay_ms(200);
}
}



Para la simulación de este ejemplo se implementan en ISIS los dispositivos: 16F628A, RES,
1N4148, IRLINK, 74LS04, y el VIRTUAL TERMINAL. El circuito electrónico debe ser
ensamblado de la siguiente manera:


Circuito 13-8
177
La modulación se realiza por medio de la multiplicación de la señal moduladora, con la señal
portadora, para esto se implementa una compuerta AND, diseñada con diodos rectificadores de
switcheo rápido. En funcionamiento de la simulación, el hiperterminal virtual debe mostrar los
datos de texto de la siguiente forma:


Figura 13-13
13.6 Comunicación con memorias SD
Las memorias de almacenamiento masivo SD, pueden ser manipuladas por los
microcontroladores de alta gama como la familia 18F. Para leer o escribir sobre una memoria SD,
se requiere trabajar con segmentos de memoria de 512 bytes. Para este fin es necesario contar con
espacios de memoria RAM, de gran capacidad como los microcontroladores de la familia 18F. La
comunicación de las memorias SD, requieren de un protocolo serial conocido como SPI, para
este fin los microcontroladores cuentan con un módulo de comunicación serial de este tipo. La
alimentación de voltaje de las memorias SD es de 3 voltios, para esto se requiere hacer un acople
de voltajes con el microcontrolador por medio de un divisor de voltaje con resistencias. La
apariencia física y la distribución de pines de este tipo de memorias son las siguientes:


Figura 13-14

La capacidad de las memorias SD actuales es del orden de los Giga bytes, sin embargo la máxima
capacidad que soporta la librería para estás memorias en MikroC PRO, es de 1G bytes. Para la
manipulación de este tipo de memorias el compilador MikroC PRO, cuenta con una librería
178
denominada: Mmc. La cual posee tres funciones para estáblecer de forma simple la comunicación
y control de la memoria. La función: unsigned char Mmc_Init( void ); está función inicializa la
comunicación con la memoria SD, y retorna 0 si la comunicación se estábleció exitosamente, o
retorna 1 si algún error se presenta. La función: unsigned char Mmc_Read_Sector(unsigned long
sector, char *dbuff); está permite leer un sector de 512 bytes, guardando la lectura en el
apuntador dbuff, e iniciando en el sector definido por el parámetro sector. La función: unsigned
char Mmc_Write_Sector(unsigned long sector, char *dbuff); está función trabaja de forma
similar a la función de lectura, en las dos funciones se retorna el valor 0 si la operación culmina
exitosamente o retornan 1 si un fallo se presenta. Para la comunicación física se requieren 4
conexiones que son: una línea de reloj, dos líneas de datos, entrada y salida, y un selector de
pastilla.
Para contextualizar el funcionamiento de estos dispositivos se puede observar y analizar el
siguiente código fuente:

// Pines de conexión para el selector de pastilla
sbit Mmc_Chip_Select at RC2_bit;
sbit Mmc_Chip_Select_Direction at TRISC2_bit;


void main( void )
{
//Declaración de variables.
char SD;
char Texto[50];
long SEC=0;
unsigned int Cont;
unsigned short Menu=0;
unsigned short NN;
char Dato;
unsigned short Bufer[512];
//Inicio del puerto serial a 9600 bps.
UART1_Init(9600);
//Inicio del puerto SPI, para la memoria SD.
SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV64, _SPI_DATA_SAMPLE_MIDDLE,
_SPI_CLK_IDLE_HIGH, _SPI_LOW_2_HIGH);
//Inicio de la memoria.

if( Mmc_Init()==0 )
{
//Confirmación exitosa de conexión.
SD = 1;
UART1_Write_Text("// La memoria fue iniciada exitosamente. //");
UART1_Write(13);
UART1_Write(10);
UART1_Write(13);
UART1_Write(10);
179
}
else
{
//Confirmación fallida de conexión.
SD = 0;
UART1_Write_Text("ERROR no se puede iniciar la memoria !!!");
UART1_Write(13);
UART1_Write(10);
UART1_Write(13);
UART1_Write(10);
}

while(1) //Bucle infinito.
{

if( SD ) //Se ejecuta el Menú si la conexión fue exitosa.
{
//Se despliega el Menú.
UART1_Write_Text( "1. Grabar un Sector." );
UART1_Write(13);
UART1_Write(10);
UART1_Write_Text( "2. Leer un Sector." );
UART1_Write(13);
UART1_Write(10);
UART1_Write_Text( "Digite una opcion: " );
Menu = 0;
//Bucle para esperar la opción.

while( Menu==0 )
{

if( UART1_Data_Ready() )
{
Dato = UART1_Read();
UART1_Write(Dato);
UART1_Write(' ');
switch( Dato )
{
case '1': Menu=1; break;
case '2': Menu=2; break;
default: break;
}
}

}
//Envió del enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
//Captura del sector de lectura o escritura.
180
UART1_Write_Text( "Digite el numero del sector de memoria, y pulse ENTER: " );
SEC=-1;
NN=0;
Texto[0]=0;
//Bucle para digital el sector.

while( SEC==-1 )
{

if( UART1_Data_Ready() )
{
Dato = UART1_Read();
UART1_Write(Dato);
switch( Dato )
{
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0': Texto[NN++]=Dato;
Texto[NN]=0;
break;
case 13: SEC=atol(Texto);
default : break;
}
}

}
//Envió del enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
//Se graban o se leen los sectores.
if( Menu==1 )
{
UART1_Write_Text( "Se grabara el sector: " );
LongToStr( SEC, Texto);
UART1_Write_Text( Texto );
//Envió del enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
//Se borra el Búfer.
for( Cont=0; Cont<512; Cont++ )
Bufer[Cont]=0;
//Bucle para capturar los datos a grabar.
181
while( Menu )
{
if( UART1_Data_Ready() )
{
Dato = UART1_Read();
UART1_Write(Dato);
switch( Dato )
{
case 13: Menu=0; break;
default : Bufer[NN++]=Dato;
break;
}
}
}
//Se graban los 512 datos del sector en la memoria.
Mmc_Write_Sector( SEC, Bufer);
//Envió del enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
UART1_Write_Text( "Fin de la grabacion." );
}
else
{
UART1_Write_Text( "Se leera el sector: " );
LongToStr( SEC, Texto);
UART1_Write_Text( Texto );
//Envió del enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
//Se leen 512 datos del sector.
Mmc_Read_Sector( SEC, Bufer);
//Bucle para imprimir los datos leídos de la memoria.
for( Cont=0; Cont<512; Cont++ )
UART1_Write( Bufer[Cont] );
}
//Envió del enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
//Envió del enter y retroceso del carro.
UART1_Write(13);
UART1_Write(10);
}
}
}

Como medida adicional se debe inicializar el puerto SPI, por medio de la función:
SPI1_Init_Advanced. Para comprobar el funcionamiento de de este programa se debe construir en
ISIS, un circuito con los dispositivos: 18F452, MMC, y VIRTUAL TERMINAL, como se puede
apreciar en la siguiente gráfica:
182

Circuito 13-9

Para fines de simulación las conexiones con la memoria se hacen directamente como se puede ver
en el anterior circuito. Sin embargo se debe tener presente que para fines prácticos la memoria
trabaja con 3 voltios, y se debe acoplar con un arreglo de resistencias como el siguiente:


Circuito 13-10
183
13.7 Relojes en tiempo real
Algunas aplicaciones requieren de un reloj en tiempo real, para tener dominio del tiempo de
eventos, acciones, y demás. Este tipo de relojes son circuitos integrados con baterías propias, y
cristales de cuarzo para definir las unidades de tiempo. Los relojes en tiempo real se comunican
por medio del protocolo I²C. Una de las referencias comerciales más populares es el DS1307, que
puede contar años, meses, días, hora, minutos, y segundos. La apariencia física y la vista en ISIS
de estos dispositivos es la siguiente:



Figura 13-15

Este circuito integrado cuenta con dos pines de comunicación un pin de salida oscilante, y una
entrada para la batería, además para fines prácticos cuenta con dos terminales para instalar un
cristal de cuarzo de 32,768KHz. El siguiente código fuente en lenguaje C, muestra una serie de
funciones que forman una librería que permiten manipular este tipo de relojes:

//Declaración de estructuras
typedef struct
{
unsigned short Hor, Min, Seg;
}Hora;

typedef struct
{
unsigned short Dia, Fec, Mes, Ano;
}Fecha;

//Función para definir dirección de memoria.
void DS1307SetDir( unsigned short dir )
{
I2C1_Start();
I2C1_Wr(0xD0);
I2C1_Wr(dir);
}

//Función para convertir de código Bcd a Entero.
unsigned short BcdToShort( unsigned short bcd )
{
unsigned short LV, HV;
LV = bcd&0x0F;
HV = (bcd>>4)&0x0F;
184
return LV + HV*10;
}

//Función para convertir de Entero a Bcd.
unsigned short ShortToBcd( unsigned short valor )
{
unsigned short HV, LV;
HV = valor/10;
LV = valor - HV*10;
return LV + HV*16;
}

//Función para inicializar el DS1307.
void DS1307Inicio( void )
{
unsigned short VAL[7], HV, LV, DATO;
I2C1_Init(100000); //Inicio del bus I2C.
delay_ms(50); //Retardo.
//Lectura de las primeras 7 direcciones.
DS1307SetDir(0);
I2C1_Repeated_Start();
I2C1_Wr(0xD1);
VAL[0] = I2C1_Rd(1);
VAL[1] = I2C1_Rd(1);
VAL[2] = I2C1_Rd(1);
VAL[3] = I2C1_Rd(1);
VAL[4] = I2C1_Rd(1);
VAL[5] = I2C1_Rd(1);
VAL[6] = I2C1_Rd(0);
I2C1_Stop();
delay_ms(50); //Retardo.
//Validación y corrección de información,
//como hora y fecha.
DATO = BcdToShort( VAL[0] );
if( DATO > 59 )VAL[0]=0;
DATO = BcdToShort( VAL[1] );
if( DATO>59 )VAL[1]=0;
DATO = BcdToShort( VAL[2] );
if( DATO>23 )VAL[2]=0;
DATO = BcdToShort( VAL[3] );
if( DATO>7 || DATO==0 )VAL[3]=1;
DATO = BcdToShort( VAL[4] );
if( DATO>31 || DATO==0 )VAL[4]=1;
DATO = BcdToShort( VAL[5] );
if( DATO>12 || DATO==0 )VAL[5]=1;
DATO = BcdToShort( VAL[6] );
if( DATO>99 )VAL[6]=0;
//Grabación de las primeras 7 direcciones.
DS1307SetDir(0);
185
I2C1_Wr(VAL[0]);
I2C1_Wr(VAL[1]);
I2C1_Wr(VAL[2]);
I2C1_Wr(VAL[3]);
I2C1_Wr(VAL[4]);
I2C1_Wr(VAL[5]);
I2C1_Wr(VAL[6]);
I2C1_Wr(0x10); //Se activa la salida oscilante 1Hz.
I2C1_Stop();
delay_ms(50); //Retardo.
}

//Función para grabar la hora minutos y segundos.
void DS1307SetHora( Hora h )
{
DS1307SetDir(0);
I2C1_Wr( ShortToBcd(h.Seg) );
I2C1_Wr( ShortToBcd(h.Min) );
I2C1_Wr( ShortToBcd(h.Hor) );
I2C1_Stop();
}

//Función para grabar el día, fecha, mes, y año.
void DS1307SetFecha( Fecha f )
{
DS1307SetDir(3);
I2C1_Wr( ShortToBcd(f.Dia) );
I2C1_Wr( ShortToBcd(f.Fec) );
I2C1_Wr( ShortToBcd(f.Mes) );
I2C1_Wr( ShortToBcd(f.Ano) );
I2C1_Stop();
}

//Función para leer la hora minutos y segundos.
Hora DS1307GetHora( void )
{
Hora H;
unsigned short VAL[3];
DS1307SetDir(0);
I2C1_Repeated_Start();
I2C1_Wr(0xD1);
VAL[0] = I2C1_Rd(1);
VAL[1] = I2C1_Rd(1);
VAL[2] = I2C1_Rd(0);
I2C1_Stop();
H.Seg = BcdToShort( VAL[0] );
H.Min = BcdToShort( VAL[1] );
H.Hor = BcdToShort( VAL[2] );
return H;
186
}

//Función para leer el día, fecha, mes, y año.
Fecha DS1307GetFecha( void )
{
Fecha F;
unsigned short VAL[4];
DS1307SetDir(3);
I2C1_Repeated_Start();
I2C1_Wr(0xD1);
VAL[0] = I2C1_Rd(1);
VAL[1] = I2C1_Rd(1);
VAL[2] = I2C1_Rd(1);
VAL[3] = I2C1_Rd(0);
I2C1_Stop();
F.Dia = BcdToShort( VAL[0] );
F.Fec = BcdToShort( VAL[1] );
F.Mes = BcdToShort( VAL[2] );
F.Ano = BcdToShort( VAL[3] );
return F;
}

// Funciones para leer y grabar datos individuales //
//Función para leer las horas.
unsigned short DS1307GetHoras( void )
{
Hora h;
h=DS1307GetHora();
return h.Hor;
}
//Función para leer los minutos.
unsigned short DS1307GetMinutos( void )
{
Hora h;
h=DS1307GetHora();
return h.Min;
}
//Función para leer los segundos.
unsigned short DS1307GetSegundos( void )
{
Hora h;
h=DS1307GetHora();
return h.Seg;
}
//Función para grabar las horas.
void DS1307SetHoras( unsigned short ho )
{
Hora h;
h=DS1307GetHora();
187
h.Hor = ho;
DS1307SetHora( h );
}
//Función para grabar los minutos.
void DS1307SetMinutos( unsigned short mi )
{
Hora h;
h=DS1307GetHora();
h.Min = mi;
DS1307SetHora( h );
}

//Función para grabar los segundos.
void DS1307SetSegundos( unsigned short se )
{
Hora h;
h=DS1307GetHora();
h.Seg = se;
DS1307SetHora( h );
}

//Función para leer el día de la semana.
unsigned short DS1307GetDias( void )
{
Fecha f;
f=DS1307GetFecha();
return f.Dia;
}

//Función para leer la fecha del mes.
unsigned short DS1307GetFechas( void )
{
Fecha f;
f=DS1307GetFecha();
return f.Fec;
}

//Función para leer el mes del año.
unsigned short DS1307GetMeses( void )
{
Fecha f;
f=DS1307GetFecha();
return f.Mes;
}

//Función para leer el año.
unsigned short DS1307GetAnos( void )
{
Fecha f;
188
f=DS1307GetFecha();
return f.Ano;
}

//Función para grabar el dia de la semana.
void DS1307SetDias( unsigned short di )
{
Fecha f;
f=DS1307GetFecha();
f.Dia = di;
DS1307SetFecha(f);
}

//Función para grabar la fecha del mes.
void DS1307SetFechas( unsigned short fe )
{
Fecha f;
f=DS1307GetFecha();
f.Fec = fe;
DS1307SetFecha(f);
}

//Función para grabar el mes del año.
void DS1307SetMeses( unsigned short me )
{
Fecha f;
f=DS1307GetFecha();
f.Mes = me;
DS1307SetFecha(f);
}

//Función para grabar el año.
void DS1307SetAnos( unsigned short an )
{
Fecha f;
f=DS1307GetFecha();
f.Ano = an;
DS1307SetFecha(f);
}

Para realizar la simulación de un ejemplo con este tipo de reloj, se puede implementar la anterior
librería y el siguiente código fuente correspondiente a la función main:

void main( void )
{
//Declaración de variables.
char Text[50];
unsigned short DATO;
//Inicio y configuración del reloj.
189
DS1307Inicio();
//Inicio y configuración de la USART a 9600 bps.
UART1_Init(9600);
//Se leen los segundos del reloj.
DATO = DS1307GetSegundos();
while(1) //Bucle infinito.
{
//Se espera el cambio en los segundos.
while( DATO == DS1307GetSegundos() )delay_ms(200);
//Se leen la hora y se transmiten por la USART.
DATO = DS1307GetHoras();
ByteToStr( DATO, Text );
UART1_Write_Text( Text ); UART1_Write_Text(":");
//Se leen los minutos y se transmiten por la USART.
DATO = DS1307GetMinutos();
ByteToStr( DATO, Text );
UART1_Write_Text( Text ); UART1_Write_Text(":");
//Se leen los segundos y se transmiten por la USART.
DATO = DS1307GetSegundos();
ByteToStr( DATO, Text );
UART1_Write_Text( Text ); UART1_Write(13); UART1_Write(10);
}
}
Para realizar la simulación pertinente en ISIS, se implementan los siguientes dispositivos:
16F877A, RES, LED-RED, DS1307, CRYSTAL, CELL, y los instrumentos virtuales, VIRTUAL
TERMINAL, I2C DEBUGGER. El circuito electrónico correspondiente para simular es el
siguiente:


Circuito 13-11
190
14 Tratamiento digital de
señales
El tratamiento digital de señales es una línea de la tecnología demasiado extensa para ser tratada
en un solo capítulo; sin embargo, este capítulo se centra en las nociones de adquisición
tratamiento y reconstrucción de señales. Cabe denotar que el alcance de los microcontroladores
PICMicro de baja gama como los 16F y 18F sólo permiten realizar tratamientos sobre señales de
baja frecuencia. De todos modos, muchas aplicaciones son de utilidad bajo estás características
tales como sistemas de comunicación, generación de tonos DTMF y adquisición de señales
bioeléctricas, entre otras. Si el desarrollador desea trabajar y tratar señales de mayor espectro,
realizando tratamientos de mayor complejidad será necesario trabajar con unidades de
procesamiento más robustas como la familia de microcontroladores PICMicro ds. Estos
microcontroladores no pueden ser programados con el compilador MikroC PRO, para estos se
debe utilizar el compilador MikroC PRO para dsPIC.

Un diagrama en bloques del procesamiento digital de una señal se puede apreciar en la siguiente
figura:


Figura 14-1
14.1 Muestreo
Como primera medida se requiere adquirir muestras periódicamente de la señal análoga, esto se
logra haciendo una conversión análoga digital, con una frecuencia constante denominada
frecuencia de muestreo Fs. El periodo de muestreo T, es el tiempo en segundos que existe entre
muestra y muestra, y a su vez es el inverso de la frecuencia de muestreo T = 1/Fs. El muestreo
permite convertir una señal continua en una señal discreta, es decir que pasa de x(t) a x(Tn),
donde T es el periodo de muestreo, y n es el número de la muestra digitalizada. Para definir el
periodo de muestreo T, o lo que es igual la frecuencia de muestreo Fs, se debe tener la siguiente
consideración: La frecuencia de muestreo debe ser mayor que la máxima componente espectral
contenida en la señal x(t). Por ejemplo, si la máxima frecuencia contenida en la señal x(t) es
500Hz, la frecuencia de muestreo debe ser como mínimo el doble, es decir Fs = 2x500Hz =
1000Hz. Este concepto es conocido como teorema de muestreo de Nyquist. Sin embargo la
frecuencia de muestreo puede ser mayor al doble del máximo en frecuencia de la señal x(t), de
hecho cuanto mayor sea la frecuencia de muestreo mayor será la fidelidad de la señal
digitalizada, pero a su vez mayor serán los recursos requeridos en velocidad, y memoria de
procesamiento.

MAX
F Fs 2 >
Ecuación 14-1

191
14.2 Funciones de transferencia
Una función de transferencia es la relación que existe entre la entrada de una señal y su salida al
pasar por el bloque de proceso. Las funciones de transferencia se estudian y se manipulan en
términos de la frecuencia compleja y no en el dominio del tiempo continuo. Esto se debe a que al
convertir las funciones a la frecuencia compleja las operaciones que implican manipulación de
números complejos y sistemas integro diferenciales que se hacen lineales, y esto simplifica su
tratamiento. Las funciones de transferencia para el caso de los sistemas de tiempo discreto se
hacen en términos de la variable compleja z, para el tratamiento de estos sistemas se recurre a la
transformación z, que representa la frecuencia compleja para el tiempo discreto. Las funciones de
transferencia se denotan por excelencia con la letra H, para las funciones en términos de la
variable z, y con h, para las funciones en términos del tiempo discreto Tn. A continuación se
puede ver un ejemplo:
B
A
z X
z Y
z H = =
) (
) (
) (
Ecuación 14-2

De la ecuación anterior se puede deducir que:

) ( ) ( ) ( z Y z X z H =
Ecuación 14-3

Como se puede ver en la anterior ecuación la salida Y, de un sistema discreto es igual a la entrada
X, multiplicada por la función de transferencia H. Se debe recordar que el tratamiento real y la
programación se realizan en términos del tiempo discreto, por está razón siempre se deberá
realizar la transformada inversa de z. La transformación inversa de la ecuación anterior da como
resultado la siguiente ecuación en función del tiempo discreto Tn:

) ( ) ( * ) ( n y n x n h =
Ecuación 14-4

De la anterior ecuación se puede observar que se omite la escritura del periodo T, dado que este
es un valor constante, y no tiene relevancia escribirlo siempre. Por otro lado se puede observar
que la función h y la señal x, no se multiplican como en la frecuencia compleja, en este caso al
realizar la transformación su equivalencia es la convolución. Para el caso del tratamiento de
señales, la longitud de h(n), es finita, y el número máximo que asume n se denomina M, este a su
vez determina el orden de la función de transferencia. Por otra parte la señal de entrada x(n), es
una serie de muestras de longitud indeterminada, igual que la salida y(n).

De lo anterior se puede concluir que para realizar un tratamiento digital sobre una señal se debe
realizar la operación de convolución continua entre los coeficiente de la función h(n) y la señal
x(n). Sin embargo la operación de convolución puede ser de dos formas, una en la cual la
convolución se hace con las muestras pasadas y actuales de la señal x(n), esto se conoce como
sistema sin memoria o sistemas FIR (Respuestá Finita al Impulso). Por otra parte cuando la
convolución requiere información de las muestras pasadas de x(n), y de y(n), estos sistemas se
denominan sistemas con memoria o IIR (Respuestá Infinita al Impulso). Los sistemas IIR son
sistemáticamente más simples, en su implementación y requieren de campos de memoria
pequeños, por otra parte realizar su diseño y lograr su estábilidad es más complicado y requiere
de tediosos procesos matemáticos. Los sistemas FIR, ofrecen características contrarias a los
sistemas IIR, los sistemas FIR, son de fácil implementación y diseño, pero consumen recursos de
192
memoria y procesamiento mayor, una de las virtudes de estos sistemas es que siempre son
estábles. Los análisis y ejemplos de este capítulo se concentrarán en los sistemas FIR e IIR.
14.3 Convolución
Para iniciar es importante conocer la estructura de una convolución continua en forma
matemática y por medio de un segmento de código en lenguaje C:

| |
¿

÷· =
÷ =
k
k n x k h n y ) ( ) ( ) (
Ecuación 14-5

La ecuación anterior describe la forma general de la convolución, sin embargo se debe recordar
que la longitud de la función h(n), es finita y su máximo es M, por lo tanto la ecuación se puede
reescribir de la siguiente forma:
| |
¿
=
÷ =
M
k
k n x k h n y
0
) ( ) ( ) (
Ecuación 14-6

Cada vez que una muestra de la salida y(n), es calculada por medio de la convolución, se
requieren M, muestras de la señal x(n), incluida la muestra actual, esto quiere decir que para
hacer la convolución se debe tener presente la necesidad de un campo de memoria igual a M para
guardar las últimas muestras durante el proceso.

A continuación se puede observar un ejemplo de cómo se implementar una convolución, en
lenguaje C:

//Orden del sistema FIR de orden 17, M=17.
#define M 17
float x[M];
float h[M];

//Rutina para hacer la convolución.

float yn=0.0;
short k;
for( k=M-1; k>=1; k-- )x[n]=x[n-1];
x[0]=x0;
for( k=0; k<M; k++ )yn += h[k]*x[k];

14.4 Filtros FIR
El diseño de los filtros FIR, es relativamente simple y utiliza estructuras ya definidas para cada
tipo de filtro. Los filtros pueden ser de cinco naturalezas: Pasa bajas, pasa altas, pasa bandas,
rechaza banda, y multi banda. Para el tratamiento de los filtros se debe tener presente las
siguientes consideraciones:

Los cálculos de los filtros se hacen en radianes y no en hercios.
193
La frecuencia de muestreo en hercios Fs, es equivalente a una frecuencia en radianes/segundo de
2π.
La máxima frecuencia tratada por los filtros es Fs/2, o π Radianes.
La relación que existe entre radianes y hercios es: W = 2πF, y F = W/2π.
Las frecuencias de corte de los filtros se denotan como Wc, y Fc.
La frecuencia de corte se calcula como Wc = 2 π Fc / Fs.
La banda de transición del filtro se denota como dW en radianes/segundo y dF en hercios.
El orden de los sistemas FIR se denota como M.
Se recomienda usar un número impar para M.

A continuación se estudiará la forma de diseño para cada uno de estos filtros.
14.4.1 Filtro Pasa bajas
Los filtros pasa bajas se caracterizan por tener una frecuencia de corte a partir de la cual las
frecuencias inferiores son permitidas, y las frecuencias superiores son atenuadas. La función de
transferencia h(n), para este filtro es:


2 2
0 ,
0 ,
) (
) (
M
n
M
n
w
n
n
n w Sen
n h
c
c
< <
÷
¦
¦
¹
¦
¦
´
¦
=
=
=
t
t
Ecuación 14-7

La respuestá espectral de este filtro de orden 7, en escala lineal es la siguiente:




Figura 14-2

194
14.4.2 Filtros Pasa Altas
Los filtros pasa altos permiten el paso de las frecuencias superiores a la frecuencia de corte, y
suprimen las inferiores a la misma. El cálculo de la función de transferencia h(n), para estos
filtros está definida por la siguiente ecuación:

2 2
0 , 1
0 ,
) (
) (
M
n
M
n
w
n
n
n w Sen
n h
c
c
< <
÷
¦
¦
¹
¦
¦
´
¦
= ÷
=
÷
=
t
t
Ecuación 14-8


La respuestá espectral en escala lineal de este filtro en orden 17, es la siguiente:



Figura 14-3
14.4.3 Filtro Pasa Banda
Los filtros pasa banda permiten el paso de una porción de frecuencias entre Wc1 y Wc2, y el
resto de frecuencias son eliminadas. La función de transferencia para este tipo de filtros se
calcula por medio de la siguiente ecuación:

2 2
0 ,
0 ,
) ( ) (
) (
1 2
1 2
M
n
M
n
w w
n
n
n w Sen n w Sen
n h
c c
c c
< <
÷
¦
¦
¹
¦
¦
´
¦
=
÷
=
÷
=
t
t
Ecuación 14-9

195
La respuestá espectral en escala lineal de este filtro en orden 17, es la siguiente:



Figura 14-4
14.4.4 Filtro Rechaza Banda
Los filtros rechaza banda tienen un comportamiento similar a los filtros pasa banda, y su proceder
es inverso a los filtros pasa banda, la función que caracteriza los coeficientes de la función de
transferencia es la siguiente:

2 2
0 , 1
0 ,
) ( ) (
) (
1 2
2 1
M
n
M
n
w w
n
n
n w Sen n w Sen
n h
c c
c c
< <
÷
¦
¦
¹
¦
¦
´
¦
=
÷
÷
=
÷
=
t
t
Ecuación 14-10

La respuestá espectral en escala lineal de este filtro en orden 17, es la siguiente:



Figura 14-5
196
14.4.5 Filtro Multi Band
Los filtros multi banda son arreglos que integran diferentes filtros en uno solo, estos pueden dar
ganancias arbitrarias en un rango de frecuencias. La siguiente ecuación integra las ganancias A
del filtro, y las frecuencias de corte w:

2 2
0 , ) (
0 ,
) (
) (
) (
1
1
1
1
M
n
M
n
w
A A
n
n
n w sen
A A
n h
L
l
l
l l
L
l
l
l l
< <
÷
¦
¦
¹
¦
¦
´
¦
=
(
¸
(

¸

÷
=
(
¸
(

¸

÷
=
¿
¿
=
+
=
+
t
t
Ecuación 14-11

La siguiente gráfica muestra la respuestá, de un filtro multi banda de orden 65, con cuatro bandas
diferentes:


Figura 14-6

Para fines de diseño se debe tener presente que la ultima frecuencia calculada debe ser igual a π.
Este tipo de filtros son ideales para el diseño de sistemas que requieran efectuar ecualización,
sobre una señal.
14.4.6 Ventanas fijas
Las ventanas se aplican a las funciones de transferencia h(n), el objetivo de las ventanas es
mejorar y suavizar la respuestá espectral de los filtros FIR. Las ventanas de mayor uso son las
siguientes:
- Rectangular
- Hamming
- Hanning
- Blackman
197
Los filtros que se demostraron anteriormente por defecto son filtros con ventana rectangular.
Estos filtros tienen la menor transición en la frecuencia de corte, lo que los hace más cercanos a
los filtros ideales, sin embargo está propiedad produce en los filtros sobresaltos y oscilaciones en
el espectro. Este efecto es conocido como: fenómeno Gibbs. Este efecto puede apreciarse en la
siguiente gráfica:



Figura 14-7

La ventana Rectangular ofrece una banda de transición igual a la siguiente relación: dW = 1,8π/M
14.4.7 Ventanas Rectangulares
Una ventana rectangular ofrece un patrón lineal, y constante. Se debe recordar que al realizar el
cálculo de un filtro FIR, por defecto ya se encuentra con una ventana de este tipo.

La aplicación de las ventanas involucra la creación de una nueva función de factores w(n), que
posteriormente debe ser multiplicada término a término con la función de transferencia h(n), para
crear la función h(n) definitiva.

Una ventana Rectangular se representa por medio de la siguiente función w(n):


Figura 14-8

La respuestá espectral de la ventana Rectangular en escala logarítmica es la siguiente:
198


Figura 14-9

La ventana Rectangular ofrece una atenuación del fenómeno Gibbs de -13dB.

14.4.8 Ventanas Hamming
Para el caso de la ventana Hamming, los factores w(n), se calculan de la siguiente forma:

M n
M n Cos
n w s s
÷
= 0 ,
2
) / 2 ( 1
) (
t
Ecuación 14-12

La misma respuestá espectral aplicando una ventana Hamming, en la figura (14.7) del fenómeno
Gibbs se ve de la siguiente forma:



Figura 14-10

La ventana Hamming ofrece una banda de transición igual a la siguiente relación: dW = 6,2π/M

Una ventana Hamming se representa por medio de la siguiente función w(n):

199


Figura 14-11

La respuestá espectral de la ventana Hamming en escala logarítmica es la siguiente:



Figura 14-12

En la ventana Hamming se ofrece una atenuación del fenómeno Gibbs de -31dB.

14.4.9 Ventanas Hanning
Para el cálculo de la ventana Hanning, los factores w(n), se deducen de la siguiente forma:


M n
M n Cos
n w s s
÷
= 0 ,
50
) / 2 ( 23 27
) (
t
Ecuación 14-13


La misma respuestá espectral aplicando una ventana Hanning, en la figura (14.7) del fenómeno
Gibbs se ve de la siguiente forma:
200


Figura 14-13

La ventana Hanning ofrece una banda de transición igual a la siguiente relación: dW = 6,6π/M

Una ventana Hanning se representa por medio de la siguiente función w(n):



Figura 14-14

La respuestá espectral de la ventana Hanning en escala logarítmica es la siguiente:



Figura 14-15

En la ventana Hanning se ofrece una atenuación del fenómeno Gibbs de -41dB.
201
14.4.10 Ventanas Blackman
Para el diseño de una ventana Blackman, los factores w(n), se calculan de la siguiente forma:

M n
M n Cos M n Cos
n w s s
+ ÷
= 0 ,
50
) / 4 ( 4 ) / 2 ( 25 21
) (
t t
Ecuación 14-14

La misma respuestá espectral aplicando una ventana Blackman, en la figura (14.7) del fenómeno
Gibbs se ve de la siguiente forma:



Figura 14-16

La ventana Blackman ofrece una banda de transición igual a la siguiente relación: dW = 11π/M

Una ventana Blackman se representa por medio de la siguiente función w(n):



Figura 14-17

La respuestá espectral de la ventana Blackman en escala logarítmica es la siguiente:



Figura 14-18
202
La ventana Blackman ofrece una atenuación del fenómeno Gibbs de -57dB.
14.5 Ejemplos de diseño para filtros FIR
En la siguiente sección se mostrarán ejemplos de diseño para filtros FIR, implementando un
microcontrolador 18F452, con una fuente de 40MHz como reloj. Para fines prácticos reales la
frecuencia de 40MHz, se logra utilizando un cristal de 10MHz, y activando la fuente de reloj HS-
PLL en el PIC. Está opción implementa internamente en el PIC, un PLL que multiplica la
frecuencia externa por un factor de cuatro, y el resultado es usado como fuente de reloj para el
procesador del microcontrolador. Para todos los ejemplos FIR que se mostrarán se deben simular
con el mismo arreglo en ISIS, para este fin se implementa un circuito con los dispositivos
18F452, RES, OSCILLOSCOPE, Generador virtual Seno.

Para usar los generadores de señal virtual en ISIS, se debe picar en la barra de herramientas de la
izquierda el icono de Generator Mode, este tiene la siguiente apariencia visual: , dentro de este
se escoge la opción SINE, y se pega dentro del área de trabajo. Este generador tiene por defecto
una frecuencia de 1Hz, y una amplitud de 1 voltio. La adquisición de señales se hace en el
microcontrolador por medio del módulo AD, los niveles de tensión que las entradas análogas
admiten, no pueden salirse de los confines de la polarización del microcontrolador. En otras
palabras los niveles de las entradas análogas no pueden ser superiores a 5 voltios, ni voltajes
negativos. Para evitar las circunstancias antes nombradas, se debe manipular la configuración del
generador virtual de señal, para este propósito, se hace doble clic, sobre el generador y se editan
los parámetros: Offset, y Amplitude. El parámetro offset se configura a 2,5 voltios, y el
parámetro Amplitude a 2 voltios. Está acción evita que las señales generadas se salgan de los
parámetros de PIC. Para fines de simulación el parámetro Frequency, puede ser alterado al gusto
del desarrollador.

Una vista de está edición se puede apreciar en la siguiente figura:


Figura 14-19
203

Indiferente al ejemplo que se simule en está sección, se puede implementar el siguiente circuito
electrónico en ISIS:


Circuito 14-1

Para la reconstrucción de la señal procesada, se configuran 10 bits de salida, para hacer un
convertidor DA, por medio de un arreglo R-2R.

Para todos los ejemplos usados en este apartado, se usará la interrupción por TIMER 0, para crear
el periodo de muestreo, y por defecto la frecuencia de muestreo.

El siguiente código fuente muestra un ejemplo del muestreo de la señal por medio del TIMER 0:

//Declaración de varíales.
float x0, y0;
unsigned int YY;
//Declaración de la función de interrupciones.
void interrupt ( void )
{
if( INTCON.F2 )
{
TMR0L=135;
//Timer0 con periodo de 774,4u segundo.
// Fs = 1291,32 Hz.
//Adquisición de una muestra de 10 bits en, x[0].
x0 = (float)(ADC_Read(0)-512.0);
//
//Espacio para procesar la señal.
//
//Reconstrucción de la señal: y en 10 bits.
YY = (unsigned int)(x0+512);
PORTC = (YY>>8)&3;
PORTB = YY&255;
204
INTCON.F2=0;
}
}

void main( void )
{
//Inicio del puerto B como salida.
TRISB = 0;
PORTB = 0;
TRISC = 0;
PORTC = 0;
//Se configura el TIMER 0, su interrupción.
INTCON = 0b10100000;
T0CON = 0b11000101;
while(1)//Bucle infinito.
{
}
}
14.5.1 Ejemplo de diseño para filtro pasa bajas
Fs = 1291,32KHz.
Fc = 150Hz.
Ventana Rectangular.

Se determina la frecuencia de corte digital:
Wc = 2 π Fc / Fs = 2 π 150 / 1291,32 = 0,72985.

Usando la formula (14.7), se designan los coeficientes de la función h(n):

h(-8)=-0.0171035387965417
h(-7)=-0.0419431579233366
h(-6)=-0.0501329294124475
h(-5)=-0.0309497847516785
h(-4)=0.0175345019583181
h(-3)=0.0864308262744764
h(-2)=0.158173992108178
h(-1)=0.212237065988464
h(0)=0.232320416318186
h(1)=0.212237065988464
h(2)=0.158173992108178
h(3)=0.0864308262744764
h(4)=0.0175345019583181
h(5)=-0.0309497847516785
h(6)=-0.0501329294124475
h(7)=-0.0419431579233366
h(8)=-0.0171035387965417


205
Se debe tener presente que para fines de programación los valores de n no pueden ser negativos,
por está razón se debe iniciar el vector en 0, y para usarlo en el código en lenguaje C, se
implementa de la siguiente manera:

const float h[]=
{
-0.0171035387965417, //h(0)
-0.0419431579233366, //h(1)
-0.0501329294124475, //h(2)
-0.0309497847516785, //h(3)
0.0175345019583181, //h(4)
0.0864308262744764, //h(5)
0.158173992108178, //h(6)
0.212237065988464, //h(7)
0.232320416318186, //h(8)
0.212237065988464, //h(9)
0.158173992108178, //h(10)
0.0864308262744764, //h(11)
0.0175345019583181, //h(12)
-0.0309497847516785, //h(13)
-0.0501329294124475, //h(14)
-0.0419431579233366, //h(15)
-0.0171035387965417 //h(16)
};

El programa definitivo con la función de transferencia h(n), será de la siguiente forma:

#define M 17
//Función de trasferencia h[n]
const float h[]=
{
-0.0171035387965417, //h(0)
-0.0419431579233366, //h(1)
-0.0501329294124475, //h(2)
-0.0309497847516785, //h(3)
0.0175345019583181, //h(4)
0.0864308262744764, //h(5)
0.158173992108178, //h(6)
0.212237065988464, //h(7)
0.232320416318186, //h(8)
0.212237065988464, //h(9)
0.158173992108178, //h(10)
0.0864308262744764, //h(11)
0.0175345019583181, //h(12)
-0.0309497847516785, //h(13)
-0.0501329294124475, //h(14)
-0.0419431579233366, //h(15)
-0.0171035387965417 //h(16)
};
206
//Declaración de varíales.
float x0, y0;
float x[M];
unsigned int YY;
unsigned short i;
//Declaración de la función de interrupciones.
void interrupt ( void )
{
if( INTCON.F2 )
{
TMR0L=135;
PORTC.F7=1;
//Timer0 con periodo de 774,4u segundo.
// Fs = 1291,32 Hz.
//Corrimiento continuo de la señal x[n]
for( i=M-1; i!=0; i-- )x[i]=x[i-1];
//Adquisición de una muestra de 10 bits en, x[0].
x[0] = (float)(ADC_Read(0)-512.0);
//Convolución continúa.
y0 = 0.0; for( i=0; i<M; i++ ) y0 += h[i]*x[i];
//Reconstrucción de la señal: y en 10 bits.
YY = (unsigned int)(y0+512.0);
PORTC = (YY>>8)&3;
PORTB = YY&255;
PORTC.F7=0;
INTCON.F2=0;
}
}

void main( void )
{
//Inicio del puerto B como salida.
TRISB = 0;
PORTB = 0;
TRISC = 0;
PORTC = 0;
//Se configura el TIMER 0, su interrupción.
INTCON = 0b10100000;
T0CON = 0b11000101;
while(1)//Bucle infinito.
{
}
}


Por limitaciones de velocidad en este microcontrolador, y teniendo presente la frecuencia de
muestreo de 1291,32Hz no es posible usar un orden del filtro superior a 17. Sin embargo es
posible usar órdenes menores como; 15, 13, 11, 9, 7, 5, o 3. Para fines prácticos y didácticos en
los próximos ejemplos se implementarán filtros de orden 17 igual a este.
207
14.5.2 Ejemplo de diseño para filtro pasa altas
Fs = 1291,32KHz.
Fc = 200Hz.
Ventana Hamming.

Se determina la frecuencia de corte digital:
Wc = 2 π Fc / Fs = 2 π 200 / 1291,32 = 0,97314.

Usando la formula (14.8), se definen los coeficientes de la función h(n) multiplicados por la
ventana Hamming w(n) de la ecuación (14.12), dando como resultado la siguiente función h(n):

h(-8)=0
h(-7)=-0.000774581987451374
h(-6)=0.00297591407324525
h(-5)=0.0174357425540551
h(-4)=0.0246447931670207
h(-3)=-0.0148886974624375
h(-2)=-0.118648252092459
h(-1)=-0.243426834227261
h(0)=0.684363125797732
h(1)=-0.260893089082499
h(2)=-0.136977589378571
h(3)=-0.0187342667800403
h(4)=0.0345797805794857
h(5)=0.028555061237806
h(6)=0.00631989035694063
h(7)=-0.00299371636029323
h(8)=-0.00134023946307802

La implementación del código fuente es igual al filtro pasa bajas, sustituyendo el arreglo h[], por
el siguiente:

const float h[]=
{
0, //h(0)
-0.000774581987451374, //h(1)
0.00297591407324525, //h(2)
0.0174357425540551, //h(3)
0.0246447931670207, //h(4)
-0.0148886974624375, //h(5)
-0.118648252092459, //h(6)
-0.243426834227261, //h(7)
0.684363125797732, //h(8)
-0.260893089082499, //h(9)
-0.136977589378571, //h(10)
-0.0187342667800403, //h(11)
0.0345797805794857, //h(12)
0.028555061237806, //h(13)
208
0.00631989035694063, //h(14)
-0.00299371636029323, //h(15)
-0.00134023946307802 //h(16)
};
14.5.3 Ejemplo de diseño para filtro pasa banda
Fs = 1291,32KHz.
Fc1 = 150Hz.
Fc2 = 400Hz.
Ventana Hanning.

Se determinan las frecuencias de corte digital:
Wc1 = 2 π Fc / Fs = 2 π 150 / 1291,32 = 0,72985.
Wc2 = 2 π Fc / Fs = 2 π 400 / 1291,32 = 1,94628.

Usando la formula (14.9), se definen los coeficientes de la función h(n) multiplicados por la
ventana Hanning w(n) de la ecuación (14.13), dando como resultado la siguiente función h(n):

h(-8)=0.0018052104538582
h(-7)=0.00905810215732946
h(-6)=0.00179096961917994
h(-5)=0.00393014193876244
h(-4)=0.0307760790492056
h(-3)=-0.0879236273273945
h(-2)=-0.218010454414228
h(-1)=0.078115497545518
h(0)=0.384167993159943
h(1)=0.0832388331308223
h(2)=-0.248392736747743
h(3)=-0.107904878578323
h(4)=0.0411879382357919
h(5)=0.00583790841302624
h(6)=0.00299868100666665
h(7)=0.0163162389068139
h(8)=0.00250614601893693

La función h[], para implementar en el código fuente en lenguaje C es:

const float h[]=
{
0.0018052104538582, //h(0)
0.00905810215732946, //h(1)
0.00179096961917994, //h(2)
0.00393014193876244, //h(3)
0.0307760790492056, //h(4)
-0.0879236273273945, //h(5)
-0.218010454414228, //h(6)
0.078115497545518, //h(7)
209
0.384167993159943, //h(8)
0.0832388331308223, //h(9)
-0.248392736747743, //h(10)
-0.107904878578323, //h(11)
0.0411879382357919, //h(12)
0.00583790841302624, //h(13)
0.00299868100666665, //h(14)
0.0163162389068139, //h(15)
0.00250614601893693 //h(16)
};
14.5.4 Ejemplo de diseño para filtro rechaza banda
Fs = 1291,32KHz.
Fc1 = 150Hz.
Fc2 = 400Hz.
Ventana Blackman.

Se determinan las frecuencias de corte digital:
Wc1 = 2 π Fc / Fs = 2 π 150 / 1291,32 = 0,72985.
Wc2 = 2 π Fc / Fs = 2 π 400 / 1291,32 = 1,94628.

Usando la formula (14.10), se definen los coeficientes de la función h(n) multiplicados por la
ventana Blackman w(n) de la ecuación (14.14), dando como resultado la siguiente función h(n):

h(-8)=3.13154095338657E-19
h(-7)=-0.00105084724932717
h(-6)=-0.000518135020348656
h(-5)=-0.00174730211401576
h(-4)=-0.0182611591144528
h(-3)=0.0645431455464317
h(-2)=0.186587834540544
h(-1)=-0.0738928265716166
h(0)=0.604271792109402
h(1)=-0.0827284691705043
h(2)=0.234965429330525
h(3)=0.0923521657912548
h(4)=-0.0302353209611255
h(5)=-0.00346395569934133
h(6)=-0.00133318382487159
h(7)=-0.00472035632958784
h(8)=-0.000290742652784183

La función h[], para implementar en el código fuente en lenguaje C es:

const float h[]=
{
3.13154095338657E-19, //h(0)
-0.00105084724932717, //h(1)
210
-0.000518135020348656, //h(2)
-0.00174730211401576, //h(3)
-0.0182611591144528, //h(4)
0.0645431455464317, //h(5)
0.186587834540544, //h(6)
-0.0738928265716166, //h(7)
0.604271792109402, //h(8)
-0.0827284691705043, //h(9)
0.234965429330525, //h(10)
0.0923521657912548, //h(11)
-0.0302353209611255, //h(12)
-0.00346395569934133, //h(13)
-0.00133318382487159, //h(14)
-0.00472035632958784, //h(15)
-0.000290742652784183 //h(16)
};
14.5.5 Ejemplo de diseño para filtro multi banda
Fs = 1291,32KHz.
Fc1 = 161Hz.
Fc2 = 322Hz.
Fc3 = 484Hz.
Fc4 = 645.66Hz.
A1 = 1.
A2 = 0,5.
A3 = 0,2.
A4 = 1.
Ventana Rectangular.

Se determinan las frecuencias de corte digital:
Wc1 = 2 π Fc1 / Fs = 2 π 161 / 1291,32 = 0,78337.
Wc2 = 2 π Fc2 / Fs = 2 π 322 / 1291,32 = 1,56675.
Wc3 = 2 π Fc3 / Fs = 2 π 484 / 1291,32 = 2,355.
Wc4 = 2 π Fc4 / Fs = 2 π 645,66 / 1291,32 = π.

Usando la formula (14.11), se definen los coeficientes de la función h(n):

h(-8)=-0.000403386658426763
h(-7)=-0.00443133542115946
h(-6)=-0.0685784954060509
h(-5)=0.0330418473673128
h(-4)=-0.000367826230314909
h(-3)=-0.0538949660023408
h(-2)=0.207286062892996
h(-1)=0.0275264614524777
h(0)=0.674596536876994
h(1)=0.0275264614524777
h(2)=0.207286062892996
211
h(3)=-0.0538949660023408
h(4)=-0.000367826230314909
h(5)=0.0330418473673128
h(6)=-0.0685784954060509
h(7)=-0.00443133542115946
h(8)=-0.000403386658426763

La función h[], para implementar en el código fuente en lenguaje C es la siguiente:

const float h[]=
{
-0.000403386658426763, //h(0)
-0.00443133542115946, //h(1)
-0.0685784954060509, //h(2)
0.0330418473673128, //h(3)
-0.000367826230314909, //h(4)
-0.0538949660023408, //h(5)
0.207286062892996, //h(6)
0.0275264614524777, //h(7)
0.674596536876994, //h(8)
0.0275264614524777, //h(9)
0.207286062892996, //h(10)
-0.0538949660023408, //h(11)
-0.000367826230314909, //h(12)
0.0330418473673128, //h(13)
-0.0685784954060509, //h(14)
-0.00443133542115946, //h(15)
-0.000403386658426763, //h(16)
};

212
14.6 Filtros IIR
Los filtros IIR, son de implementación computacional más simple, pero su diseño y sus cálculos
son de mayor complejidad. Para diseñar un filtro IIR, existen diferentes técnicas, sin embargo
este capítulo se centrará en el diseño por medio de la transformación bilineal. La transformación
bilineal es una relación que existe entre la variable compleja s, y z. Esto quiere decir que para
realizar un filtro IIR, se requiere una función de transferencia en términos de la variable s. En
síntesis para iniciar el diseño de un filtro IIR, se requiere como punto de partida, la función de
transferencia de un filtro análogo. Para fines de estudio en este capítulo se mostrarán a
continuación las funciones de transferencia de los filtros básicos de segundo orden:

Filtro Pasa Bajas:
2 2
2
2
) (
c c
c
s s
s H
O + O +
O
=
,
Ecuación 14-15
Filtro Pasa Altas:
2 2
2
2
) (
c c
s s
s
s H
O + O +
=
,
Ecuación 14-16

Filtro Pasa Banda:
2
0
2
) (
O + O +
O
=
s s
s
s H
B
B
Ecuación 14-17


Filtro Rechaza Banda:
2
0
2
2
0
2
) (
O + O +
O +
=
s s
s
s H
B
Ecuación 14-18

Las frecuencias Ω están definidas en radianes/segundo. Las frecuencias Ωc representan la
frecuencia de corte en los filtros pasa bajas, y altas. Las frecuencias Ωo representan la frecuencia
de resonancia en los filtros pasa banda y rechaza banda. Las frecuencias ΩB representan el ancho
de banda de los filtros pasa banda y rechaza banda. El coeficiente , , representa el
amortiguamiento del filtro, el valor que este coeficiente debe tomar es mayor que 0, cuando el
coeficiente de amortiguamiento vale 0, el filtro se convierte en resonante, y tendría un
comportamiento similar a los filtros pasa banda o rechaza banda. Por otra parte el coeficiente de
amortiguamiento no podrá ser negativo, esto generaría una función de transferencia inestable.
Cabe denotar que la implementación de filtros IIR, produce en la respuesta de la señal
distorsiones mayores a las que se generan con los filtros FIR.
213
14.6.1 Transformación bilineal
La transformación bilineal es una relación matemática entre las variables complejas s, y z. Está
relación se rige por las siguientes ecuaciones:
) 1 (
) 1 ( 2
) 1 (
) 1 ( 2
1
1
1
1
÷
÷
÷
÷
+
÷
=
+
÷
=
z
z F
z T
z
s
s
s
Ecuación 14-19
En la ecuación (14.19), se puede observar la relación entre las dos variables, donde Ts, y Fs, son
respectivamente el periodo de muestreo y frecuencia de muestreo implementado. De la misma
forma existe una relación entre las frecuencias del filtro análogo y el filtro digital, está relación se
puede apreciar en la siguiente ecuación:
)
2
tan( 2
)
2
tan( 2
d
s
s
d
w
F
T
w
= = O
Ecuación 14-20
Donde la frecuencia digital es: Wd, la frecuencia análoga es: Ω, y Ts, Fs, son respectivamente el
periodo de muestreo y la frecuencia de muestreo.

De la misma forma que en los filtros FIR, la relación entre la frecuencia digital y la frecuencia en
hercios es la siguiente:
) tan( 2
) tan( 2
2
s
x
s
s
s
x
x
s
x
d
F
F
F
T
F
F
F
F
w
t
t
t
= = O ¬ =
Ecuación 14-21
Para contextualizar este proceso, se mostrará a continuación la transformación bilineal de los
cuatro filtros básicos en segundo orden.
14.6.2 Filtro Pasa Bajas IIR
Como primera medida, se realiza el reemplazo de variables en la ecuación (14.15), para obtener
la función h(z):
2
1
1
2
1
1
2
) 1 (
) 1 ( 2
2
) 1 (
) 1 ( 2
) (
c
s
c
s
c
z
z F
z
z F
z H
O +
(
¸
(

¸

+
÷
O +
(
¸
(

¸

+
÷
O
=
÷
÷
÷
÷
,


2 1 2 1 1 2 1 2
2 1 2
) 1 ( ) 1 )( 1 ( 4 ) 1 ( 4
) 1 (
) (
÷ ÷ ÷ ÷
÷
+ O + + ÷ O + ÷
+ O
=
z z z F z F
z
z H
c s c s
c
,


) 2 1 ( ) 1 ( 4 ) 2 1 ( 4
) 2 1 (
) (
2 1 2 2 2 1 2
2 1 2
÷ ÷ ÷ ÷ ÷
÷ ÷
+ + O + ÷ O + + ÷
+ + O
=
z z z F z z F
z z
z H
c s c s
c
,


2 2 2 1 2 2 2 2
2 2 1 2 2
] 4 4 [ ] 8 2 [ ] 4 4 [
2
) (
÷ ÷
÷ ÷
O + O ÷ + ÷ O + O + O +
O + O + O
=
z F F z F F F
z z
z H
c s c s s c c s c s
c c c
, ,

214
En este punto se tiene una función de transferencia con polinomios ordenados en los polos y los
ceros, de la forma H(z) = A(z)/B(z). Para los polinomios A, B, se tiene en este caso expresiones
de segundo orden con la forma:
2
3
1
2
0
1
) (
÷ ÷
+ + = z a z a z a z A ,
2
3
1
2
0
1
) (
÷ ÷
+ + = z b z b z b z B .
Después de tener la función de transferencia de esta forma se debe implementar una ecuación en
diferencias para la ejecución en la programación, para deducir la ecuación en diferencias es
necesario forzar el coeficiente del denominador b1 a valer 1. Para lograr este objetivo todos los
coeficientes a y b se dividen en b1. Está condición aplicada en la última ecuación del filtro pasa
bajas, da como resultado lo siguiente:

2
2 2
2 2
1
2 2
2 2
2
2 2
2
1
2 2
2
2 2
2
] 4 4 [
] 4 4 [
] 4 4 [
] 8 2 [
1
] 4 4 [ ] 4 4 [
2
] 4 4 [
) (
÷ ÷
÷ ÷
O + O +
O + O ÷
+
O + O +
÷ O
+
O + O +
O
+
O + O +
O
+
O + O +
O
=
z
F F
F F
z
F F
F
z
F F
z
F F F F
z H
c s c s
c s c s
c s c s
s c
c s c s
c
c s c s
c
c s c s
c
,
,
,
, , ,


La misma ecuación en términos de los coeficientes, es de la siguiente forma:

2 2
2 2
3
2 2
2 2
2
1
1 3
1 2
2 2
2
1
2
3
1
2
0
1
2
3
1
2
0
1
4 4
4 4
4 4
8 2
1
2
4 4
, ) (
c s c s
c s c s
c s c s
s c
c s c s
c
F F
F F
b
F F
F
b
b
a a
a a
F F
a
z b z b z b
z a z a z a
z H
O + O +
O + O ÷
=
O + O +
÷ O
=
=
=
=
O + O +
O
=
+ +
+ +
=
÷ ÷
÷ ÷
,
,
,
,


Para realizar la ecuación en diferencias se hace la siguiente transformación de z a n.

) ( * ) ( ) ( * ) ( ) ( ) ( ) ( ) (
) (
) (
) (
) (
) ( n a n x n b n y z A z X z B z Y
z B
z A
z X
z Y
z H = ÷ = ¬ = =

Realizando la transformación inversa de z, se tiene una doble convolución, que genera una
ecuación de la siguiente forma:
) 2 ( ) 1 ( ) ( ) 2 ( ) 1 ( ) (
2 2 1 3 2 1
÷ + ÷ + = ÷ + ÷ + n x a n x a n x a n y b n y b n y b


En conclusión la salida y(n) queda de la siguiente manera:
) 2 ( ) 1 ( ) 2 ( ) 1 ( ) ( ) (
3 2 2 2 1
÷ ÷ ÷ ÷ ÷ + ÷ + = n y b n y b n x a n x a n x a n y
215
14.6.3 Ejemplo de diseño para filtro pasa bajas
Fs = 1291.32Hz.
Fc = 100Hz.
2
2
= , .
Para el caso particular de los filtros pasa bajas, y pasa altas de segundo orden el valor de , debe
ser
2
2 para que la frecuencia de corte no sufra desplazamientos con respecto a las condiciones
establecidas para los filtros en general.

Para deducir la frecuencia de corte análoga en función de las frecuencias reales se usa la ecuación
(14.21), dando como resultado la siguiente frecuencia de corte:

0154 . 641 )
32 , 1291
100
tan( ) 32 , 1291 ( 2 ) tan( 2 = = = O
t t
s
c
s c
F
F
F

El paso siguiente es remplazar las constantes en las relaciones que determinan los coeficientes a,
y b. Este paso se puede apreciar a continuación:

6363549 0,50294954
) 0154 , 641 ( ) 32 , 1291 )( 0154 , 641 )(
2
2
( 4 ) 32 , 1291 ( 4
) 0154 , 641 ( ) 32 , 1291 )( 0154 , 641 )(
2
2
( 4 ) 32 , 1291 ( 4
4 4
4 4
0532316 -1,3284348
) 0154 , 641 ( ) 32 , 1291 )( 0154 , 641 )(
2
2
( 4 ) 32 , 1291 ( 4
) 32 , 1291 ( 8 ) 0154 , 641 ( 2
4 4
8 2
1
52600961 0,04362868
05201923 0,08725737 2
52600961 0,04362868
) 0154 , 641 ( ) 32 , 1291 )( 0154 , 641 )(
2
2
( 4 ) 32 , 1291 ( 4
) 154 , 641 (
4 4
, ) (
2 2
2 2
2 2
2 2
3
2 2
2 2
2 2
2 2
2
1
1 3
1 2
2 2
2
2 2
2
1
2
3
1
2
0
1
2
3
1
2
0
1
=
+ +
+ ÷
=
O + O +
O + O ÷
=
=
+ +
÷
=
O + O +
÷ O
=
=
= =
= =
=
+ +
=
O + O +
O
=
+ +
+ +
=
÷ ÷
÷ ÷
c s c s
c s c s
c s c s
s c
c s c s
c
F F
F F
b
F F
F
b
b
a a
a a
F F
a
z b z b z b
z a z a z a
z H
,
,
,
,


La implementación de un filtro IIR, de orden N, requiere un búfer de datos igual a: 2(N+1). En el
caso de un filtro de segundo orden se requieren 3 campos para guardar los últimos valores de la
entrada x, y 3 campos más para los 3 últimos valores de la salida y. Para ilustrar la
implementación de este proceso se usará la misma arquitectura de software y hardware
implementada en los filtros FIR. A continuación se puede observar el siguiente ejemplo en
lenguaje C:

//Declaración de varíales.
float x0=0, x1=0, x2=0, y0=0, y1=0, y2=0;
216
unsigned int YY;
//Declaración de la función de interrupciones.
void interrupt ( void )
{
if( INTCON.F2 )
{
TMR0L=135;
//Timer0 con periodo de 774,4u segundo.
// Fs = 1291,32 Hz.
//Adquisición de una muestra de 10 bits en, x[0].
x0 = (float)(ADC_Read(0)-512.0);
//Implementación de la ecuación en diferencias.
y0 = (x0+x2)*0.0436286852600961 + x1*0.0872573705201923 + y1*1.32843480532316
-y2*0.502949546363549;
//Corrimiento de los valores x(n), y y(n).
y2 = y1;
y1 = y0;
x2 = x1;
x1 = x0;
//Reconstrucción de la señal: y en 10 bits.
YY = (unsigned int)(y0+512.0);
PORTC = (YY>>8)&3;
PORTB = YY&255;
INTCON.F2=0;
}
}

void main( void )
{
//Inicio del puerto B como salida.
TRISB = 0;
PORTB = 0;
TRISC = 0;
PORTC = 0;
//Se configura el TIMER 0, su interrupción.
INTCON = 0b10100000;
T0CON = 0b11000101;
while(1)//Bucle infinito.
{
}
}

14.6.4 Filtro Pasa Altas IIR
De la misma forma que se hizo en el filtro pasa bajas, se realiza el reemplazo de variable en la
ecuación (14.16), para obtener la función h(z):

217
2
1
1
2
1
1
2
1
1
) 1 (
) 1 ( 2
2
) 1 (
) 1 ( 2
) 1 (
) 1 ( 2
) (
c
s
c
s
s
z
z F
z
z F
z
z F
z H
O +
(
¸
(

¸

+
÷
O +
(
¸
(

¸

+
÷
(
¸
(

¸

+
÷
=
÷
÷
÷
÷
÷
÷
,


2 1 2 1 1 2 1 2
2 1 2
) 1 ( ) 1 )( 1 ( 4 ) 1 ( 4
) 1 ( 4
) (
÷ ÷ ÷ ÷
÷
+ O + ÷ + O + ÷
÷
=
z z z F z F
z F
z H
c s c s
s
,


) 2 1 ( ) 1 ( 4 ) 2 1 ( 4
) 2 1 ( 4
) (
2 1 2 2 2 1 2
2 1 2
÷ ÷ ÷ ÷ ÷
÷ ÷
+ + O + ÷ O + + ÷
+ ÷
=
z z z F z z F
z z F
z H
c s c s
s
,


2 2 2 1 2 2 2 2
2 2 1 2 2
] 4 4 [ ] 8 2 [ ] 4 4 [
4 8 4
) (
÷ ÷
÷ ÷
O + O ÷ + ÷ O + O + O +
+ ÷
=
z F F z F F F
z F z F F
z H
c s c s s c c c s
s s s
, ,



Realizando la normalización de los coeficientes se obtiene lo siguiente:

2
2 2
2 2
1
2 2
2 2
2
2 2
2
1
2 2
2
2 2
2
4 4
4 4
4 4
8 2
1
4 4
4
4 4
8
4 4
4
) (
÷ ÷
÷ ÷
O + O +
O + O ÷
+
O + O +
÷ O
+
O + O +
+
O + O +
÷
O + O +
=
z
F F
F F
z
F F
F
z
F F
F
z
F F
F
F F
F
z H
c s c s
c s c s
c s c s
s c
c s c s
s
c s c s
s
c s c s
s
,
,
,
, , ,


De está forma los coeficientes a, y b quedan definidos así:

2 2
2 2
3
2 2
2 2
2
1
1 3
1 2
2 2
2
1
2
3
1
2
0
1
2
3
1
2
0
1
4 4
4 4
4 4
8 2
1
2
4 4
4
, ) (
c s c s
c s c s
c s c s
s c
c s c s
s
F F
F F
b
F F
F
b
b
a a
a a
F F
F
a
z b z b z b
z a z a z a
z H
O + O +
O + O ÷
=
O + O +
÷ O
=
=
=
÷ =
O + O +
=
+ +
+ +
=
÷ ÷
÷ ÷
,
,
,
,


14.6.5 Ejemplo de diseño para filtro pasa altas
Fs = 1291.32Hz.
Fc = 200Hz.
218
2
2
= , .

Se determina la frecuencia de corte análoga en términos de la frecuencia digital:

19404 , 1366 )
32 , 1291
200
tan( ) 32 , 1291 ( 2 ) tan( 2 = = = O
t t
s
c
s c
F
F
F

Seguidamente se determinan los coeficientes del filtro:

7215126 0,26212340
) 19404 , 1366 ( ) 32 , 1291 )( 19404 , 1366 )(
2
2
( 4 ) 32 , 1291 ( 4
) 19404 , 1366 ( ) 32 , 1291 )( 19404 , 1366 )(
2
2
( 4 ) 32 , 1291 ( 4
4 4
4 4
43923452 -0,7099519
) 19404 , 1366 ( ) 32 , 1291 )( 19404 , 1366 )(
2
2
( 4 ) 32 , 1291 ( 4
) 32 , 1291 ( 8 ) 19404 , 1366 ( 2
4 4
8 2
1
7784645 0,49301883
75569289 -0,9860376 2
7784645 0,49301883
) 19404 , 1366 ( ) 32 , 1291 )( 19404 , 1366 )(
2
2
( 4 ) 32 , 1291 ( 4
) 32 , 1291 ( 4
4 4
4
, ) (
2 2
2 2
2 2
2 2
3
2 2
2 2
2 2
2 2
2
1
1 3
1 2
2 2
2
2 2
2
1
2
3
1
2
0
1
2
3
1
2
0
1
=
+ +
+ ÷
=
O + O +
O + O ÷
=
=
+ +
÷
=
O + O +
÷ O
=
=
= =
= ÷ =
=
+ +
=
O + O +
=
+ +
+ +
=
÷ ÷
÷ ÷
c s c s
c s c s
c s c s
s c
c s c s
s
F F
F F
b
F F
F
b
b
a a
a a
F F
F
a
z b z b z b
z a z a z a
z H
,
,
,
,


La programación de este filtro pasa altas, bajo las mismas condiciones del filtro pasa bajas es el
siguiente:

//Declaración de varíales.
float x0=0, x1=0, x2=0, y0=0, y1=0, y2=0;
unsigned int YY;
//Declaración de la función de interrupciones.
void interrupt ( void )
{
if( INTCON.F2 )
{
TMR0L=135;
//Timer0 con periodo de 774,4u segundo.
// Fs = 1291,32 Hz.
//Adquisición de una muestra de 10 bits en, x[0].
x0 = (float)(ADC_Read(0)-512.0);
//Implementación de la ecuación en diferencias.
y0 = (x0+x2)*0.493018837784645 - x1*0.986037675569289 +
y1*0.709951943923452 - y2*0.262123407215126;
//Corrimiento de los valores x(n), y y(n).
219
y2 = y1;
y1 = y0;
x2 = x1;
x1 = x0;
//Reconstrucción de la señal: y en 10 bits.
YY = (unsigned int)(y0+512.0);
PORTC = (YY>>8)&3;
PORTB = YY&255;
INTCON.F2=0;
}
}

void main( void )
{
//Inicio del puerto B como salida.
TRISB = 0;
PORTB = 0;
TRISC = 0;
PORTC = 0;
//Se configura el TIMER 0, su interrupción.
INTCON = 0b10100000;
T0CON = 0b11000101;
while(1)//Bucle infinito.
{
}
}

14.6.6 Filtro Pasa Banda IIR
Para la función de transferencia h(z) en el filtro pasa banda se realiza el cambio de variable
bilineal, tomando como base la ecuación (14.17):

2
0 1
1
2
1
1
1
1
) 1 (
) 1 (
2
) 1 (
) 1 (
2
) 1 (
) 1 (
2
) (
O +
(
¸
(

¸

+
÷
O +
(
¸
(

¸

+
÷
O
(
¸
(

¸

+
÷
=
÷
÷
÷
÷
÷
÷
z
z
F
z
z
F
z
z
F
z H
s B s
B s


2 1 2
0
1 1 2 1 2
1 1
) 1 ( ) 1 )( 1 ( 2 ) 1 ( 4
) 1 )( 1 ( 2
) (
÷ ÷ ÷ ÷
÷ ÷
+ O + + ÷ O + ÷
+ ÷ O
=
z z z F z F
z z F
z H
B s s
B s


) 2 1 ( ) 1 ( 2 ) 2 1 ( 4
) 1 ( 2
) (
2 1 2
0
2 2 1 2
2
÷ ÷ ÷ ÷ ÷
÷
+ + O + ÷ O + + ÷
÷ O
=
z z z F z z F
z F
z H
B s s
B s


2 2
0
2 1 2
0
2
0
2
2
] 2 4 [ ] 2 2 [ ] 2 4 [
2 2
) (
÷ ÷
÷
O + O ÷ + O ÷ O + O + O +
O ÷ O
=
z F F z F F F
z F F
z H
B s s B s B s s
B s B s

220
Normalizando la ecuación se obtiene la siguiente relación:

2
2
0
2
2
0
2
1
2
0
2
2
0
2
2
0
2 2
0
2
2 4
2 4
2 4
2 2
1
2 4
2
2 4
2
) (
÷ ÷
÷
O + O +
O + O ÷
+
O + O +
O ÷ O
+
O + O +
O
÷
O + O +
O
=
z
F F
F F
z
F F
F
z
F F
F
F F
F
z H
B s s
B s s
B s s
B s
B s s
B s
B s s
B s


De la anterior ecuación, se pueden estáblecer las siguientes constantes:

2
0
2
2
0
2
3
2
0
2
2
0
2
1
1 3
2
2
0
2 1
2
3
1
2
0
1
2
3
1
2
0
1
2 4
2 4
2 4
2 2
1
0
2 4
2
, ) (
O + O +
O + O ÷
=
O + O +
O ÷ O
=
=
÷ =
=
O + O +
O
=
+ +
+ +
=
÷ ÷
÷ ÷
B s s
B s s
B s s
B s
B s s
B s
F F
F F
b
F F
F
b
b
a a
a
F F
F
a
z b z b z b
z a z a z a
z H


14.6.7 Ejemplo de diseño para filtro pasa banda
Fs = 1291.32Hz.
Fo = 300Hz.
Fb = 10Hz.

A continuación se determinan las frecuencias análogas para el diseño del filtro pasa banda:

58 , 2310 )
32 , 1291
300
tan( ) 32 , 1291 ( 2 ) tan( 2
0
0
= = = O
t t
s
s
F
F
F
8442 , 62 )
32 , 1291
10
tan( ) 32 , 1291 ( 2 ) tan( 2 = = = O
t t
s
b
s B
F
F
F

Posteriormente se determinan los coeficientes del filtro:

221
1725574 0.97332963
) 58 , 2310 ( ) 8442 , 62 )( 32 , 1291 ( 2 ) 32 , 1291 ( 4
) 58 , 2310 ( ) 8442 , 62 )( 32 , 1291 ( 2 ) 32 , 1291 ( 4
2 4
2 4
04098491 -0.2187550
) 58 , 2310 ( ) 8442 , 62 )( 32 , 1291 ( 2 ) 32 , 1291 ( 4
) 8442 , 62 )( 32 , 1291 ( 2 ) 58 , 2310 ( 2
2 4
2 2
1
841372129 -0.0133351
0
41372129 0.01333518
) 58 , 2310 ( ) 8442 , 62 )( 32 , 1291 ( 2 ) 32 , 1291 ( 4
) 8442 , 62 )( 32 , 1291 ( 2
2 4
2
, ) (
2 2
2 2
2
0
2
2
0
2
3
2 2
2
2
0
2
2
0
2
1
1 3
2
2 2 2
0
2 1
2
3
1
2
0
1
2
3
1
2
0
1
=
+ +
+ ÷
=
O + O +
O + O ÷
=
=
+ +
÷
=
O + O +
O ÷ O
=
=
= ÷ =
=
=
+ +
=
O + O +
O
=
+ +
+ +
=
÷ ÷
÷ ÷
B s s
B s s
B s s
B s
B s s
B s
F F
F F
b
F F
F
b
b
a a
a
F F
F
a
z b z b z b
z a z a z a
z H



14.6.8 Filtro Rechaza Banda IIR
Para la función de transferencia h(z) en el filtro pasa banda se realiza el cambio de variable
bilineal, tomando como base la ecuación (14.17):


2
0 1
1
2
1
1
2
0
2
1
1
) 1 (
) 1 (
2
) 1 (
) 1 (
2
) 1 (
) 1 (
2
) (
O +
(
¸
(

¸

+
÷
O +
(
¸
(

¸

+
÷
O +
(
¸
(

¸

+
÷
=
÷
÷
÷
÷
÷
÷
z
z
F
z
z
F
z
z
F
z H
s B s
s


2 1 2
0
1 1 2 1 2
2 1 2
0
2 1 2
) 1 ( ) 1 )( 1 ( 2 ) 1 ( 4
) 1 ( ) 1 ( 4
) (
÷ ÷ ÷ ÷
÷ ÷
+ O + + ÷ O + ÷
+ O + ÷
=
z z z F z F
z z F
z H
B s s
s


) 2 1 ( ) 1 ( 2 ) 2 1 ( 4
) 2 1 ( ) 2 1 ( 4
) (
2 1 2
0
2 2 1 2
2 1 2
0
2 1 2
÷ ÷ ÷ ÷ ÷
÷ ÷ ÷ ÷
+ + O + ÷ O + + ÷
+ + O + + ÷
=
z z z F z z F
z z z z F
z H
B s s
s


2 2
0
2 1 2 2
0
2
0
2
2 2
0
2 1 2 2
0
2
0
2
] 2 4 [ ] 8 2 [ ] 2 4 [
] 4 [ ] 8 2 [ ] 4 [
) (
÷ ÷
÷ ÷
O + O ÷ + ÷ O + O + O +
O + + ÷ O + O +
=
z F F z F F F
z F z F F
z H
B s s s B s s
s s s


Normalizando la ecuación se obtiene la siguiente relación:

2
2
0
2
2
0
2
1
2
0
2
2 2
0
2
2
0
2
2
0
2
1
2
0
2
2 2
0
2
0
2
2
0
2
2 4
2 4
2 4
8 2
1
2 4
4
2 4
8 2
2 4
4
) (
÷ ÷
÷ ÷
O + O +
O + O ÷
+
O + O +
÷ O
+
O + O +
O +
+
O + O +
÷ O
+
O + O +
O +
=
z
F F
F F
z
F F
F
z
F F
F
z
F F
F
F F
F
z H
B s s
B s s
B s s
s
B s s
s
B s s
s
B s s
s


De la anterior ecuación, se pueden estáblecer las siguientes constantes:
222
2
0
2
2
0
2
3
2
0
2
2 2
0
2
1
1 3
2
0
2
2 2
0
2
2
0
2
2
0
2
1
2
3
1
2
0
1
2
3
1
2
0
1
2 4
2 4
2 4
8 2
1
2 4
8 2
2 4
4
, ) (
O + O +
O + O ÷
=
O + O +
÷ O
=
=
=
O + O +
÷ O
=
O + O +
O +
=
+ +
+ +
=
÷ ÷
÷ ÷
B s s
B s s
B s s
s
B s s
s
B s s
s
F F
F F
b
F F
F
b
b
a a
F F
F
a
F F
F
a
z b z b z b
z a z a z a
z H




14.6.9 Ejemplo de diseño filtro rechaza banda
Fs = 1291.32Hz.
Fo = 100Hz.
Fb = 10Hz.


A continuación se determinan las frecuencias análogas para el diseño del filtro rechaza banda:


015 , 641 )
32 , 1291
100
tan( ) 32 , 1291 ( 2 ) tan( 2
0
0
= = = O
t t
s
s
F
F
F


8442 , 62 )
32 , 1291
10
tan( ) 32 , 1291 ( 2 ) tan( 2 = = = O
t t
s
b
s B
F
F
F

Posteriormente se determinan los coeficientes del filtro:

, ) (
2
3
1
2
0
1
2
3
1
2
0
1
÷ ÷
÷ ÷
+ +
+ +
=
z b z b z b
z a z a z a
z H
223
901547 0.95518463
) 015 , 641 ( ) 8442 , 62 )( 32 , 1291 ( 2 ) 32 , 1291 ( 4
) 015 , 641 ( ) 8442 , 62 )( 32 , 1291 ( 2 ) 32 , 1291 ( 4
2 4
2 4
6958352 -1.7282689
1
9507735 0,97759231
6958352 -1.7282689
) 015 , 641 ( ) 8442 , 62 )( 32 , 1291 ( 2 ) 32 , 1291 ( 4
) 32 , 1291 ( 8 ) 015 , 641 ( 2
2 4
8 2
9507735 0,97759231
) 015 , 641 ( ) 8442 , 62 )( 32 , 1291 ( 2 ) 32 , 1291 ( 4
) 015 , 641 ( ) 32 , 1291 ( 4
2 4
4
2 2
2 2
2
0
2
2
0
2
3
2 2
1
1 3
2 2
2 2
2
0
2
2 2
0
2
2 2
2 2
2
0
2
2
0
2
1
=
+ +
+ ÷
=
O + O +
O + O ÷
=
= =
=
= =
=
+ +
÷
=
O + O +
÷ O
=
=
+ +
+
=
O + O +
O +
=
B s s
B s s
B s s
s
B s s
s
F F
F F
b
a b
b
a a
F F
F
a
F F
F
a


El código fuente en lenguaje C, correspondiente a este diseño es el siguiente:

//Declaración de varíales.
float x0=0, x1=0, x2=0, y0=0, y1=0, y2=0;
unsigned int YY;
//Declaración de la función de interrupciones.
void interrupt ( void )
{
if( INTCON.F2 )
{
TMR0L=135;
//Timer0 con periodo de 774,4u segundo.
// Fs = 1291,32 Hz.
//Adquisición de una muestra de 10 bits en, x[0].
x0 = (float)(ADC_Read(0)-512.0);
//Implementación de la ecuación en diferencias.
y0 = (x0+x2)*0.977592319507735 + (y1-x1)*1.72826896958352 - y2*0.95518463901547;
//Corrimiento de los valores x(n), y y(n).
y2 = y1;
y1 = y0;
x2 = x1;
x1 = x0;
//Reconstrucción de la señal: y en 10 bits.
YY = (unsigned int)(y0+512.0);
PORTC = (YY>>8)&3;
PORTB = YY&255;
INTCON.F2=0;
}
}

void main( void )
{
//Inicio del puerto B como salida.
TRISB = 0;
224
PORTB = 0;
TRISC = 0;
PORTC = 0;
//Se configura el TIMER 0, su interrupción.
INTCON = 0b10100000;
T0CON = 0b11000101;
while(1)//Bucle infinito.
{
}
}
14.7 Osciladores digitales
Generar una señal seno de forma sintética, implica usar una matriz de rotación que incluye dos
valores de salida de la señal y dos valores pasados, de la misma. La forma general de está matriz
se puede apreciar a continuación:

(
¸
(

¸

÷
÷
(
¸
(

¸

=
(
¸
(

¸

) 1 (
) 1 (
) (
) (
n x
n y
d c
b a
n x
n y


Los cálculos de la matriz de rotación, permiten crear osciladores de 2 salidas que pueden tener
amplitud equitativa o no y salida en cuadratura o no, así como el número de multiplicaciones
requeridas por iteración. Estas condiciones se resumen en la siguiente tabla:

Propiedades de los osciladores recursivos
Oscilador
Multiplicaciones
por iteración
Amplitud
equitativa
Salida en
cuadratura
Factores Matriz de rotación
Doble
cuadrado
2 Si No k=2Cos(θ)
(
¸
(

¸
÷
0 1
1 k

De acople
en
cuadratura
estándar
4 Si Si k=sen(θ)
(
(
¸
(

¸

÷ ÷
÷
2
2
1
1
k k
k k


Tabla 14-1

Para iniciar el diseño de un oscilador o generador digital se debe definir el valor de la constante k,
y por defecto, el valor del ángulo θ, este se define por medio de la siguiente ecuación:

s
g
F
F t
u
2
=
Ecuación 14-22


225
Donde Fg es la frecuencia en hercios de la señal que se desea generar, y Fs es la frecuencia de
muestreo en hercios. La implementación de estos generadores siempre usa la misma forma de
operaciones matemáticas, que es la siguiente:

) 1 ( ) 1 ( ) (
) 1 ( ) 1 ( ) (
÷ + ÷ =
÷ + ÷ =
n dx n cy n x
n bx n ay n y


Para que el oscilador arranque correctamente es importante dar valores iniciales a las salidas y1, y
y2. Por obvias razones los osciladores de mayor velocidad son los que solo requieren de una
multiplicación por iteración.
14.7.1 Ejemplo de diseño oscilador doble cuadrado
Fs = 1291,32Hz.
Fg = 100Hz.

El valor del ángulo θ, es el que se calcula a continuación:

4865707421 , 0
32 , 1291
100 2
2
= = =
t
t
u
s
g
F
F


Con el valor θ se puede calcular el valor de k, que es el siguiente:

k=2Cos(0,4865707421) = 1,76788313

De está manera se tiene la siguiente matriz de rotación:

(
¸
(

¸
÷
0 1
1 1,76788313


Las ecuaciones de implementación son las siguientes:

) 1 ( ) (
) 1 ( ) 1 ( 1,76788313 ) (
÷ =
÷ + ÷ =
n y n x
n x n y n y


Las mismas funciones en términos de solo la salida y es igual a la siguiente ecuación:

) 2 ( ) 1 ( 1,76788313 ) ( ÷ + ÷ = n y n y n y

La condición para el funcionamiento de este oscilador es que la magnitud de la señal debe ser 1.
Otra condición para el funcionamiento adecuado de este oscilador es dar valores iniciales a las
salidas y(n-1), y y(n-2), las cuales se pueden iniciar de la siguiente forma:

y(n-1) = Cos(-θ) = Cos(-0,4865707421).
y(n-2) = Cos(-2 θ) = Cos(-2(0,4865707421)).

Para manipular la amplitud se debe multiplicar la salida y(n), por el factor deseado.
226
Para fines de simulación, se implementarán las mismas condiciones del hardware usado en los
filtros FIR, e IIR, omitiendo el generador de onda seno que se conecta a la entrada análoga.
Teniendo presente las anteriores observaciones se puede implementar el siguiente código fuente
en lenguaje C, para simular este ejemplo:

//Declaración de varíales inicializadas.
float y0, y1=1.0, y2=0.883941565;
unsigned int YY;
//Declaración de la función de interrupciones.
void interrupt ( void )
{
if( INTCON.F2 )
{
TMR0L=135;
//Timer0 con periodo de 774,4u segundo.
// Fs = 1291,32 Hz.
//Implementación de la ecuación del oscilador.
y0 = 1.76788313*y1 - y2;
//Se hace el corrimiento de la salida.
y2 = y1;
y1 = y0;
//Reconstrucción de la señal: y en 10 bits, amplificada 500 veces.
YY = (unsigned int)(500*y0+512.0);
PORTC = (YY>>8)&3;
PORTB = YY&255;
INTCON.F2=0;
}
}

void main( void )
{
//Inicio del puerto B como salida.
TRISB = 0;
PORTB = 0;
TRISC = 0;
PORTC = 0;
//Se configura el TIMER 0, su interrupción.
INTCON = 0b10100000;
T0CON = 0b11000101;
while(1)//Bucle infinito.
{
}
}
14.7.2 Ejemplo de diseño para oscilador de acople en cuadratura
Fs = 1291,32Hz.
Fg = 150Hz.
A = 450.
227
Se calcula el ángulo de paso θ:

7298561132 , 0
32 , 1291
150 2
2
= = =
t
t
u
s
g
F
F


Posteriormente se calculan las constantes de la matriz:

. 6667624073 , 0 ) (
. 7452703484 , 0 )] 7298561132 , 0 ( [ 1 )] ( [ 1
2 2
= = ÷ =
= ÷ = ÷ = =
u
u
sen c b
sen sen d a


El patrón de la matriz de rotación es el siguiente:

(
¸
(

¸

÷ 7452703484 , 0 6667624073 , 0
6667624073 , 0 7452703484 , 0


El juego de ecuaciones que rigen las iteraciones del oscilador son las siguientes:

) 1 ( 7452703484 , 0 ) 1 ( 6667624073 , 0 ) (
) 1 ( 6667624073 , 0 ) 1 ( 7452703484 , 0 ) (
÷ + ÷ ÷ =
÷ + ÷ =
n x n y n x
n x n y n y


Para dar inicio correcto al oscilador se deben dar valores iniciales a las salidas retardadas y(n-1),
y x(n-1), para este fin se debe tener presente que este oscilador tiene las dos salidas en cuadratura,
es decir que entre las dos salidas siempre existe un desfase de 90 grados. Bajo este concepto se
puede concluir que las dos señales en cuadratura tendrán la misma magnitud a 3π/4, de está
manera se asignan los siguientes valores iniciales:

y(n-1) = x(n-1) = ASen(3π/4),
y(n-1) = x(n-1) = 450Sen(3π/4) = 318,1980515.

Finalmente se implementa, el código fuente en lenguaje C, para este oscilador:

//Declaración de varíales inicializadas.
float y0, y1=318.1980515, x0, x1=318.1980515;
unsigned int YY;
//Declaración de la función de interrupciones.
void interrupt ( void )
{
if( INTCON.F2 )
{
TMR0L=135;
//Timer0 con periodo de 774,4u segundo.
// Fs = 1291,32 Hz.
//Implementación de las ecuaciones del oscilador.
y0 = 0.7452703484*y1 + 0.6667624073*x1;
x0 = -0.6667624073*y1 + 0.7452703484*x1;
//Se hace el corrimiento de la salida.
228
y1 = y0;
x1 = x0;
//Reconstrucción de la señal: y en 10 bits.
YY = (unsigned int)(y0+512.0);
PORTC = (YY>>8)&3;
PORTB = YY&255;
INTCON.F2=0;
}
}

void main( void )
{
//Inicio del puerto B como salida.
TRISB = 0;
PORTB = 0;
TRISC = 0;
PORTC = 0;
//Se configura el TIMER 0, su interrupción.
INTCON = 0b10100000;
T0CON = 0b11000101;
while(1)//Bucle infinito.
{
}
}
14.8 Transformada discreta de Fourier DFT
La transformación de Fourier es un procedimiento matemático que permite analizar las
componentes espectrales y la intensidad de potencia de una señal análoga, o discreta. Las
aplicaciones de este procedimiento son numerosas en las que se pueden nombrar: Analizadores
de espectro, reconocimiento de patrones, en señales, de comunicaciones, biomédicas, y audio,
entre otras. La transformada de Fourier, es una temática sumamente densa, sin embargo este
capítulo busca mostrar de forma simple la implementación de está herramienta. La transformada
discreta de Fourier o DTF, está definida por la siguiente ecuación:

¿
÷
=
÷
=
1
0
) ( ) (
L
n
jwn
e n x jw X
Ecuación 14-23

Como se puede apreciar en la anterior ecuación la potencia de una frecuencia w, se puede evaluar
en un fragmento de señal de longitud L, de igual manera se puede notar que los valores
resultantes de la función X(jw), son números complejos, por está razón los análisis de Fourier se
hacen por excelencia en términos de la magnitud de estos números complejos. La ecuación
(15.23) en términos de un número complejo rectangular es la siguiente:
¿
÷
=
÷ =
1
0
)] ( ) )[cos( ( ) (
L
n
wn jsen wn n x jw X
Ecuación 14-24

229
De está forma la relación X(jw), puede ser pasada a la magnitud de sí misma como |X(jw)|, y su
relación matemática es la siguiente:

2
1
0
2
1
0
) ( ) ( ) cos( ) ( | ) ( |
(
¸
(

¸

+
(
¸
(

¸

=
¿ ¿
÷
=
÷
=
L
n
L
n
wn sen n x wn n x jw X
Ecuación 14-25

Está ecuación matemática permite evaluar la potencia, o la intensidad de la componente w, dentro
de la muestra x(n), de longitud L.

Un fragmento de código en lenguaje C, que realice está operación se puede implementar con una
función como la siguiente:

//Función para determinar la intensidad de potencia
//de la componente w, en la señal x_n.
float TDF( float *x_n, float w, unsigned int L )
{
//Declaracion de variables.
unsigned int n;
float R=0.0, I=0.0;
//Bucle for para realizar las sumatorias.
for( n=0; n<L; n++ )
{
//Cálculo y sumatoria de los componentes
//reales e imaginarios.
R += cos( w*n );
I += sin( w*n );
}
//Se retorna el valor de la magnitud del
// número complejo.
return sqrt( R*R + I*I );
}

El siguiente ejemplo implementa un programa que permite detectar la presencia de tres
frecuencias diferentes en una señal análoga, por medio de la transformada de Fourier. Como
primera medida se definen las tres frecuencias análogas y sus respectivas frecuencias digitales,
F1=100Hz, F2=200Hz, y F3=300Hz, la frecuencia de muestreo se define de 1291,32Hz, con el
fin de implementar la misma estructura del microcontrolador 18F452, que se usó en los filtros
FIR, e IIR.

459712226 , 1
32 . 1291
300 2
9731414842 , 0
32 . 1291
200 2
1 4865770742 , 0
32 . 1291
100 2
3
2
1
= =
= =
= =
t
t
t
W
W
W

230
Para realizar la transformada de Fourier, se usará la función antes citada. Una salida del
microcontrolador se activa cuando se detecta la potencia suficiente de la componente espectral.
La señal es adquirida por una entrada análoga del PIC, la cual es la suma de tres fuentes
diferentes. El programa correspondiente para este ejercicio es el siguiente:

//Declaración de varíales inicializadas.
float x[64];
unsigned short i, OK=1;
//Declaración de la función de interrupciones.

//Función para determinar la intensidad de potencia
//de la componente w, en la señal x_n.
float TDF( float *x_n, float w, unsigned int L )
{
//Declaración de variables.
unsigned short n;
float R=0.0, I=0.0;
//Bucle for para realizar las sumatorias.
for( n=0; n<L; n++ )
{
//Cálculo y sumatoria de los componentes
//reales e imaginarios.
R += x_n[n]*cos( w*n );
I += x_n[n]*sin( w*n );
}
//Se retorna el valor de la magnitud del
// número complejo.
return sqrt( R*R + I*I );
}

void interrupt ( void )
{
if( INTCON.F2 )
{
TMR0L=135;
//Timer0 con periodo de 774,4u segundo.
// Fs = 1291,32 Hz.
if( OK ) //Se evalúa la adquisición de muestras.
{
OK++; //Se cuentan las muestras tomadas.
//Se corren las últimas 64 muestras en el bufer x.
for( i=63; i!=0; i-- )x[i]=x[i-1];
//Se guarda la última muestra.
x[0] = ((float)ADC_Read(0)-512.0);
}
INTCON.F2=0;
}
}

231
void main( void )
{
float RES1, RES2, RES3;
//Inicio del puerto B como salida.
TRISB = 0;
PORTB = 0;
//Se configura el TIMER 0, su interrupción.
INTCON = 0b10100000;
T0CON = 0b11000101;
while(1)//Bucle infinito.
{
while( OK<65 );//Se espera a recibir 64 muestras.
OK =0; //Se suspenden la adquisición de muestras.
//Se hace la transformada de Fourier, de la componente de 100Hz.
RES1 = TDF( x, 0.4865707421, 64 );
//Se hace la transformada de Fourier, de la componente de 200Hz.
RES2 = TDF( x, 0.9731414842, 64 );
//Se hace la transformada de Fourier, de la componente de 300Hz.
RES3 = TDF( x, 1.459712226, 64 );
OK=1; //Se activa la adquisición de muestras.
//Se evalúa la potencia de la señal de 100Hz.
if( RES1>800 )PORTB.F2=1; else PORTB.F2=0;
//Se evalua la potencia de la señal de 200Hz.
if( RES2>800 )PORTB.F1=1; else PORTB.F1=0;
//Se evalúa la potencia de la señal de 300Hz.
if( RES3>800 )PORTB.F0=1; else PORTB.F0=0;
}
}

Para realizar la simulación de este ejercicio se implementa en ISIS, los dispositivos: 18F452,
RES, CAP, OP1P, BUTTON, LED-RED, y generador virtual. El circuito correspondiente para
está simulación es el siguiente:

Circuito 14-2
232
La transformada de Fourier, implica múltiples cálculos matemáticos, como sumas,
multiplicaciones y evaluaciones trigonométricas. Estos procesos hacen lento el rendimiento de la
máquina de proceso. Por esta razón la ciencia de tratamiento de señales, se vio en la necesidad de
buscar técnicas de realizar la transformada de Fourier de forma mucho más eficiente, de este
esfuerzo, sale como resultado notable el algoritmo de transformación rápida de Fourier, o FFT.
Este algoritmo se fundamenta en la división en segmentos cortos para realizar la evaluación de la
transformada de Fourier.
14.8.1 Transformada rápida de Fourier FFT
La transformada rápida de Fourier, organiza en una matriz las muestras de entrada y realiza
trasformadas más pequeñas en las filas y posteriormente en las columnas. Las muestras
digitalizadas de la señal análoga se pueden representar de la siguiente forma:

x(0), x(1), x(2), x(3), x(4), x(5),…………….x(N)

Donde N es el número máximo de muestras que se desean analizar.

Las muestras son organizadas en una matriz de L filas, con M columnas. En la siguiente matriz se
puede apreciar el almacenamiento por columnas:

(
(
(
(
¸
(

¸

=
) 15 ( ) 11 ( ) 7 ( ) 3 (
) 14 ( ) 10 ( ) 6 ( ) 2 (
) 13 ( ) 9 ( ) 5 ( ) 1 (
) 12 ( ) 8 ( ) 4 ( ) 0 (
) , (
x x x x
x x x x
x x x x
x x x x
m l x


Seguidamente se debe realizar la transformación por filas, haciendo la trasformada de M
muestras. Está operación se realiza en función de la siguiente ecuación:

| |
)
2
( )
2
cos(
) 1 ( 0
) 1 ( 0
) , ( ) , (
2
1
0
M
mq
jsen
M
mq
e W
M q
L l
W m l x q l F
M
mq
j
mq
M
M
m
mq
M
t t
t
÷ = =
÷ s s
÷ s s
=
÷
÷
=
¿
Ecuación 14-26
La matriz F(l,q), es un arreglo de valores complejos correspondiente a las transformadas de M,
puntos sobre cada una de las filas. Seguidamente la matriz F(l,q), se multiplica por los factores de
fase Wn, está operación se define en la siguiente ecuación:

)
2
( )
2
cos(
) 1 ( 0
) 1 ( 0
) , ( ) , (
2
N
lq
jsen
N
lq
e W
M q
L l
q l F W q l G
N
lq
j
lq
N
lq
N
t t
t
÷ = =
÷ s s
÷ s s
=
÷
Ecuación 14-27

233
Por último se realiza la trasformada por columnas de L muestras. Está operación se resume en la
siguiente ecuación:
| |
)
2
( )
2
cos(
) 1 ( 0
) 1 ( 0
) , ( ) , (
2
1
0
L
lp
jsen
L
lp
e W
M q
L p
W q l G q p X
L
lp
j
lp
L
L
l
lp
L
t t
t
÷ = =
÷ s s
÷ s s
=
÷
÷
=
¿
Ecuación 14-28

La matriz X(p,q), es la transformada final de las N muestras iniciales. Está matriz contiene en
cada uno de sus términos un número complejo que representa la magnitud y la fase de las
componentes espectrales. La matriz X(p,q), se debe leer por filas, como se aprecia en la siguiente
matriz:
(
(
(
(
¸
(

¸

=
) 15 ( ) 14 ( ) 13 ( ) 12 (
) 11 ( ) 10 ( ) 9 ( ) 8 (
) 7 ( ) 6 ( ) 5 ( ) 4 (
) 3 ( ) 2 ( ) 1 ( ) 0 (
) , (
X X X X
X X X X
X X X X
X X X X
q p X


Cabe denotar que entre más muestras se ingresen para hacer la transformación, mayor será la
resolución en el espectro, pero a su vez mayor será la cantidad de recursos de procesamiento y de
memoria requeridos. A continuación se resume el proceso antes citado:

- Se archiva la señal de x(n), por columnas en la matriz x(l,m).
- Se calcula la transformada de M muestras de cada una de las filas.
- Se multiplican las anteriores transformadas por el factor de fase Wn.
- Se calcula la transformada de L muestras de cada una de las columnas.
- Se leen los datos de la matriz X(p,q), por filas.

El resultado de una transformada de Fourier, entrega un espectro evaluado de 0 a 2π, lo que
significa que los valores superiores a π, son un espejo de las componentes inferiores causadas por
los armónicos de las frecuencias inferiores a π. En conclusión la lectura de la matriz solo se debe
hacer hasta la mitad, por ejemplo si la cantidad de muestras analizadas son 128, solo se deben
leer 64. De la misma manera la resolución de la transformación en hercios está definida por la
siguiente ecuación:
N
F
R
s
Hz
=
Ecuación 14-29

Donde R, es la resolución en hercios, o el mínimo ancho de banda que se puede analizar con la
FFT. Fs es la frecuencia de muestreo, y N es el número de muestras ingresadas a la FFT.
También cabe denotar que la primera muestra de la FFT, hace alusión a la componente espectral
más baja incluida la frecuencia 0, o en otras palabras los niveles DC. Estás componentes son las
más intensas, y para fines prácticos está primera muestra puede ser ignorada.

234
Para realizar un ejemplo con la FFT, se implementará el PIC 18F4585, dada su amplia memoria
RAM de 3328 Bytes, útil para los procesos de la FFT. Sin embargo está capacidad de memoria
solo permite hacer una FFT, con máximo 169 muestras.

A continuación se presentará un ejemplo en lenguaje C, para realizar la transformación de una
señal de N = ML, muestras:

#define M 16
#define L 8
#define N L*M
#define PI 3.141592654

typedef struct
{
float R, I;
}Complejo;

//Declaración de varíales inicializadas.

unsigned short i, OK=0;
unsigned short Fi=0, Co=0;
//Declaración de la función de interrupciones.

Complejo X[L][M];
Complejo Y[L][M];

//Función para realizar una multiplicación compleja.
Complejo Producto( Complejo A, Complejo B )
{
Complejo Res;
Res.R = A.R*B.R - A.I*B.I;
Res.I = A.R*B.I + A.I*B.R;
return Res;
}

//Función para realizar una suma compleja.
Complejo Adicion( Complejo A, Complejo B )
{
Complejo Res;
Res.R = A.R + B.R;
Res.I = A.I + B.I;
return Res;
}

//Función para determinar la magnitud de un número complejo.
float Magnitud( Complejo A )
{
return sqrt( A.R*A.R + A.I*A.I );
}
235
//Cálculo rápido de Fourier, de N muestras.
void FFT( void )
{
unsigned short l,q,m,p;
Complejo WW;

//Trío de bucles for, para ejecutar la
//ecuación (15.26), la matriz Y[][], representa F(l,q).
for( l=0; l<L; l++ )
{
for( q=0; q<M; q++ )
{
Y[l][q].R = 0.0;
Y[l][q].I = 0.0;
for( m=0; m<M; m++ )
{
WW.R = cos( 2*PI*m*q / M );
WW.I = -sin( 2*PI*m*q / M );
WW = Producto( X[l][m], WW );
Y[l][q] = Adicion( Y[l][q], WW );
}
}
}


//Dupla se bucles for, para realizar la multiplicación
//de factores Wn (15.27), Y[][] representa la matriz F(l,q), y
//X[][] representa la matriz G(l,q).
for( l=0; l<L; l++ )
{
for( q=0; q<M; q++ )
{
WW.R = cos( 2*PI*l*q / N );
WW.I = -sin( 2*PI*l*q / N );
X[l][q] = Producto( Y[l][q], WW );
}
}

//Trío de bucles for, para ejecutar la
//ecuación (15.28), la matriz Y[][], representa X(p,q),
//y X[][] representa la matriz G(l,q).
for( p=0; p<L; p++ )
{
for( q=0; q<M; q++ )
{
Y[p][q].R=0.0;
Y[p][q].I=0.0;
for( l=0; l<L; l++ )
{
236
WW.R = cos( 2*PI*l*p / L );
WW.I = -sin( 2*PI*l*p / L );
WW = Producto( X[l][q], WW );
Y[p][q] = Adicion( Y[p][q], WW );
}
}
}

//Doble for anidado para determinar,
//la magnitud de la transformada,
//Este resultado queda en los términos reales de X[][]
for( l=0; l<L; l++ )
for( m=0; m<M; m++ )
{
X[l][m].R = Magnitud( Y[l][m] );
X[l][m].I = 0.0;
}

}

void interrupt ( void )
{
if( INTCON.F2 )
{
TMR0L=135;
//Timer0 con periodo de 774,4u segundo.
// Fs = 1291,32 Hz.
if( OK ) //Se evalúa la adquisición de muestras.
{
//Se hace la adquisición de las muestras,
//y se archivan por columnas.
X[Fi][Co].R = ((float)ADC_Read(0)-127.0);
X[Fi][Co].I = 0.0;
Fi++;
if( Fi==L )
{
Fi = 0;
Co++;
if( Co==M )
{
Co=0;
OK=0;
}
}
}
INTCON.F2=0;
}
}

237
void main( void )
{
char Text[30];
unsigned short ff, cc, cont;
//Inicio del puerto B como salida.
TRISB = 0;
PORTB = 0;
//Se configura el TIMER 0, su interrupción.
INTCON = 0b10100000;
T0CON = 0b11000101;
UART1_Init(9600);

while(1)//Bucle infinito.
{
OK = 1; //Se habilita la lectura de muestras.
UART1_Write_Text("// Adquisicion y FFT de:");
IntToStr( N, Text );
UART1_Write_Text(Text);
UART1_Write_Text(" Muestras //");
UART1_Write(13); UART1_Write(10);
while( OK ); //Se espera la adquisicion de las muestras.
FFT(); //Cálculo de la FFT.
UART1_Write_Text("// Inicio de las muestras //");
UART1_Write(13); UART1_Write(10);
//La primera componente espectral se ignora y se hace igual a cero.
X[0][0].R = 0.0;
cont = 0;
//Se envían la magnitud de la FFT, hasta N/2
//por medio del puerto serial.
for( ff=0; ff<L; ff++ )
{
for( cc=0; cc<M; cc++ )
{
FloatToStr( X[ff][cc].R, Text );
UART1_Write_Text(Text);
UART1_Write(13); UART1_Write(10);
cont++;
if( cont==N )
{
cc = M;
ff = L;
}
}
}
UART1_Write_Text("// Fin de las muestras //");
UART1_Write(13); UART1_Write(10);
}
}

238
Para simular este ejercicio en ISIS, se implementa un circuito similar al ejercicio anterior de
transformada discreta de Fourier. Sustituyendo el PIC, por un 18F4585, y anexando un
VIRTUAL TERMINAL. El circuito para simular es el siguiente:

Circuito 14-3
14.9 Control digital PID
El en ámbito del control automático la forma de control más popular es el PID, que significa
proporcional, integral, derivativo. El control es una rama de la física y la electrónica que permite
manipular una variable física para permanecer en un valor deseado arbitrariamente. Todo sistema
de control cuenta con las siguientes características:

- Punto de control
- Error
- Controlador (PID)
- Corrección
- Planta
- Variable a controlar
- Sensor o transductor

Estás características se pueden apreciar en el siguiente diagrama en bloques:



Figura 14-20
239

Para el caso del microcontrolador su función está sujeta a la entrada del punto de control, la
lectura del sensor, y la salida de corrección. El punto de partida de un controlador es el
controlador PID análogo que tiene una función de transferencia PID(s), está función tiene una
relación similar a un conjunto de filtros y su tratamiento es equivalente a un filtro IIR. La función
de transferencia análoga es la siguiente:

sKd
s
Ki
Kp s PID + + = ) (
Ecuación 14-30

De la misma forma que se hace con los filtros IIR, se realiza la transformación bilineal, y este
desarrollo da el siguiente análisis:

) 1 (
) 1 ( 2
) 1 (
) 1 (
2
) (
) 1 (
) 1 ( 2
) 1 ( 2
) 1 (
) (
) 1 (
) 1 ( 2
) 1 (
) 1 ( 2
) (
1
1
1
1
1
1
1
1
1
1
1
1
÷
÷
÷
÷
÷
÷
÷
÷
÷
÷
÷
÷
+
÷
+
÷
+
+ =
+
÷
+
÷
+
+ =
+
÷
+
+
÷
+ =
z
z FsKd
z
z
Fs
Ki
Kp z PID
z
z FsKd
z Fs
z Ki
Kp z PID
z
z FsKd
z
z Fs
Ki
Kp z PID


Del anterior análisis se pueden deducir tres funciones de transferencia para el controlador donde
la entrada, es la señal de error para cada una de ellas. Las respectivas operaciones matemáticas
para su implementación son las siguientes:

) ( ) ( ) ( ) (
) 1 ( )] 1 ( ) ( [ 2 ) (
) 1 ( )] 1 ( ) ( [
2
) (
) ( ] [ ) (
n y n y n y n y
n y n e n e FsKd n y
n y n e n e
Fs
Ki
n y
n e Kp n y
d i p pid
d d
i i
p
+ + =
÷ ÷ ÷ ÷ =
÷ + ÷ + =
=
Ecuación 14-31

Para el caso particular del siguiente ejemplo se emplea la técnica de conversión digital análogo
por medio de una salida PWM, y como estrategia para simular el comportamiento de la planta se
usa un circuito RC, de segundo orden. El siguiente código fuente muestra un control PID
correspondiente a la sintonización del control PID, cuando su Kp crítico es de 1,8 y un periodo
crítico de 41,2m segundos.

//Declaración de constantes y variables.
const float Kp=1.08, Ki=52.42718447, Kd=0.005562, Fs = 152.5878906;
//Declaración de coeficientes de integración y derivación.
const float Fi=ki/(2.0*Fs), Fd=2.0*Fs*Kd;

240
float SENSOR, PUNTO_DE_CONTROL=127.0, YN=0.0;
int ADQUI;
unsigned short SALIDA;

float e0=0.0, e1=0.0, yi0=0.0, yi1=0.0, yd0=0.0, yd1=0.0, ypid=0.0;

//Función de interrupciones para el Timer 0.
void interrupt()
{
if( INTCON.F2 )// 6,5536ms :: 152,5878906 Hz
{
//Adquisición de la variable controlada.
SENSOR = (float)((ADC_Read(1)>>2)&0xFF);
//Adquisición del punto de control.
PUNTO_DE_CONTROL = (float)((ADC_Read(0)>>2)&0xFF);
//Calculo del nivel de error.
e0 = PUNTO_DE_CONTROL - SENSOR;
//Ecuación en diferencias.
//Ecuación integral.
yi0=Fi*(e0+e1)+yi1;
//Ecuación derivativa.
yd0=Fd*(e0-e1)-yd1;
//Resultado PID.
ypid=Kp*e0+yi0+yd0;
//Ajuste y corrección de la SALIDA Y(n)
//delimitada por los límites 0 y 255.
YN += ypid;
if(YN>255.0)YN=255.0;
if(YN<0.0)YN=0.0;
SALIDA = (unsigned short)(YN);
PWM1_Set_Duty(SALIDA);
//Actualización de muestras.
e1=e0;
yi1=yi0;
yd1=yd0;
INTCON.F2=0;
}
}

void main( void )
{
//Configuración del modulo PWM.
PWM1_Init(10000);
PWM1_Start();
//Configuración de la interrupción de Timer 0.
//a 6,5536ms
OPTION_REG = 0b00000110;
INTCON = 0b10100000;
while( 1 ) //Bucle infinito.
241
{
}
}


El circuito para simular implementa los dispositivos: 16F877A, POT-HG, RES, CAP, OP1P La
simulación trabaja con reloj de 20MHz. El esquemático del circuito es el siguiente:



Circuito 14-4


Para la sintonía del controlador, y establecer las constantes Kp, Ki, y Kd, se puede usar la
siguiente estrategia:

Primero se establece el controlador con las constantes Ki, y Kd igual a 0. Posteriormente se
incrementa el valor de Kp, gradualmente hasta que la salida oscile de forma sostenida. Cuando
esto sucede se mide el periodo de la oscilación sostenida y se determina el valor de Kp. Estos dos
parámetros se denominan periodo crítico Pc, y Kp crítico Kpc. Posteriormente estos valores se
remplazan en la siguiente tabla, para determinar los valores definitivos de Kp, Ki, y Kd:

Tipo de control Kp Ti Td
P 0,5Kpc Infinito, Ki=0 0
PI 0,45Kpc Pc/1,2 0
PID 0,6Kpc 0,5Pc 0,125Pc

Tabla 14-2

Donde, Ti es el periodo de integración y Td el periodo derivativo. La relación de estos periodos
con las constantes Ki, y Kd se especifica en las siguientes ecuaciones:
242

TdKp Kd
Ti
Kp
Ki
=
=
Ecuación 14-32
243
15 Transmisión de datos
La transmisión de datos es una línea de las comunicaciones electrónicas que se encarga de
garantizar el transporte de información de forma digital. La información enviada puede ser
bloques de texto, de audio, video, o cualquier otro tipo de información. Para realizar la
transmisión de información se pueden seguir diversos formatos y protocolos, sin embargo este
capítulo se centra en técnicas básicas de transmisión de datos como son, el control de flujo de la
información, la transparencia de datos y el control de errores. Las técnicas de transmisión de
datos permiten optimizar y garantizar el estado correcto de los datos durante una transmisión de
datos. Este es un aspecto importante dado que un medio de transmisión está expuesto a producir
errores por razones de ruido o falta de continuidad en el medio de transmisión de datos. El
siguiente grafico ilustra de forma simple el tráfico de información entre dos puntos que gestionan
una comunicación.


Figura 15-1

244
15.1 Control de flujo
El control de flujo permite gestionar el tráfico de información de forma ordenada. Cuando el
medio de transmisión es altamente confiable, se puede implementar solamente el control de flujo.
Por otro lado es importante tener presente que los ejemplos mostrados en este capítulo trafican
con la información en código ASCII, y el carácter de reconocimiento positivo ACK, se representa
con el valor 0x06, el carácter de reconocimiento negativo NAK, se representa con el código
0x15, los finales de cadenas de texto o de caracteres se representan con el carácter nulo NULL
que equivale al código 0x00. El siguiente ejemplo muestra una comunicación simples con el
respectivo control de flujo, implementado con un microcontrolador PIC 16F628A, con oscilador
de 4MHz.

// Declaración de constantes del LCD
sbit LCD_RS at RB0_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D7 at RB7_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_RS_Direction at TRISB0_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D7_Direction at TRISB7_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB4_bit;

//Declaración de variables de trabajo.
char BUFER[20], Dato, Bandera=0;
unsigned short Estado=0, N=0;

//Declaración de constantes.
const char ACK='A', NAK='N', ENQ='&', EOT='%', NULL=0x00;

//Vector de interrupciones.
void interrupt()
{
//Se evalúa la interrupción por recepción serial.
if( PIR1.F5 )
{
//Se lee el Dato de entrada.
Dato = UART1_Read();
//Se evalúa el estado del tráfico de información.
switch( Estado )
{
//En espera...
case 0: //Se evalúa si llega una solicitud..
if( Dato == ENQ )
{
//Se cambia al siguiente Estado y se
245
//envía el reconocimiento positivo.
Estado = 1;
N = 0;
UART1_Write(ACK);
}
else
{
//Se envía reconocimiento negativo.
UART1_Write(NAK);
}
break;
//Recibiendo...
case 1: // Se evalúa si lo que llega es el final de transmisión EOT
if( Dato == EOT )
{
//Llega el fin de la información y se vuelve
//al Estado inicial y se activa la Bandera de datos
//de llegada.
BUFER[N] = NULL;
Estado = 0;
Bandera = 1;
}
else
{
//Se archiva el Dato recibido y se incrementa
//el indicador N
BUFER[N] = Dato;
N++;
}

default: break;
}
}
}

void main( void )
{
//Se activan las interrupciones periféricas.
INTCON = 0b11000000;
//Se activa la interrupción por recepción serial.
PIE1 = 0b00100000;
//Configuración del modulo USART a 9600 bps.
UART1_Init(9600);
//Configuración del LCD.
Lcd_Init();

Lcd_Out(1,1,"Control de Flujo");

while(1) //Bucle infinito.
246
{
//Se evalúa si la Bandera esta activa.
if( Bandera == 1 )
{
//Se imprime la información en el LCD.
Lcd_Out(2,1," ");
Lcd_Out(2,1,BUFER);
Bandera = 0;
}
}
}

Es importante notar que en la declaración de constantes se usan como caracteres de ACK, NAK,
ENQ y EOT, los caracteres A, N, &, y %, esto solo se realiza en este ejemplo de forma didáctica
dado que este tipo de caracteres tienen valores y equivalencias que no se pueden ver ni digitar en
el VIRTUAL TERMINAL.

Para realizar la respectiva simulación de este ejemplo se implementa en ISIS los dispositivos
PIC16F628A, VIRTUAL TERMINAL, y LM016L, en un circuito como el siguiente:


Circuito 15-1

La transmisión de información obedece a la siguiente estructura:


Figura 15-2
247
15.2 Transparencia de datos
La transparencia de datos es la capacidad de empaquetar información con un bloque de inicio o
encabezado y un bloque final del paquete de información, el éxito de la transparencia esta en no
repetir los datos de encabezado o final dentro de la información. De esta manera se puede ver de
forma transparente la información dentro de los datos de control. Para encabezar la información
se implementa el carácter DLE o marcador de transparencia equivalente al código 0x10, STX
comienzo de texto que equivale al código 0x02, y para finalizar el bloque de datos se usa el
carácter ETX que es equivalente a 0x03.

Para demostrar el funcionamiento de la estructura de transparencia se implementan a
continuación dos programas uno que transmite información y otro que la recibe, los programas
esta fundamentados en un microcontrolador PIC 16F628A, y un PIC 16F877A ambos con
oscilador de 4MHz, a continuación se puede apreciar el código fuente del modulo receptor que es
similar al ejemplo del control de flujo:

// Declaración de constantes del LCD
sbit LCD_RS at RB0_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D7 at RB7_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_RS_Direction at TRISB0_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D7_Direction at TRISB7_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB4_bit;

//Declaración de variables de trabajo.
char BUFER[20], Dato, Bandera=0;
unsigned short Estado=0, N=0;

//Declaración de constantes.
const char ACK=0x06, NAK=0x15, ENQ=0x05, EOT=0x04, STX=0x02, ETX=0x03,
DLE=0x10,
NULL=0x00;

//Vector de interrupciones.
void interrupt()
{
//Se evalúa la interrupción por recepción serial.
if( PIR1.F5 )
{
//Se lee el Dato de entrada.
Dato = UART1_Read();
//Se evalúa el estado del tráfico de información.
switch( Estado )
248
{
//En espera...
case 0: //Se evalúa si llega una solicitud..
if( Dato == ENQ )
{
//Se cambia al siguiente Estado y se
//envía el reconocimiento positivo.
Estado = 1;
UART1_Write(ACK);
}
else
{
//Se envía reconocimiento negativo.
UART1_Write(NAK);
}
break;
//Delimitador de transparencia...
case 1: // Se evalúa si lo que llega es DLE.
if( Dato == DLE )
{
//Se cambia al siguiente Estado...
Estado = 2;
}
else
{
//Se identifica un error de orden en los datos
//y se transmite un reconocimiento negativo y se
//reanuda al Estado inicial.
//Se reinicia de nuevo al Estado inicial.
Estado = 0;
UART1_Write(NAK);
}
break;
// Recibiendo información...
case 2: // Se evalúa si lo que llega es STX.
if( Dato == STX )
{
//Se inicia la cadena de información y se
//pasa al Estado 3
N=0;
BUFER[N]=NULL;
Estado = 3;
}
else
{
//Se identifica un error de orden en los datos
//y se transmite un reconocimiento negativo y se
//reanuda al Estado inicial.
//Se reinicia de nuevo al Estado inicial.
249
Estado = 0;
UART1_Write(NAK);
}
break;
// Esperando datos y delimitador de transparencia.
case 3: //Se evalúa si el Dato que llega es un DLE.
if( Dato == DLE )
{
//Se cambia al Estado siguiente.
Estado = 4;
//Se finaliza el BUFER de datos;
BUFER[N]=NULL;
}
else
{
//Se anexa un nuevo Dato al BUFER.
BUFER[N]=Dato;
//Se incrementa el índice de los datos.
N++;
}
break;
// Esperando fin de cadena...
case 4: // Se evalúa si llega un ETX
if( Dato == ETX )
{
//Se cambia al Estado siguiente.
Estado = 5;
}
else
{
//Se cambia de nuevo al Estado inicial.
Estado = 0;
//Se transmite reconocimiento negativo.
UART1_Write(NAK);
}
break;
//Esperando fin de transmisión...
case 5: //Se evalúa si llega el EOT...
if( Dato == EOT )
{
//Se activa la Bandera de llegada de información.
Bandera = 1;
//Se transmite reconocimiento positivo.
UART1_Write(ACK);
}
else
{
//Se transmite reconocimiento negativo.
UART1_Write(NAK);
250
}
Estado = 0;
break;

default: break;
}
}
}

void main( void )
{
//Se activan las interrupciones periféricas.
INTCON = 0b11000000;
//Se activa la interrupción por recepción serial.
PIE1 = 0b00100000;
//Configuración del modulo USART a 9600 bps.
UART1_Init(9600);
//Configuración del LCD.
Lcd_Init();
//Texto de inicio.
Lcd_Out(1,1,"Transparencia");
while(1) //Bucle infinito.
{
//Se evalúa si la Bandera esta activa.
if( Bandera == 1 )
{
//Se imprime la información en el LCD.
Lcd_Out(2,1," ");
Lcd_Out(2,1,BUFER);
Bandera = 0;
}
}
}

El siguiente programa está diseñado para un microcontrolador PIC 16F877A, el cual toma la
lectura de un sensor LM35, y la transmite con el respectivo control de flujo y transparencia:

//Declaración de constantes.
const char ACK=0x06, NAK=0x15, ENQ=0x05, EOT=0x04, STX=0x02, ETX=0x03,
DLE=0x10, NULL=0x00;

void main( void )
{
char BUFER[10];
int adc;
float Temp;
//Se configura el modulo ADC, con el pin
//AN3 como voltaje de referencia.
ADCON1 = 0b11000001;
251
//Se inicia el modulo USART a 9600 bps.
UART1_Init(9600);
delay_ms(500);
while(1)//Bucle infinito.
{
OTRO:
//Lectura del canal 0
adc = ADC_Read(0);
//Se calcula el valor de la temperatura.
Temp = 0.244*adc;
//Se escribe la temperatura en el BUFER.
FloatToStr( Temp, BUFER );
//Retardo de lectura de 400m segundos
delay_ms(100);
//Se transmite el ENQ...
UART1_Write(ENQ);
//Se espera el ACK...
while( !UART1_Data_Ready() );
//Se verifica si llega un ACK...
if( UART1_Read() != ACK )goto OTRO;
//Se transmite un DLE...
UART1_Write(DLE);
//Se transmite un STX...
UART1_Write(STX);
//Se transmite la información del BUFER...
UART1_Write_Text(BUFER);
//Se transmite un DLE...
UART1_Write(DLE);
//Se transmite el ETX...
UART1_Write(ETX);
//Se transmite el fin de transmisión...
UART1_Write(EOT);
//Se espera el ACK
while( !UART1_Data_Ready() );
//Se verifica si llega un ACK...
if( UART1_Read() != ACK )goto OTRO;
}
}

La estructura para la transparencia de datos obedece a la siguiente forma:



Figura 15-3
252
Para realizar la respectiva simulación se realiza el siguiente circuito que tiene los dispositivos,
PIC 16F628A, PIC 16F877A, RES, LM016L, LM35:



Circuito 15-2


15.3 Control de errores CRC
El control de errores en una transmisión es de suma importancia y más cuando la calidad del
medio de transmisión no es confiable. Para detectar los errores de transmisión existen diferentes
técnicas entre las cuales se pueden citar, VRC o verificación de redundancia vertical, esta
estrategia anexa un bit más a cada paquete de información el cual representa el número par o
impar de unos en el paquete. LRC o Verificación de redundancia longitudinal la cual verifica el
número de bits pares o impares en un grupo de bytes, para este fin se realiza la operación lógica o
exclusiva, entre ellos. Por último se puede hablar del CRC, o verificación de redundancia cíclica,
que es la estrategia de mayor efectividad, y que es la técnica en la cual se centra el ejemplo de
este capítulo. Para llegar al resultado del CRC, se efectúa una división binaria entre el paquete de
datos que se desean transmitir y un polinomio fijo que debe respetar algunas condiciones.
Después de hacer la división el residuo de la misma es anexado al paquete de información y este
es el CRC. El ente receptor realiza la misma división con el mismo polinomio y después de
culminarla el resultado consignado en el residuo debe ser 0 si los datos han llegado sin ningún
error, de lo contrario los datos del paquete están deteriorados.



La estructura de información con el CRC incorporado se implementa en la forma que se puede
apreciar en la siguiente figura:

253


Figura 15-4

Los polinomios que se implementan en el cálculo del CRC se denotan en términos de X, en el
cual cada X, es un 1 en el polinomio, para ilustrar esta situación se puede apreciar el siguiente
ejemplo:

Este polinomio equivale al siguiente número binario:

10100111

Para que los polinomios funciones de forma adecuada deben cumplir con las siguientes
condiciones:

- Debe empezar y terminar con un 1.
- No debe ser divisible por X, es decir por 10.
- Debe ser divisible por X+1, es decir por 11.

Los siguientes son polinomios estándar para diferente número de bits:


CRC-4

CRC-8

CRC-12

CRC-16

CRC-32

El siguiente ejemplo aritmético muestra una división para generar el CRC, y su respectiva
verificación:
254


La implementación de una división de este modo es demasiado compleja. Cuanto más larga sea
la trama más difícil es realizarla, por eso es más simple realizar una operación lógica con
registros de desplazamiento como estrategia para determinar el residuo de la división. El
siguiente diagrama muestra un circuito para el polinomio:

Figura 15-5

Usando como estrategia de programación el corrimiento de bits como lo muestra la figura se
puede hacer un conjunto de funciones que calculen el CRC, sobre una trama de longitud
indefinida teniendo como base mínima un byte y polinomios con orden múltiplo de 8, tales como
8, 16, 24, 32. Para comprender con claridad este concepto se puede observar el siguiente ejemplo
que usa un polinomio de orden 8:

Polinomio:

100110001
Información 1 0 1 1 0 1 0 1 1
Polinomio 1 0 0 1 1
1 0 1 1 0 1 0 1 1 0 0 0 0 1 0 1 1 0 1 0 1 1 1 1 0 0
1 0 0 1 1 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 1 0 0 1 1 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
0 1 0 1 1 ↓ ↓ ↓ ↓ ↓ ↓ ↓ 0 1 0 1 1 ↓ ↓ ↓ ↓ ↓ ↓ ↓
0 0 0 0 0 ↓ ↓ ↓ ↓ ↓ ↓ ↓ 0 0 0 0 0 ↓ ↓ ↓ ↓ ↓ ↓ ↓
1 0 1 1 0 ↓ ↓ ↓ ↓ ↓ ↓ 1 0 1 1 0 ↓ ↓ ↓ ↓ ↓ ↓
1 0 0 1 1 ↓ ↓ ↓ ↓ ↓ ↓ 1 0 0 1 1 ↓ ↓ ↓ ↓ ↓ ↓
0 1 0 1 1 ↓ ↓ ↓ ↓ ↓ 0 1 0 1 1 ↓ ↓ ↓ ↓ ↓
0 0 0 0 0 ↓ ↓ ↓ ↓ ↓ 0 0 0 0 0 ↓ ↓ ↓ ↓ ↓
1 0 1 1 1 ↓ ↓ ↓ ↓ 1 0 1 1 1 ↓ ↓ ↓ ↓
1 0 0 1 1 ↓ ↓ ↓ ↓ 1 0 0 1 1 ↓ ↓ ↓ ↓
0 1 0 0 0 ↓ ↓ ↓ 0 1 0 0 1 ↓ ↓ ↓
0 0 0 0 0 ↓ ↓ ↓ 0 0 0 0 0 ↓ ↓ ↓
1 0 0 0 0 ↓ ↓ 1 0 0 1 1 ↓ ↓
1 0 0 1 1 ↓ ↓ 1 0 0 1 1 ↓ ↓
0 0 1 1 0 ↓ 0 0 0 0 0 ↓
0 0 0 0 0 ↓ 0 0 0 0 0 ↓
0 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 Chequeo →
Calculo del CRC Chequeo del CRC
Se cambia el polinomio por 0 →
dado que el bit mas
significativo es cero
← CRC
255
unsigned short CRC=0;

//Función para insertar in bit en el cálculo del CRC.
void CorrerBitCRC( char _bit )
{
unsigned short CRC2=0;
CRC2 = CRC2 | (CRC<<1)&128; //Set X7
CRC2 = CRC2 | (CRC<<1)&64; //Set X6
CRC2 = CRC2 | ((CRC<<1)^(CRC>>2))&32; //Set X5
CRC2 = CRC2 | ((CRC<<1)^(CRC>>3))&16; //Set X4
CRC2 = CRC2 | (CRC<<1)&8; //Set X3
CRC2 = CRC2 | (CRC<<1)&4; //Set X2
CRC2 = CRC2 | (CRC<<1)&2; //Set X1
CRC2 = CRC2 | ((CRC>>7)^_bit)&1; //Set X0
CRC = CRC2;
}
//Función para insertar un Byte al cálculo del CRC.
void CorrerByteCRC( char Byte )
{
short B;
for( B=7; B>=0; B-- )
CorrerBitCRC( (Byte>>B)&1 );
}

//Función para calcular el CRC de una trama de texto.
unsigned short CalcularRCR( char *trama, unsigned char FINAL )
{
unsigned short N=0;
CRC = 0;
while( trama[N]!=0 )
{
CorrerByteCRC( trama[N] );
N++;
}
CorrerByteCRC( FINAL );
return CRC;
}

Para realizar la implementación de este ejemplo, se usa el mismo hardware del ejemplo de
transparencia, el código fuente para el microcontrolador transmisor implementando el CRC es el
siguiente:

//Función para insertar un Byte al cálculo del CRC.
void CorrerByteCRC( char Byte )
{
short B;
for( B=7; B>=0; B-- )
CorrerBitCRC( (Byte>>B)&1 );
}
256
//Función para calcular el CRC de una trama de texto.
unsigned short CalcularRCR( char *trama, unsigned char FINAL )
{
unsigned short N=0;
CRC = 0;
while( trama[N]!=0 )
{
CorrerByteCRC( trama[N] );
N++;
}
CorrerByteCRC( FINAL );
return CRC;
}

void main( void )
{
char BUFER[10];
int adc;
float Temp;
//Se configura el modulo ADC, con el pin
//AN3 como voltaje de referencia.
ADCON1 = 0b11000001;
//Se inicia el modulo USART a 9600 bps.
UART1_Init(9600);
delay_ms(500);
while(1)//Bucle infinito.
{
OTRO:
//Lectura del canal 0
adc = ADC_Read(0);
//Se calcula el valor de la temperatura.
Temp = 0.244*adc;
//Se escribe la temperatura en el BUFER.
FloatToStr( Temp, BUFER );
//Calculo del CRC.
CalcularRCR( BUFER, 0 );
//Retardo de lectura de 400m segundos
delay_ms(100);
//Se transmite el ENQ...
UART1_Write(ENQ);
//Se espera el ACK...
while( !UART1_Data_Ready() );
//Se verifica si llega un ACK...
if( UART1_Read() != ACK )goto OTRO;
//Se transmite un DLE...
UART1_Write(DLE);
//Se transmite un STX...
UART1_Write(STX);
//Se transmite la información del BUFER...
257
UART1_Write_Text(BUFER);
//Se transmite el CRC...
UART1_Write(CRC);
//Se transmite un DLE...
UART1_Write(DLE);
//Se transmite el ETX...
UART1_Write(ETX);
//Se transmite el fin de transmisión...
UART1_Write(EOT);
//Se espera el ACK...
while( !UART1_Data_Ready() );
//Se verifica si llega un ACK...
if( UART1_Read() != ACK )goto OTRO;
}
}


El código fuente para el microcontrolador receptor con la implementación del CRC es el
siguiente:

// Declaración de constantes del LCD
sbit LCD_RS at RB0_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D7 at RB7_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_RS_Direction at TRISB0_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D7_Direction at TRISB7_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB4_bit;

//Declaración de variables de trabajo.
char BUFER[20], Dato, Bandera=0;
unsigned short Estado=0, N=0;

//Declaración de constantes.
const char ACK=0x06, NAK=0x15, ENQ=0x05, EOT=0x04, STX=0x02, ETX=0x03,
DLE=0x10,
NULL=0x00;

unsigned short CRC=0, CRC_2, Lon;

//Función para insertar in bit en el cálculo del CRC.
void CorrerBitCRC( char _bit )
{
unsigned short CRC2=0;
258
CRC2 = CRC2 | (CRC<<1)&128; //Set X7
CRC2 = CRC2 | (CRC<<1)&64; //Set X6
CRC2 = CRC2 | ((CRC<<1)^(CRC>>2))&32; //Set X5
CRC2 = CRC2 | ((CRC<<1)^(CRC>>3))&16; //Set X4
CRC2 = CRC2 | (CRC<<1)&8; //Set X3
CRC2 = CRC2 | (CRC<<1)&4; //Set X2
CRC2 = CRC2 | (CRC<<1)&2; //Set X1
CRC2 = CRC2 | ((CRC>>7)^_bit)&1; //Set X0
CRC = CRC2;
}

//Función para insertar un Byte al cálculo del CRC.
void CorrerByteCRC( char Byte )
{
short B;
for( B=7; B>=0; B-- )
CorrerBitCRC( (Byte>>B)&1 );
}

//Función para calcular el CRC de una trama de texto.
unsigned short CalcularRCR( char *trama, unsigned char FINAL )
{
unsigned short N=0;
CRC = 0;
while( trama[N]!=0 )
{
CorrerByteCRC( trama[N] );
N++;
}
CorrerByteCRC( FINAL );
return CRC;
}

//Vector de interrupciones.
void interrupt()
{
//Se evalúa la interrupción por recepción serial.
if( PIR1.F5 )
{
//Se lee el Dato de entrada.
Dato = UART1_Read();
//Se evalúa el estado del tráfico de información.
switch( Estado )
{
//En espera...
case 0: //Se evalúa si llega una solicitud..
if( Dato == ENQ )
{
//Se cambia al siguiente Estado y se
259
//envía el reconocimiento positivo.
Estado = 1;
UART1_Write(ACK);
}
else
{
//Se envía reconocimiento negativo.
UART1_Write(NAK);
}
break;
//Delimitador de transparencia...
case 1: // Se evalúa si lo que llega es DLE.
if( Dato == DLE )
{
//Se cambia al siguiente Estado...
Estado = 2;
}
else
{
//Se identifica un error de orden en los datos
//y se transmite un reconocimiento negativo y se
//reanuda al Estado inicial.
//Se reinicia de nuevo al Estado inicial.
Estado = 0;
UART1_Write(NAK);
}
break;
// Recibiendo información...
case 2: // Se evalúa si lo que llega es STX.
if( Dato == STX )
{
//Se inicia la cadena de información y se
//pasa al Estado 3
N=0;
BUFER[N]=NULL;
Estado = 3;
}
else
{
//Se identifica un error de orden en los datos
//y se transmite un reconocimiento negativo y se
//reanuda al Estado inicial.
//Se reinicia de nuevo al Estado inicial.
Estado = 0;
UART1_Write(NAK);
}
break;
// Esperando datos y delimitador de transparencia.
case 3: //Se evalúa si el Dato que llega es un DLE.
260
if( Dato == DLE )
{
//Se cambia al Estado siguiente.
Estado = 4;
//Se finaliza el BUFER de datos;
BUFER[N]=NULL;
}
else
{
//Se anexa un nuevo Dato al BUFER.
BUFER[N]=Dato;
//Se incrementa el índice de los datos.
N++;
}
break;
// Esperando fin de cadena...
case 4: // Se evalua si llega un ETX
if( Dato == ETX )
{
//Se cambia al Estado siguiente.
Estado = 5;
}
else
{
//Se cambia de nuevo al Estado inicial.
Estado = 0;
//Se transmite reconocimiento negativo.
UART1_Write(NAK);
}
break;
//Esperando fin de transmisión...
case 5: //Se evalúa si llega el EOT...
if( Dato == EOT )
{
//Se mide la longitud de la trama.
Lon = strlen(BUFER);
//Se filtra el CRC, que está en la última posición.
CRC_2 = BUFER[Lon-1];
BUFER[Lon-1]=0;
//Se calcula el CRC...
CalcularRCR( BUFER, CRC_2 );
// Se verifica que el chequeo sea cero...
if( CRC==0 )
{
//Se activa la Bandera de llegada de información.
Bandera = 1;
//Se transmite reconocimiento positivo.
UART1_Write(ACK);
}
261
else
{
//Se transmite reconocimiento negativo.
UART1_Write(NAK);
}
}
else
{
//Se transmite reconocimiento negativo.
UART1_Write(NAK);
}
Estado = 0;
break;
default: break;
}
}
}

void main( void )
{
//Se activan las interrupciones periféricas.
INTCON = 0b11000000;
//Se activa la interrupción por recepción serial.
PIE1 = 0b00100000;
//Configuración del modulo USART a 9600 bps.
UART1_Init(9600);
//Configuración del LCD.
Lcd_Init();
//Texto de inicio.
Lcd_Out(1,1,"Transparencia");
while(1) //Bucle infinito.
{
//Se evalúa si la Bandera esta activa.
if( Bandera == 1 )
{
//Se imprime la información en el LCD.
Lcd_Out(2,1," ");
Lcd_Out(2,1,BUFER);
Bandera = 0;
}
}
}

262
16 Actuadores y potencia
Los actuadores son dispositivos eléctricos o electrónicos que permiten crear un cambio físico
sobre una variable. Está pueden ser temperatura, velocidad, presión, luminosidad, humedad,
posición angular o lineal, entre otras. Los actuadores pueden ser de accionamiento AC, o DC. Los
actuadores DC, son aquellos como motores, servomotores, lámparas incandescentes, solenoides,
relevadores, entre otros. Los actuadores AC, son aquellos como motores AC, lámparas
incandescentes, resistencias eléctricas, entre otros. Por otra parte la potencia es la cantidad
energía eléctrica requerida para realizar un trabajo determinado. En el caso puntual de la
electricidad la potencia está definida como:

R I
R
V
VI P
e
2
2
= = = Ecuación 16-1

Donde Pe es la potencia eléctrica en vatios, V es la diferencia de potencial eléctrico sobre una
carga eléctrica con resistencia R, por la cual circula una corriente I. En el caso de un
microcontrolador el uso de un arreglo de potencia se hace indispensable cuando un actuador
requiere más voltaje o corriente, que la que puede entregar un pin del PIC. La tensión que
entregan las salidas del PIC siempre es de 5 voltios y la corriente que puede suministrar solo
puede ser del orden de pocos miliamperios.
16.1 Actuadores DC
Los actuadores DC, requieren de corriente siempre directa es decir que siempre circula en la
misma dirección. Para el uso de estos actuadores se implementan esencialmente dos tipos de
dispositivos, los transistores de potencia o los relevadores. Los transistores pueden ser BJT, o
MOSFET. Un transistor BJT, se caracteriza por controlar una corriente por medio de otra
corriente. Los MOSFET, permiten controlar un flujo de corriente por medio de una diferencia de
potencial. Los transistores funcionan en tres zonas, conocidas como corte, saturación y activa. La
zona activa es común en los sistemas de amplificación de señal. Sin embargo para el caso de los
actuadores se requieren en la mayoría de los casos ser activados y desactivados, o tener un
comportamiento ON, OFF. En otras palabras los transistores se usan en corte y saturación. Bajo
estos términos los transistores trabajan como interruptores de corriente, en las zonas de corte y
saturación. La vista de los transistores en ISIS es la siguiente:



Figura 16-1

263
Los transistores MOSFET, controlan la corriente que circula entre la fuente S, y el drenador D,
por medio de la diferencia de potencial eléctrico sobre la compuerta G. Los transistores BJT,
controlan la corriente que fluye entre el colector C, y el emisor E, por medio de la corriente que
circula por la base B.

El uso de cargas inductivas como motores, relevadores o solenoides, tienen la propiedad de
almacenar energía en forma de campo electromagnético, esto implica que cuando las cargas
inductivas son des energizadas, estás devuelven su energía por medio de una corriente inversa,
este fenómeno puede causar daños en los sistemas controlados con transistores, para evitar este
problema se debe usar un diodo rectificador polarizado en inverso para descargar las cargas
inductivas, este arreglo se muestra en el siguiente circuito:


Circuito 16-1
16.1.1 Relevadores
Los relevadores son dispositivos electromecánicos, que tienen un electroimán con el cual se
cierra o se abre un circuito eléctrico por medio de uno o varios contactos. Los relevadores son
ideales para aislar los circuitos de potencia con la etapa de control electrónico. De igual manera
son dispositivos de bajo rendimiento, dado que no pueden hacer cambios de estádo veloces, y con
el uso constante los contactos eléctricos se deterioran con el tiempo. La apariencia física y la vista
en ISIS, de estos dispositivos es la siguiente:


Figura 16-2


Por último el uso de estos dispositivos se debe hacer de forma final como se puede observar en el
siguiente circuito, que tiene una carga de 120V AC:
264

Circuito 16-2
16.1.2 Motores DC
Los motores DC, son de naturaleza inductiva y por consiguiente merecen el mismo tratamiento
de los relevadores. En el siguiente ejemplo se puede apreciar como un microcontrolador hace
cambios de velocidad en un motor DC, para este fin se implementa el módulo PWM de un
microcontrolador 16F628A, con oscilador de 4MHz. El código fuente en lenguaje C es el
siguiente:

void main( void )
{
unsigned short CU=0;
OPTION_REG = 0; //Se activan las resistencias PULL-UP.
PWM1_Init( 500 ); //Se inicia el módulo PWM a 500Hz.
PWM1_Set_Duty(CU);
PWM1_Start();
while(1) //Bucle infinito.
{
//Bucle de incremento del PWM cuando se pulsa RB1.
while( Button( &PORTB, 1, 10, 0 ) )
{
CU++; if( CU==0 )CU=255;
PWM1_Set_Duty(CU);
delay_ms(10);
}
//Bucle de decremento del PWM cuando se pulsa RB0.
while( Button( &PORTB, 0, 10, 0 ) )
{
CU--; if( CU==255 )CU=0;
PWM1_Set_Duty(CU);
delay_ms(10);
}
}
}
265
Para poder hacer la simulación de este ejemplo se debe implementar en ISIS los dispositivos:
16F628A, BUTTON, RES, 2N3904, 1N4001, y MOTOR ACTIVE. Configurados como se puede
apreciar en el siguiente circuito:

Circuito 16-3

En función de la intensidad de corriente se puede cambiar el transistor para optimizar el
funcionamiento de los sistemas. Un transistor como el 2N3904 puede controlar una corriente de
200m Amperios, y un TIP31 puede controlar una corriente de 3 Amperios, en función de la
referencia y sus características, se puede asumir un máximo de corriente.
16.1.3 Puente H
Los puentes H son arreglos transistorizados que permiten invertir la polaridad sobre una carga
eléctrica, con el suministro de solo una fuente sencilla. Los puentes H, se pueden implementar en
discreto con transistores o usar módulos integrados que se pueden conseguir comercialmente.
Estos arreglos cuentan con dos señales de control que permiten activar la polaridad positiva, y
negativa. Un diseño discreto de este arreglo se puede apreciar en el siguiente circuito:


Circuito 16-4
266
Para activar una polaridad positiva, se activa el control 1, y se apaga el control 2, para activar la
polaridad negativa se hace la configuración contraria con los pines de control. Se debe tener
presente que nunca se podrán activar los dos puntos de control al mismo tiempo, está acción
genera un corto circuito entre Vcc, y referencia, produciendo daños considerables sobre los
transistores.

El siguiente ejemplo muestra el control de giro sobre un motor DC, por medio de un
microcontrolador 16F628A:

void main( void )
{

OPTION_REG = 0; //Se activan las resistencias PULL-UP.
//Configuración de salida y entrada del puerto B.
PORTB = 0;
TRISB = 0x0F;
PORTB = 0;

while(1) //Bucle infinito.
{

//Bucle para detectar el botón de giro a la izquierda.
while( Button( &PORTB, 0, 10, 0 ) )
{
PORTB = 0; //Se apagan los pines de control.
PORTB.F7 = 1; //Se activa el pin de control 1.
}

//Bucle para detectar el botón de detener.
while( Button( &PORTB, 1, 10, 0 ) )
{
PORTB = 0; //Se apagan los pines de control.
}

//Bucle para detectar el botón de giro a la derecha.
while( Button( &PORTB, 2, 10, 0 ) )
{
PORTB = 0; //Se apagan los pines de control.
PORTB.F6 = 1; //Se activa el pin de control 2.
}

}

}

267
La simulación en ISIS, se realiza por medio del siguiente circuito electrónico:


Circuito 16-5
16.1.4 Motores Paso
Los motores paso, son dispositivos electromecánicos que permiten hacer giros fraccionados por
grados, los motores paso son de dos naturalezas; unipolares y bipolares. Un motor unipolar
cuenta con cuatro embobinados, que se energizan uno o dos a la vez, y siempre con la misma
polaridad. Los motores paso bipolares cuentan con solo dos bobinas, las cuales se polarizan al
mismo tiempo, pero alternando la polaridad. Este efecto genera una secuencia en las bobinas y su
polaridad. Manipulando la velocidad de la secuencia se controla la velocidad de giro del motor, y
con el orden de la secuencia se controla la dirección de giro del motor.

En las siguientes gráficas se puede apreciar la apariencia física de estos motores y su vista en
ISIS:


Figura 16-3

Los motores unipolares, cuentan generalmente con 5 o 6 terminales, de las cuales 4 corresponden
a los embobinados, y 1 o 2 son terminales comunes. Las bobinas de un motor unipolar se pueden
apreciar en el siguiente circuito:

268

Circuito 16-6

La secuencia de activación para los motores unipolares puede ser de dos formas; con una sola
entrada activa, o dos entradas activas simultáneamente. Cuando se activan dos entradas
simultáneamente el torque del motor es mayor pero al mismo tiempo la corriente es mayor. La
siguiente tabla muestra la forma de activar las secuencias en los dos casos:

Secuencias para motores paso unipolares
PASO
Secuencia con una activación Secuencia con dos activaciones
L A L B L C L D L A L B L C L D
1 ON OFF OFF OFF ON ON OFF OFF
2 OFF ON OFF OFF OFF ON ON OFF
3 OFF OFF ON OFF OFF OFF ON ON
4 OFF OFF OFF ON ON OFF OFF ON

Tabla 16-1

El siguiente ejemplo muestra como realizar el control de un motor unipolar con un PIC 16F628A,
que cuenta con una frecuencia de reloj de 4MHz, para este fin la secuencia del motor se guarda
en un arreglo. El código en lenguaje C es el siguiente:

//Constantes con la secuencia de pasos.
const unsigned short PASOS[4] =
{
0b00000001,
0b00000010,
0b00000100,
0b00001000
};

void main( void )
{
//Declaración de variables.
unsigned short PASO=0;
//Inicio del puerto
TRISB = 0xF0;
PORTB = 0;
OPTION_REG = 0; //Activación de las resistencias PULL-UP.
269
while(1)//Bucle infinito.
{
//Bucle while para hacer girar en un sentido
//por medio del pin RB6
while( Button( &PORTB, 6, 100, 0) )
{
PORTB = PASOS[PASO];
PASO++;
if( PASO==4 )PASO=0;
}
//Bucle while para hacer girar en un sentido contrario
//por medio del pin RB7
while( Button( &PORTB, 7, 100, 0) )
{
PORTB = PASOS[PASO];
PASO--;
if( PASO==255 )PASO=3;
}
}
}

Para simular este ejercicio se implementa en ISIS, el siguiente circuito electrónico, con el
MOTOR-STEPPER, BUTTON, y el driver ULN2003A:


Circuito 16-7

Para realizar el ejercicio con la secuencia de doble activación simplemente se cambia la
secuencia en el arreglo del mismo programa y se usa la misma simulación. El nuevo arreglo es el
siguiente:

//Constantes con la secuencia de pasos.
const unsigned short PASOS[4] =
{
0b00000011,
0b00000110,
270
0b00001100,
0b00001001
};

Para los motores bipolares se implementa un puente H doble, de tal forma que se pueda hacer una
secuencia con doble polaridad. La distribución eléctrica de este tipo de motores es la siguiente:



Circuito 16-8

Para el control de este tipo de motores se requiere de la siguiente secuencia de polaridad:

PASO
Secuencia para Bipolar
L A L B L C L D
1 +V -V +V -V
2 +V -V -V +V
3 -V +V -V +V
4 -V +V +V -V

Tabla 16-2

Para realizar el ejercicio con este motor se implementa el mismo programa del motor unipolar y
se altera el arreglo de la secuencia, en este nuevo arreglo se asume un 1 lógico para los +V, de la
tabla y un 0 lógico para los –V. De esta forma se obtiene el siguiente arreglo:

//Constantes con la secuencia de pasos.
const unsigned short PASOS[4] =
{
0b00000101,
0b00001001,
0b00001010,
0b00000110
};


Para la simulación del sistema se implementa en ISIS un circuito similar, pero con un motor
bipolar y un driver para puente H integrado. Este integrado es el L293D, que es un circuito
integrado que tiene en su interior dos puentes H simultáneamente, característica que lo hace ideal
para este tipo de motores. La simulación en ISIS, implementa el 16F628A, BUTTON, MOTOR-
BISTEPPER, y el L293D. El circuito para simular es el siguiente:
271

Circuito 16-9
16.1.5 Servomotores
Los servomotores son sistemas integrados que tienen un control de posición angular, y un sistema
mecánico de piñones para ofrecer mayor fuerza pero menor velocidad. Un servomotor tiene
incorporado un control de posición angular, que puede ser gobernado por medio de una señal
PWM. Las aplicaciones de los servomotores están desde la robótica hasta los modelos a escala
como aviones, helicópteros y carros. Los servomotores cuentan con un terminal de tres pines para
la alimentación, la referencia y la señal de control PWM. La apariencia física y la vista en ISIS de
estos dispositivos es la siguiente:


Figura 16-4

La característica de la señal de control es su periodo de PWM de 16m a 18m segundos y el
periodo útil puede variar de 1 a 2 milisegundos, esto hace referencia respectivamente a 0 y 180
grados en la posición angular. La siguiente gráfica ilustra está situación:


Figura 16-5
272
A continuación se muestra un ejemplo en lenguaje C, para controlar la posición de un
servomotor:

//Función para generar el PWM de
//baja frecuencia, ajustado para
//cristal de 20MHz.
void Pwm_Sevo( float ang )
{
unsigned int n, max;
max = 1.61*ang;
PORTB.F0 = 1;
delay_ms(1);
for( n=0; n<max; n++ )
delay_us(1);
PORTB.F0=0;
delay_ms(15);
}

void main( void )
{
//Angulo inicial.
float ANGULO=90;
//Inicio del puerto
TRISB.F0 = 0;
//Se activan las resistencias PULL-UP.
OPTION_REG=0;
while(1)//Bucle infinito.
{
Pwm_Sevo(ANGULO); //Se genera un pulso.
//Si se pulsa el botón de RB6
//se decrementan 10 grados.
if( PORTB.F6==0 )
{
ANGULO-=10.0;
if( ANGULO<0.0 )ANGULO=0.0;
while( Button(&PORTB,6,5,0) );
}
//Si se pulsa el botón de RB7
//se incrementan 10 grados.
if( PORTB.F7==0 )
{
ANGULO+=10.0;
if( ANGULO>180.0 )ANGULO=180.0;
while( Button(&PORTB,7,5,0) );
}
}
}

273
Para simular este ejercicio, se implementa en ISIS, el PIC 16F628A, con frecuencia de reloj de
20MHz, MOTOR-PWMSERVO, y el BUTTON. El circuito correspondiente es el siguiente:


Circuito 16-10
16.2 Actuadores AC
Para el uso de actuadores AC, se pueden usar esencialmente dos técnicas, la primera es la
implementación de relevadores con la limitación de que solo pueden hacerse acciones de
conmutación ON, OFF. Sin embargo se pueden implementar dispositivos en estádo solidó como
los TRIAC, estos dispositivos permiten manipular cargas AC, y pueden regular su potencia
haciendo recorte de fase de la señal AC, que generalmente es de 60Hz. Los TRIAC son de fácil
implementación y su uso puede hacerse aislando las corrientes de potencia con optó acopladores
como los MOC3011, 3010, 3021, etc. Los TRIAC se pueden adquirir en capacidad de la máxima
corriente que pueden soportar. Un TRIAC cuenta con tres terminales que son: A1, A2, y gate, los
TRIAC cortocircuitan las terminales A1, y A2, cuando una corriente circula entre el gate y A1. y
solo se desconecta cuando la corriente de A2 se hace 0, esto es cosible cuando la señal AC, hace
su cruce por cero.

La apariencia física y la vista en ISIS de estos dispositivos es la siguiente:


Figura 16-6

Como se puede observar en la gráfica los MOC, son dispositivos que internamente tienen un
TRIAC, pero la corriente que pueden soportar es relativamente pequeña, y a su vez lo
suficientemente grande para disparar otro TRIAC de mayor potencia. También se puede observar
274
que la compuerta gate de los MOC, se activa por medio de la luz emitida por un LED, este
también está incorporado en el mismo encapsulado. En conclusión un TRIAC de gran potencia
puede ser activado por medio de un circuito digital con solo la activación de un LED.
Para entender este arreglo de forma clara, se puede observar el siguiente circuito:


Circuito 16-11

Para lograr la regulación de la potencia sobre una carga AC, se hace necesario hacer un recorte
sobre la fase, de la señal de poder seno. La red eléctrica comercial tiene 120V AC, y una
frecuencia de 60Hz. Lo que indica que un siclo de la red eléctrica dura 1/60, es decir 16,666m
segundos. El trabajo de recorte de la fase se debe hacer sobre cada medio siclo es decir en
8,333m segundos. Lo anterior indica que el recorte de fase se hace entre 0 y 180 grados, y se
repite igual entre 180 y 360 grados. Para comprender de forma clara este concepto se puede
observar la siguiente gráfica:



Figura 16-7

275
Para poder realizar el recorte de fase, es indispensable detectar el cruce por cero de la señal seno,
para sincronizarse con ella. Para detectar el cruce por cero existen diversas estrategias, sin
embargo en el próximo ejemplo se mostrará una que implica la implementación de opto
acopladores. El uso de opto acopladores es ideal para hacer un aislamiento eléctrico de la etapa
de potencia con la etapa de electrónica de control. El siguiente programa usa un PIC 16F628A,
con frecuencia de reloj de 20MHz:

//Variables globales.
unsigned short PWM=41, CONT=0;

//Función de interrupciones.
void interrupt()
{
//Interrupción externa por RB0.
if( INTCON.F1 )
{
//Se apaga el pin de disparo.
PORTB.F7=0;
//Se reinicia el contador.
CONT = 0;
//Se apaga la bandera de la interrupción
//externa.
INTCON.F1=0;
}
//Interrupción por Timer 0 a 102.4u Seg.
if( INTCON.F2 )
{
//Se incrementa el contador.
CONT ++;
//Se compara el contador con el punto,
//PWM, y se apaga el disparo y es mayor el contador.
if( CONT > PWM )PORTB.F7=1;
//Se apaga la bandera de la interrupción por
//Timer 0.
INTCON.F2 =0;
}
}

void main( void )
{
//Configuración de puertos.
TRISB = 0b01111111;
PORTB = 0;
//Activación del Timer0, y flanco de
//interrupción externa.
OPTION_REG = 0b01000000;
INTCON = 0b10110000;
while( 1 )// Bucle infinito.
{
276
//Pulsador de menor potencia.
while( Button( &PORTB, 1, 10, 0 ) )
{
//Decremento del recorte de fase.
PWM ++; if( PWM > 84 )PWM=84;
delay_ms(10); //Retardo de cambio.
}

//Pulsador de mayor frecuencia.
while( Button( &PORTB, 2, 10, 0 ) )
{
//Incremento del recorte de fase.
PWM --; if( PWM == 255 )PWM=0;
delay_ms(10); //Retardo de cambio.
}

}
}

Para realizar la simulación de este ejercicio se implementa en ISIS los dispositivos: 16F628A,
RES, BUTTON, MOC3023, Q4006L4, B80C1000, OPTOCOUPLER-NPN, VSINE, y el
OSCILLOSCOPE. El circuito electrónico es el siguiente:


Circuito 16-12

Después de correr la simulación se puede apreciar en el osciloscopio virtual, la siguiente vista:

277

Figura 16-8

El canal A representa la señal sobre la carga de potencia y se puede apreciar sobre está, el recorte
de fase. El canal B, muestra un pico cuando la señal seno presenta su cruce por cero, este pico es
usado por el microcontrolador para sincronizarse con la red por medio de la interrupción externa.
El canal C, muestra la señal de control que dispara el juego de TRIACs.
278

279
17 Anexos
17.1 Tabla ASCII


ASCII

Hex

Símbolo


0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F


NUL
SOH
STX
ETX
EOT
ENQ
ACK
BEL
BS
TAB
LF
VT
FF
(Enter)
SO
SI



ASCII

Hex

Símbolo


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31


10
11
12
13
14
15
16
17
18
19
1A
1B
1C
1D
1E
1F


DLE
DC1
DC2
DC3
DC4
NAK
SYN
ETB
CAN
EM
SUB
ESC
FS
GS
RS
US



ASCII

Hex

Símbolo


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47


20
21
22
23
24
25
26
27
28
29
2A
2B
2C
2D
2E
2F


(Espacio)
!
"
#
$
%
&
'
(
)
*
+
,
-
.
/



ASCII

Hex

Símbolo


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63


30
31
32
33
34
35
36
37
38
39
3A
3B
3C
3D
3E
3F


0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?



ASCII

Hex

Símbolo


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79


40
41
42
43
44
45
46
47
48
49
4A
4B
4C
4D
4E
4F


@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O



ASCII

Hex

Símbolo


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95


50
51
52
53
54
55
56
57
58
59
5A
5B
5C
5D
5E
5F


P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_



ASCII

Hex

Símbolo


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111


60
61
62
63
64
65
66
67
68
69
6A
6B
6C
6D
6E
6F


`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o



ASCII

Hex

Símbolo


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127


70
71
72
73
74
75
76
77
78
79
7A
7B
7C
7D
7E
7F


p
q
r
s
t
u
v
w
x
y
z
{
|
}
~





280
281
Bibliografía

Behrouz A. Forouzan, Transmisión de datos y redes de comunicaciones, Segunda Edición, Mc
Graw Hill, ISBN 84-481-3390-0.

Boylestád Robert, Electronica: Teoria de Circuitos, Sexta Edicion, PRENTICE HALL, ISBN
968-880-805-9.

Coughlin Robert, Amplificadores operacionales y circuitos integrados lineales, Quinta Edicion,
PRENTICE HALL, ISBN 970-17-0267-0.

Dorf Richard, CIRCUITOS ELECTRICOS Introducción al Analisis y Diseño, Tercera Edicion,
Alfaomega, ISBN 970-15-0517-4.

Galeano Gustavo, Programación de Sistemas Embebidos en C, Primera Edicion, Alfaomega,
ISBN 978-958-682-770-6.

Ogata Katsuhiko, Ingenieria de Control Moderna, Tercera Edicion, PRENTICE HALL, ISBN
970-17-0048-1.

M. Morris Mano, Logica Digital y Diseño de Computadores, Primera Edicion, PRENTICE
HALL, ISBN 968-880-016-3.

Oppenheim Alan, Señales y Sistemas, Segunda Edicion, PEARSON PRENTICE HALL, ISBN
970-17-0116-X.

Proakis John, Tratamiento digital de señales, Cuarta Edicion, PEARSON PRENTICE HALL,
ISBN 978-84-8322-347-5.
282
283
Índice
1
16F628A · 58, 162, 166, 176, 264, 265, 266, 268, 270, 273,
275, 276
16F84A · 39, 46, 47, 49, 52, 58
16F877A · 65, 71, 75, 77, 79, 89, 94, 110, 113, 118, 120,
121, 123, 124, 126, 129, 130, 133, 137, 140, 146, 170,
174, 189, 241
18F2550 · 88, 95, 96
18F452 · 71, 150, 159, 181, 202, 229, 231
18F4550 · 88, 95
18F4585 · 234, 238
2
24LC00 · 89
7
74LS04 · 176
A
ACK · 88, 141, 143, 244, 245, 246, 247, 248, 249, 250,
251, 256, 257, 259, 260, 279
acknowledge · 88
Actuadores AC · 273
Actuadores DC · 262
Actuadores y potencia · 262
AD · 109, 128, 129, 132, 136, 139, 202
ADC · 17, 109, 124, 128, 129, 130, 133, 136, 137, 140,
173, 203, 206, 216, 218, 223, 230, 236
ADC_Read · 109
ADCON1 · 90, 129, 136
AND · 25, 26, 27, 30, 177
Anexos · 279
ASCII · 20, 21, 50, 54, 59, 60, 61, 85, 86, 87, 94, 95, 126,
149, 175, 244, 279
ASK · 161, 174, 175
B
Bibliografía · 281
bilineal · 212, 213, 219, 221, 239
bit · 20, 21
BJT · 262, 263
Button · 74, 76, 91, 173, 264, 266, 269, 272, 276
BUTTON · 35, 37, 75, 79, 89, 118, 126, 166, 174, 231,
265, 269, 270, 273, 276
ByteToStr · 59, 80, 189
C
CAP · 113, 174, 231
Características básicas del ISIS para simular · 35
Ch
char · 20, 21
C
COM · 47, 167
COMPIM · 159
Comunicación con dispositivos · 147
Comunicación con memorias SD · 177
Comunicación RS 485 · 172
Comunicaciones seriales · 88
Condicionales e iteraciones en lenguaje C · 30
Control de display de 7 segmentos · 45
Control de displays 7 segmentos dinámicos · 47
Control de errores · 252
Control digital PID · 238
Conversión AD y DA · 109
Conversión AD, o ADC · 109
Conversión DA con arreglo R-2R · 114
Conversión DA con PWM · 110
Conversión DA o DAC · 110
Convolución · 192
coordenadas · 55, 72, 73, 148, 149, 151, 153, 155, 158
CRC · 252, 253, 254, 255, 256, 257, 258, 260
Creación de caracteres propios · 59
Creación de condiciones lógicas en lenguaje C · 30
Creación de un programa en lenguaje C · 29
Creación del primer programa en MikroC PRO · 38
cruce por cero · 273, 275, 277
D
DA · 110, 114, 203
DB9 · 92
Declaración de variables en lenguaje C · 20, 244, 247, 252
Definit.h · 99, 100
delay_ms · 46, 47, 48, 73, 91, 99, 130, 133, 137, 140, 142,
144, 145, 164, 165, 173, 176, 184, 185, 189, 264, 272,
276
delay_us · 46, 113, 116, 142, 163, 164, 272
DIN · 84
Dip-Switch · 76, 77, 78
display · 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 64, 65, 70,
73, 79, 83, 87, 154, 158, 159
Display de 7 segmentos · 44
Display LCD de caracteres · 50
Display LCD gráficos · 64
284
double · 20, 21
DS1307 · 183, 184, 189
DTF · 228
E
EEPROM · 117, 118
EEPROM_Read · 117
EEPROM_Write · 117
Ejemplo de diseño filtro rechaza banda · 222
Ejemplo de diseño oscilador doble cuadrado · 225
Ejemplo de diseño para filtro multi banda · 210
Ejemplo de diseño para filtro pasa altas · 207, 217
Ejemplo de diseño para filtro pasa bajas · 204, 215
Ejemplo de diseño para filtro pasa banda · 208, 220
Ejemplo de diseño para filtro rechaza banda · 209
Ejemplo de diseño para oscilador de acople en cuadratura ·
226
Ejemplos de diseño para filtros FIR · 202
El autor · 11
El ciclo iterativo for · 33
El ciclo iterativo while y do while · 32
El compilador MikroC PRO · 18
El microcontrolador PICMicro · 17
El simulador ISIS de Proteus · 34
ENQ · 246
EOT · 244, 245, 246, 247, 249, 250, 251, 257, 260, 279
F
FFT · 232, 233, 234, 235, 237
Filtro Multi Band · 196
Filtro Pasa Altas IIR · 216
Filtro Pasa Bajas IIR · 213
Filtro Pasa Banda · 194
Filtro Pasa Banda IIR · 219
Filtro Rechaza Banda · 195
Filtro Rechaza Banda IIR · 221
Filtros FIR · 192
Filtros IIR · 212
Filtros Pasa Altas · 194
FIR · 191, 192, 193, 196, 197, 202, 212, 213, 215, 226, 229
FLASH · 117, 118, 119, 120
FLASH_Read · 119, 120
FLASH_Write · 118, 120
float · 20, 21
FloatToStr · 58, 146, 154, 157, 158, 237
Formatos numéricos usados en el lenguaje C · 23
FSK · 161
función de transferencia · 131, 134, 135, 136, 137, 191,
193, 194, 195, 197, 205, 212, 214, 219, 221, 239
Funciones de transferencia · 191
Funciones en lenguaje C · 27
Funciones para imprimir cadenas de texto · 55
Funciones para imprimir caracteres · 54
Fundamentos de lenguaje C · 20, 243
G
Gibbs · 197, 198, 199, 200, 201, 202
GIE · 124, 125
GLCD · 65, 66, 67, 69, 71
GLCD Bitmap Editor · 66
Glcd_Box · 72, 73
Glcd_Circle · 72, 73
Glcd_Dot · 72, 73
Glcd_Fill · 70, 71, 73
Glcd_Init · 65, 70, 73
Glcd_Line · 72, 73
Glcd_Rectangle · 72
GP2D12 · 134, 137
GPS · 88, 147, 148, 149, 152, 160, 161
GROUND · 36
H
HID · 88, 98, 99, 100, 101, 103, 104, 105, 107, 108
HID Terminal · 100, 107
Hid_Disable · 99
Hid_Enable · 98, 99
Hid_Read · 99
Hid_Write · 99
I
I2C · 17, 88, 90, 184, 189
I²C · 88, 141, 183
I2C1_Init · 88, 90, 184
I2C1_Is_Idle · 88
I2C1_Rd · 88, 90, 184, 185, 186
I2C1_Repeated_Start · 88, 90, 184, 185, 186
I2C1_Start · 88, 90, 91, 183
I2C1_Stop · 89, 91, 184, 185, 186
I2C1_Wr · 89, 90, 91, 183, 184, 185, 186
I2CDEBUGGER · 89
IEEE 801.15.4 · 167
IIR · 191, 212, 215, 226, 229, 239
Impresión de valores numéricos · 57
infrarrojos · 174, 176
int · 20, 21
INTCON · 98, 124, 125, 126, 150, 158, 203, 204, 206, 216,
218, 219, 223, 224, 226, 227, 228, 230, 231, 236, 237,
275
INTE · 124, 125
Interrupciones · 124
INTF · 100, 103, 104, 124, 125
Introducción · 15
IntToStr · 57, 58, 130, 133, 137, 153, 156, 157, 159, 237
IRLINK · 175, 176
ISIS · 1, 5, 34, 35, 37, 40, 47, 49, 50, 52, 57, 61, 63, 64, 65,
71, 73, 75, 76, 77, 79, 84, 89, 91, 94, 96, 110, 116, 118,
120, 123, 126, 128, 130, 131, 133, 134, 137, 138, 140,
141, 146, 147, 159, 160, 161, 167, 175, 176, 181, 183,
189, 202, 203, 231, 238, 262, 263, 265, 267, 269, 270,
271, 273, 276
K
Keypad_Init · 79
Keypad_Key_Click · 79
Keypad_Key_Press · 79, 80, 82, 83
285
L
L293D · 241, 270
La sentencia condicional if e if else · 31
La sentencia switch case · 32
LCD · 44, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 61, 62, 63,
64, 65, 71, 72, 73, 80, 82, 83, 86, 87, 129, 132, 136, 137,
139, 140, 146, 154, 158, 159, 169, 170
LCD Custom character · 60
Lcd_Chr · 54, 55, 59, 83, 87
Lcd_Chr_Cp · 54, 55, 59
Lcd_Cmd · 64, 80, 83, 87, 129, 132, 137, 140, 146, 158
Lcd_Init · 51, 52, 54, 55, 56, 57, 58, 61, 63, 80, 83, 87, 129,
132, 136, 140, 146, 158, 169
Lcd_Out · 55, 56, 57, 58, 80, 83, 87, 129, 130, 132, 133,
137, 140, 146, 158, 159, 170
Lcd_Out_Cp · 55, 57
LDR · 138, 139, 140
LED · 35, 44, 75, 77, 89, 96, 110, 118, 126, 166, 189, 231,
274
Library Manager · 51
LM35 · 128, 130, 131
long · 20, 21
LongToStr · 59, 140, 180, 181
LongWordToStr · 59
M
main · 29, 30, 39, 46, 48, 49, 52, 54, 55, 56, 57, 58, 61, 63,
65, 70, 72, 75, 76, 77, 80, 83, 84, 87, 90, 94, 98, 109,
112, 115, 117, 119, 122, 124, 126, 129, 132, 136, 139,
145, 150, 158, 165, 166, 169, 172, 175, 178, 188, 204,
206, 216, 219, 223, 226, 228, 231, 237, 264, 266, 268,
272, 275
matriz de rotación · 224, 225, 227
MAX232 · 93
MAX485 · 172, 173, 174
MAX487 · 174
MCLR · 40
Memoria EEPROM · 117
Memoria FLASH · 118
Memorias EEPROM y FLASH · 117
MikroC PRO · 1, 5, 13, 15, 18, 20, 38, 45, 50, 51, 59, 62,
65, 66, 71, 72, 74, 79, 80, 84, 88, 89, 93, 96, 98, 99, 109,
112, 117, 124, 177, 190
Mmc_Init · 178
Mmc_Read_Sector · 178, 181
Mmc_Write_Sector · 178, 181
MOC3011 · 273
MODEM · 167
Módulo de acople con un ordenador personal · 168
Modulo serial I²C · 88
Módulo USART · 91
Módulo USB · 95, 108
Módulos GPS · 147
Módulos inalámbricos bidireccionales · 167
Módulos inalámbricos infrarrojos · 174
Módulos inalámbricos unidireccionales · 161
Módulos Timer · 121
MOSFET · 262, 263
MOTOR · 265, 269, 270, 273
motor unipolar · 267, 268, 270
Motores AC · 262
motores bipolares · 270
Motores DC · 264
Motores Paso · 267
MPX4115 · 131, 133
Muestreo · 190
N
NAK · 244, 245, 246, 247, 248, 249, 250, 257, 259, 260,
261, 279
NOT · 25, 26, 30
Nyquist · 190
O
Operadores en lenguaje C · 23
OPTION_REG · 122, 126, 165, 173, 264, 266, 268, 272,
275
opto acopladores · 275
OR · 25, 26, 27, 30
Osciladores digitales · 224
OSCILLOSCOPE · 113, 123, 126, 202, 276
P
PICMicro · 17, 18, 34, 38, 109, 190
PID · 99, 103, 238, 239, 241
PIE1 · 98, 124, 125, 126, 150, 158
PIE2 · 98, 124, 125, 126, 150, 158
PIR1 · 98, 124, 125, 126, 150, 157, 158
PIR2 · 98, 124, 126, 150, 158
PLL · 98, 202
PORTA · 48, 49, 90, 91
PORTB · 30, 39, 40, 46, 48, 75, 76, 77, 90, 91, 98, 99, 109,
115, 116, 117, 119, 122, 125, 126, 158, 165, 166, 172,
173, 203, 204, 206, 216, 219, 223, 224, 226, 228, 231,
237, 264, 266, 268, 269, 272, 275, 276
POWER · 36, 100
Prologo · 13
PS2 · 74, 84, 86, 122
Ps2_Config · 84
Ps2_Key_Read · 84, 87
Puente H · 265
PULL-UP · 165, 173, 174, 264, 266, 268, 272
PWM · 17, 109, 110, 111, 112, 113, 114, 115, 116, 162,
173, 175, 176, 264, 271, 272, 275, 276
PWM1_Init · 112, 173, 175, 264
PWM1_Set_Duty · 112, 113, 173, 176, 264
PWM1_Start · 112, 173, 175, 264
PWM1_Stop · 112
R
R-2R · 114, 203
RAM · 18, 59, 63, 103, 177, 234
RCIE · 124, 125
RCIF · 124, 125
recorte de fase · 273, 274, 275, 276, 277
Relevadores · 263
286
reloj · 84, 88, 91, 98, 121, 141, 142, 143, 145, 159, 162,
178, 183, 188, 189, 202, 241, 268, 273, 275
Relojes en tiempo real · 183
RES · 35, 47, 49, 75, 77, 79, 89, 96, 110, 113, 118, 126,
130, 137, 140, 146, 166, 174, 176, 189, 202, 231, 265,
276
RS 485 · 172
RS232 · 34, 92, 93
S
SD · 88, 105, 177, 178, 179
Sensor de temperatura LM35 · 128
Sensores · 128
Sensores de distancia · 134
Sensores de humedad y temperatura · 141
Sensores de presión · 131
Sensores LDR · 138
Servomotores · 271
short · 20, 21
ShortToStr · 59
SHT71 · 146
SHT7x · 141, 145, 146
SPI · 88, 177, 178, 181
SPI1_Init_Advanced · 178, 181
switch case · 30, 32, 82, 83
T
teclado · 18, 78, 79, 80, 81, 82, 83, 84, 86, 87
Teclados y sistemas de entrada de datos · 74
Timer · 17, 121, 122, 125, 126, 275
Timer 0 · 121, 122
TMR0IE · 124, 125
TMR0IF · 124, 125
Transformación bilineal · 213
Transformada discreta de Fourier DFT · 228
Transformada rápida de Fourier FFT · 232
Transmisión · 243
Tratamiento digital de señales · 190
TRIAC · 273
TRISA · 49
TRISB · 30, 39, 40, 46, 49, 75, 76, 77, 90, 98, 109, 115,
117, 119, 122, 126, 158, 165, 166, 172, 204, 206, 216,
219, 223, 226, 228, 231, 237, 266, 268, 272, 275
TRISE · 90
TTL · 172
typedef struct · 22
U
UART1_Data_Ready · 93, 95, 119, 170, 173, 179, 180, 181
UART1_Init · 93, 94, 119, 126, 150, 158, 169, 173, 175,
178, 189, 237
UART1_Read · 93, 95, 119, 126, 150, 157, 170, 173, 179,
180, 181
UART1_Read_Text · 93
UART1_Tx_Idle · 93
UART1_Write · 93, 94, 95, 119, 120, 126, 170, 173, 176,
178, 179, 180, 181, 189, 237
UART1_Write_Text · 94, 95, 119, 120, 126, 170, 176, 178,
179, 180, 181, 189, 237
unsigned char · 21
unsigned int · 21
unsigned long · 21
unsigned short · 21
USART · 17, 18, 58, 88, 91, 93, 94, 124, 149, 169, 172,
173, 175, 189
USB · 18, 34, 88, 95, 96, 98, 99, 100, 101, 102, 103, 105,
107
USBCONN · 96
USBdsc.c · 99, 107
Uso anidado de ciclos iterativos · 33
Uso de Dip-Switch · 76
Uso de pulsadores · 74
Uso de teclados matriciales · 78
Uso de teclados PS2 o Din · 84
V
variable compleja · 191, 212
VARs.h · 99, 104
Ventanas Blackman · 201
Ventanas fijas · 196
Ventanas Hamming · 198
Ventanas Hanning · 199
Ventanas Rectangulares · 197
VIP · 99
VIRTUAL TERMINAL · 94, 120, 126, 170, 176, 181, 189,
238
Visualización de datos · 44
VSINE · 276
VUSB · 96
W
WordToStr · 59
www.digi.com · 167
www.labcenter.com · 34
www.mikroe.com · 18
www.virtual-serial-port.com · 147
X
XBee · 88, 167, 169
XBee PRO · 167
X-CTU · 167, 169
Z
Zigbee · 169


287

288

289

2

Dedicado a mí esposa: Sandra, mi hijo Miguel, mis padres Jorge y Bibiana

3

4

Diseño y simulación de sistemas microcontrolados en lenguaje C
Programación con el compilador MikroC PRO y simulación en Proteus ISIS

Juan Ricardo Clavijo Mendoza

5

Juan Ricardo Clavijo Mendoza Corrección de estilo Ing. Sandra Milena Bernate Bautista Dr.Diseño y simulación de sistemas microcontrolados en lenguaje C. sin el consentimiento escrito del autor. Se prohíbe la reproducción total o parcial de este libro sea cual fuere el medio. electrónico o mecánico. Duilio Cruz Becerra Hecho e impreso en Colombia ISBN 978-958-44-8619-6 Primera edición mayo de 2011 6 . Autor Ing.

........ 50 5..........................................................................3 Módulo USB ........................................6.......... 78 6.................................6................... 20 2........................................................... 38 5 Visualización de datos .....1.................................3 Uso de teclados matriciales ............................................................................................................................. 23 2.............................2 Control de displays 7 segmentos dinámicos........................................................................................... 121 7 ............... 35 4 Creación del primer programa en MikroC PRO ................. 17 1......1 Conversión DA con PWM ................ 29 2.......3 Display LCD gráficos ................................................................................1.......................................................... 114 9 Memorias EEPROM y FLASH ............... 117 9....................4 Creación de caracteres propios .......................6...................................................................................................................................................................................3 La sentencia switch case...................4 El ciclo iterativo while y do while..............1 El compilador MikroC PRO ............................... 23 2...... 74 6........................................1 Declaración de variables en lenguaje C ........... o ADC ........................................................................................................1 Características básicas del ISIS para simular ....................................... 91 7................................................................................................ 117 9..3 Impresión de valores numéricos .................................................................................................1 Display de 7 segmentos .........2 Display LCD de caracteres ......2..1 Uso de pulsadores .......................................................................................................................................................................................................................................................1 Conversión AD....................................... 32 2..................... 44 5..................................................................1 Control de display de 7 segmentos ..........................................................................................................6..............5 Creación de un programa en lenguaje C .................................. 95 8 Conversión AD y DA ................................................................ 54 5..... 30 2.............................................. 20 2............................................2 Uso de Dip-Switch..........................................5 El ciclo iterativo for............Contenido 1 El Compilador MikroC y los PICMicro .................2.....................6............... 118 10 Módulos Timer .....................................................................................................................................................................................................2............... 27 2........................................ 109 8......3 Operadores en lenguaje C ..................4 Uso de teclados PS2 o Din ........................................ 110 8........................... 31 2..... 59 5.........................6.......................................1 Memoria EEPROM .....2 Memoria FLASH ..................................................................................................................... 47 5.................................................................................................. 33 2...... 76 6................ 34 3.. 64 6 Teclados y sistemas de entrada de datos ........................................2 Módulo USART ...............4 Funciones en lenguaje C ...................................................................1 Modulo serial I²C .............................2................2..................... 55 5............................ 44 5.... 109 8..................2 Funciones para imprimir cadenas de texto ........................................................................... 33 3 El simulador ISIS de Proteus .....6 Condicionales e iteraciones en lenguaje C ..... 88 7.... 84 7 Comunicaciones seriales .............6 Uso anidado de ciclos iterativos ..................................... 32 2....................1 Funciones para imprimir caracteres ........................................................................................................................... 88 7...................................................................................................2...................................................... 45 5................................................................................... 110 8..............2 La sentencia condicional if e if else .................................................................................................................................................................................................................................. 74 6............................................................ 30 2.............................................................................. 57 5...2 Formatos numéricos usados en el lenguaje C ....................... 18 2 Fundamentos de lenguaje C ................................................2 Conversión DA o DAC ...1 Creación de condiciones lógicas en lenguaje C ..................2 Conversión DA con arreglo R-2R ..........................

.......6............................................................2 Funciones de transferencia ................................ 204 14........................... 201 14............................................8 Filtro Rechaza Banda IIR ....... 147 13.............4 Sensores LDR ........................................... 147 13..........................................................................................................................................10 Ventanas Blackman ........ 141 13 Comunicación con dispositivos ....4................................6 Filtro Pasa Banda IIR ..................................... 194 14......2 Filtros Pasa Altas ..................... 131 12...5.............4 Filtros FIR ....................4............................5......................................4............................. 217 14.... 216 14.................. 167 13................................ 210 14..............................................................6.................................1 Ejemplo de diseño para filtro pasa bajas ........................... 221 14.................4 Ejemplo de diseño para filtro rechaza banda ........... 124 Sensores ................................................. 226 8 ....................................................................... 172 13.........5.................4..................................... 193 14...........................................................1 Sensor de temperatura LM35 ....6............................3 Ejemplo de diseño para filtro pasa banda.........................2 Filtro Pasa Bajas IIR ............................................7 Relojes en tiempo real ...............4................ 190 14...........................3 Filtro Pasa Banda ........................5 Filtro Multi Band .............................................................. 220 14............................................. 192 14.................................4 Filtro Pasa Altas IIR ..........11 12 Interrupciones ......................................... 208 14......................................................... 196 14........6........................................................ 138 12........... 219 14...................................................................................................................................................6 Comunicación con memorias SD.................... 197 14..............2 Ejemplo de diseño para oscilador de acople en cuadratura ........1 Filtro Pasa bajas .....................................6 Ventanas fijas .............................................. 212 14.........................7 Osciladores digitales .................................................................................................................................................... 128 12..................................................... 195 14...............................4 Comunicación RS 485 ........................5 Módulos inalámbricos infrarrojos .......................................................................................................................................4......................................................... 213 14.3 Ejemplo de diseño para filtro pasa bajas ............. 222 14......... 215 14....6....................5...................................................................................................7....... 199 14.. 194 14..................................6............... 128 12..3 Sensores de distancia .......................................................................9 Ejemplo de diseño filtro rechaza banda ...................................4....6.................................. 161 13................................................................................................................................ 192 14................................................................................................................................ 202 14.........................3 Convolución ....7 Ventanas Rectangulares ............................4..1 Muestreo ............................ 183 14 Tratamiento digital de señales ........ 224 14....................................................................................................................... 190 14..................................4 Filtro Rechaza Banda ............................7 Ejemplo de diseño para filtro pasa banda............5. 207 14...............................................................2 Módulos inalámbricos unidireccionales..................................................................................1 Módulos GPS ...........................1 Transformación bilineal ............................................3 Módulos inalámbricos bidireccionales......... 225 14...1 Ejemplo de diseño oscilador doble cuadrado ...................................................................... 209 14......................... 196 14..6 Filtros IIR ...................6........................................................5 Ejemplo de diseño para filtro pasa altas ........................................5 Ejemplos de diseño para filtros FIR ........................................................... 177 13.................... 191 14.....5 Ejemplo de diseño para filtro multi banda .......................................................................... 134 12.........................................................................................................................4....... 198 14...............................................2 Ejemplo de diseño para filtro pasa altas ....................6..7.2 Sensores de presión .............................. 213 14...............................................................9 Ventanas Hanning ........................................4.5 Sensores de humedad y temperatura ........................................................................................................................................................ 174 13..................8 Ventanas Hamming .........................................................................

.............................................................................1.......................3 Control de errores CRC ....... 279 17........................ 228 14.....1.....................................................1 Control de flujo ...............................................................................................................................2 Transparencia de datos ................. 238 15 Transmisión de datos ........................................................................................................................................ 247 15..................... 262 16.................................................................................................. 279 Bibliografía ..............................................................................................................14......................... 264 16..1............................................ 232 14........................................................1 Tabla ASCII ..............................4 Motores Paso ...................................2 Motores DC .1................................................5 Servomotores .....................................................................................................................8.....................................................3 Puente H ......................................................................................................................................................1 Actuadores DC ...... 262 16................................. 271 16............. 273 17 Anexos ................................ 244 15........................................... 243 15.......... 252 16 Actuadores y potencia ................1............................................ 263 16........................................... 283 9 .......................................................................................................................9 Control digital PID .................1 Relevadores ...............................................................................................1 Transformada rápida de Fourier FFT ..........8 Transformada discreta de Fourier DFT ....................................................... 267 16....................................................................2 Actuadores AC ........................................................................................................................................... 281 Índice ............................... 265 16...

10

El autor
Juan Ricardo Clavijo Mendoza, Ingeniero Electrónico egresado de la universidad Manuela Beltrán de Bogotá D.C, Colombia. Autor de capitulo de libro de: “Ciencia, tecnología e innovación”, Tomo 1, publicado por la Universidad Católica de Colombia. Docente de la Universidad Católica de Colombia, en el programa de Tecnología en Electrónica y Telecomunicaciones, asesor temático en proyectos de investigación de la Universidad Católica de Colombia, y la Escuela de Telemática de la Policía Nacional de Colombia, desarrollador de proyectos tecnológicos en diversas empresas de Bogotá Colombia.

11

12

Prologo
El desarrollo de la tecnología hace parte vital de la evolución y el progreso de una nación, está razón motiva la creación de documentos que formen a los desarrolladores e investigadores en las diversas áreas de la ciencia. Este libro busca hacer un aporte en este propósito como un curso de fácil entendimiento en el diseño de sistemas microcontrolados, para este fin este texto se enfoca en la simulación de sistemas digitales en el paquete de software PROTEUS, y la programación de microcontroladores PIC con la herramienta de programación en lenguaje C MikroC PRO. Este libro es ideal para estudiantes de ingeniería, tecnología, o carreras técnicas en electrónica, sistemas, mecatrónica, biomédica, y todas aquellas afines con la electrónica. De igual manera es un aporte importante al desarrollo de proyectos de investigación y tecnología.

13

14

De la misma forma. y circuitos electrónicos análogos. Este libro se enfoca en la demostración de diseños simples que posteriormente pueden ser integrados para realizar diseños de mayor complejidad en función de la necesidad del desarrollador. se debe conocer las teorías de circuitos eléctricos. teorías básicas de algebra. de la familia microchip 12F. También es de utilidad tener conocimientos básicos de programación en lenguaje C y algoritmos.Introducción Este libro trata temáticas relacionadas con el diseño de sistemas con microcontroladores. Para entender este libro de forma clara el estudiante o desarrollador debe tener bases sólidas de electrónica digital como circuitos combinacionales y secuenciales. 16F. el compilador MikroC PRO. y el paquete de software para simulación Proteus. 15 . y algebra lineal así como la manipulación de números complejos en todas sus expresiones. y 18F. Usando como herramienta de diseño.

16 .

18F. En función de la necesidad del proyecto el desarrollador debe escoger la familia y la referencia que más se acerque a su necesidad. Figura 1-1 La información técnica.1 El Compilador MikroC y los PICMicro Un microcontrolador es un dispositivo electrónico encapsulado en un circuito de alto nivel de integración. 30F. Motorola. Philips. algunas de ellas son: 12F. y Microchip. Fácilmente se pueden apreciar diferencias que permiten crear aplicaciones diferentes entre estos dos ejemplos. Microchip suministra programadores especializados en diferentes escalas. Un microcontrolador como el 16F877 cuenta con 40 pines y módulos como: Timer. y 33F. crear editar y depurar un programa de máquina y programar eléctricamente el microcontrolador con un programador especifico para los PICMicro. entre otros. Básicamente implementar un desarrollo con un microcontrolador PIC consiste en identificar la problemática del desarrollo.microchip. en sus líneas de producción se encuentran los microcontroladores PICMicro. I2C. PICkit3. 24F. Microchip en particular es una empresa fabricante de dispositivos electrónicos. los cuales se pueden adquirir en diferentes familias. PWM. 16F. Los microcontroladores se pueden adquirir comercialmente de diferentes casas fabricantes como: Freescale. A pesar de que existan programadores comerciales un desarrollador puede construir o adquirir un programador didáctico de bajo costo. tal vez el más popular es el PICSTART Plus. la empresa Microchip cuenta con el portal WEB www.com en donde se puede descargar información y aplicativos de software que facilitan los desarrollos con sus microcontroladores. sin embargo existen otros como el PICkit2. por ejemplo el microcontrolador 12F675 es un PIC de 8 pines con módulos integrados básicos como: Timer y ADC. 17 . los hace ideales para aprender y estudiar su funcionamiento. la masiva comercialización y la increíble información publicada acerca de los microcontroladores PIC. USART. Intel. ADC. de estos últimos existe amplia información publicada en Internet.

proximidad. la única limitación es la dimensión del código de máquina que no puede exceder 2K bytes. En síntesis las posibilidades son infinitas.1 El compilador MikroC PRO La programación de microcontroladores se basa en un código de máquina que es conocido como código ensamblador. es posible crear ambientes de visualización con displays numéricos. el PICMicro cuenta con una memoria RAM. 1. El estudio de este entorno de desarrollo es posible debido a que el estudiante puede descargar una versión demo o estudiantil. luminosidad. este código ensamblador o también conocido como código asembler es minucioso. comercialmente existen varios compiladores de diferentes fabricantes y diferentes lenguajes de alto nivel. La creación de compiladores de alto nivel facilitó la edición y creación de programas en todo modo de programación lógica. La versión demo se puede descargar de la pagina: www. que tiene las mismas características de la versión completa. MikroC PRO es un paquete de software con una amplia variedad de ayudas y herramientas que facilita la creación de proyectos y aplicativos para los microcontroladores PICMicro. y tedioso de editar. impresora. botones.Figura 1-2 Un microcontrolador tiene una arquitectura básica que es similar a la de un computador de escritorio. este código contiene una a una las instrucciones del programa. lectura de memorias de almacenamiento masivo. monitor.mikroe. PIC Basic. por supuesto los microcontroladores no fueron la excepción. sin embargo es una capacidad suficiente al tratarse de un prime aprendizaje. entre otros. alfanuméricos y gráficos.com. el microcontrolador posee puertos de entrada y salida que son similares a los periféricos de entrada y salida del computador tales como puertos para el ratón. presión. entre otros. 18 . teclado y demás. El asembler crea códigos de programa extensos y de difícil comprensión. Es posible adquirir compiladores como el PICC. Estás características hacen que un microcontrolador sea ideal para crear aplicaciones a pequeña escala que tengan interfaz de usuario. adecuando teclados. que cumple las mismas funciones de la memoria RAM de un ordenador personal. El estudio de este libro se centra en el compilador MikroC PRO. humedad. 16F. De igual manera. sensores de diversas variables como: temperatura. Los puertos seriales como la USART y USB permiten crear comunicaciones seriales y comunicaciones inalámbricas con otros dispositivos. cuenta con un bloque de memoria OTP o Flash en la cual se guardan las instrucciones del programa está sección es similar al disco duro del computador. y 18F. CCS. que es un compilador en lenguaje C para microcontroladores PICMicro de la familia 12F.

y punto decimal.hex. optimiza las operaciones matemáticas y los procesos.En la siguiente figura se puede apreciar la apariencia visual del entorno de desarrollo. de tipo carácter. así como el uso de un conjunto de variables. entero. El compilador crea automáticamente el código ensamblador y a su vez un código similar consignado en un archivo con extensión *. Figura 1-3 El compilador de alto nivel en lenguaje C utiliza estructuras que facilitan la programación. 19 . este archivo es el resultado primordial del compilador dado que con este se programa eléctricamente el microcontrolador o con el mismo se puede realizar una simulación computacional. por medio del uso de funciones predefinidas y las no predefinidas que el desarrollador puede crear.

o se pueden declarar por medio de la opción sin signo con la directriz unsigned. 20 . sin embargo para la programación de los microcontroladores el estudiante requiere una porción fundamental que le permita iniciar y crear los primeros proyectos en MikroC PRO. Las variables char se utilizan para almacenar caracteres codificados con el código ASCII. rápidamente tomó fuerza por su funcionalidad y facilidad en la implementación en diversos sistemas computacionales que requerían códigos de máquina.1 Declaración de variables en lenguaje C Las variables básicas en este compilador específico son:  bit  char  short  int  long  float  double Las variables bit permiten almacenar un valor lógico es decir verdadero o falso. está variable permite guardar números de: -32767 a 32767. Las variables tipo int guardan números enteros de 16 bits. La forma del lenguaje C. fue creado por los laboratorios Bell como resultado de la necesidad de reescribir los sistemas operativos UNIX con el fin de optimizar el conocido código ensamblador. y BCPL.2 Fundamentos de lenguaje C El lenguaje C data del año 1972. Una variable short almacena un número entero de 8 bits corto puede valer de: -127 a 127. son útiles para guardar letras o textos. Las anteriores variables pueden declararse incluyendo el signo positivo y negativo. De igual manera el lenguaje C fue la evolución de lenguajes previos llamados B. se fundamenta en un complejo estructural que requiere un amplio estudio de las mismas. La variable tipo long almacena números enteros largos de 32 bits. 2. 0 ó 1. para este fin el actual capítulo se centra en conocer las nociones necesarias para iniciar el estudio de los microcontroladores con este libro. El nuevo lenguaje C. su rango puede ser de: -2147483647 a 2147483647. Las variables tipo float y double permiten guardar números con punto decimal.

45.32767 a 32767 . Los siguientes ejemplos muestras como declarar variables sin signo: unsigned char CARACTER.68. En el punto de la declaración de una variable es posible dar un valor inicial a cada una de las variables sin embargo este último no es estrictamente necesario.45. Por último la declaración debe culminar con el carácter punto y coma (. Tipo de variable bit char short int long float double unsigned char unsigned short unsigned int unsigned long Tamaño en Bytes 1 1 1 2 4 4 4 1 1 2 4 Tabla 2-1 Valores que soporta 0ó1 -127 a 127 -127 a 127 . en conclusión este tipo de declaraciones pueden ser de una o más dimensiones. long ENTERO2=-954261. //Declaración de una variable tipo entera inicializada con //el valor 1234. //Declaración de una variable tipo char inicializada con el //valor ASCII del carácter J.4x10^38 0 a 255 0 a 255 0 a 65535 0 a 4294967295 La declaración de variables se realiza indicando el tipo de variable seguido de un nombre que el desarrollador asigna arbitrariamente a la variable. char CARACTER2='J'. //Declaración de una variable tipo entera sin signo. //Declaración de una variable con punto decimal //inicializada con el valor 56.).En la siguiente tabla se pueden apreciar las características de las variables.5x10^45 a 3. En los siguientes ejemplos se puede apreciar como se hacen las declaraciones: bit VARIABLE1_BIT. int ENTERO=1234. unsigned int ENTERO. char CARACTER. 21 . unsigned long ENTERO2. //Declaración de una variable tipo char. //Declaración de una variable con punto decimal //inicializada con el valor -12. //Demacración de una variable de tipo entero largo sin signo.4x10^38 -1. //Demacración de una variable de tipo entero largo //inicializada con el valor -954261. //Declaración de una variable tipo char sin signo. double DECIMAL2=56.68. Las variables también pueden ser declaradas en un formato que asocia varias variables a un mismo nombre.5x10^45 a 3.2147483647 a 2147483647 -1. float DECIMAL=-12. //Declaración de una variable tipo bit. este formato se conoce como una cadena de variables. o un vector e incluso puede ser una matriz de variables.

//Declaración de una cadena de números con //punto decimal inicializadas.2. para este fin tenga presente las siguientes recomendaciones:    Las variables no deben tener nombres repetidos.3. int Edad. Una variable no puede utilizar caracteres especiales como: / * „ . este tipo de declaración se puede ver en el siguiente ejemplo: char Texto[20] = “Nuevo Texto”.Nombre[2]=‟a‟. char -CARÁCTER!. y la forma de declarar una variable creada por el desarrollador es la siguiente: typedef struct { char Nombre[10]. A continuación se muestran ejemplos de declaraciones de variables que no se pueden hacer: bit 1_VARIABLE-.4. Estás estructuras se pueden realizar con las variables ya predefinidas y pueden ser declaraciones de variables o de arreglos de variables.1.Edad = 25. //Declaración de una cadena de caracteres //inicializada con el texto: Nuevo Texto.1}. //Declaración de una cadena de enteros con //valores iniciales.Nombre[0]=‟J‟. 22 . float Decimales[3]={0. U.El siguiente ejemplo muestra un vector de caracteres. La declaración de las variables debe respetar algunas reglas básicas que evitan errores y contradicciones en la compilación del código. Este tipo de estructuras se declarar. { }-\! · % &.8}. //Cadena de caracteres con 20 posiciones de memoria.5. o también conocido como una cadena de caracteres: char Texto[20]. De igual manera las cadenas de caracteres o de variables pueden ser declaradas con un valor inicial.8. int 3ENTERO*.Nombre[1]=‟u‟. De igual manera es posible crear estructuras de información como un nuevo tipo de variable creada por el desarrollador. U. U.5. La siguiente es la forma de usar una variable personalizada por el desarrollador: Usuario U. Las variables no deben empezar por un número. por medio de la directriz: typedef struct. int Enteros[5]={5. }Usuario. U.

E y F. 9. U.Nombre[4]=0. se puede hacer como se ve en el siguiente ejemplo: int A. 1. La restá aritmética entre dos o más números. se puede hacer como se ve en el siguiente ejemplo: int A.Nombre[3]=‟n‟. //Está expresión guarda la suma de A y B en la variable C. a pesar de que para el trabajo de bajo nivel del microcontrolador todos sus números están procesados en base 2 es decir en números binarios. D. C. un ejemplo de está escritura es: 0b10100001 que es equivalente al número decimal 161. int B. es decir con números decimales.3 Operadores en lenguaje C El lenguaje C permite hacer operaciones aritméticas o matemáticas básicas entre números contenidos en variables o constantes. A. 4. El ser humano no está acostumbrado a pensar y procesar operaciones en está base numérica. C = A+B. B y C en la variable C. 23 . Los números binarios solo pueden tener dos dígitos que son el 0 y el 1. Las operaciones aritméticas disponibles son las siguientes:      Suma Restá Multiplicación División Modulo La suma aritmética entre dos o más números. Los números hexadecimales pueden tener 16 dígitos que son. la expresión 0x2A.U. //Está expresión guarda la diferencia entre A y B en la variable C. int C. //Está expresión guarda la suma de A. 2. 0. y equivale a 42 en decimal. son los implementados en este compilador en lenguaje C.2 Formatos numéricos usados en el lenguaje C Los aplicativos en lenguaje C usan números en diferentes bases numéricas. y 16. es un ejemplo de un número hexadecimal en lenguaje C. 7. 8. es la forma de mayor simplicidad ya que se escriben en lenguaje C de la misma manera convencional que se aprende desde los primeros cursos de matemáticas. 3. B. //Está expresión guarda la diferencia entre A. B y C en la variable C. Los sistemas numéricos en base 2. 5. y hexadecimal haciendo más simple realizar cálculos y tareas que en decimal serían más complejas. Los números binarios se escriben con el encabezado 0b seguidos del número en binario. C = A+B+C. int C. int B. C = A-B-C. Los números en base 16 o hexadecimales se denotan con el encabezado 0x precedidos de un número en hexadecimal. Desde las primeras etapas de la formación académica las escuelas y colegios enseñan a pensar y procesar todos los cálculos en base 10. 2. C = A-B. La escritura de números decimales. Además del sistema decimal el compilador en lenguaje C puede trabajar otras bases tales como el binario. 2. Es por esto que los compiladores en lenguaje C trabajan los números decimales facilitando los diseños para el desarrollador. 10. 6.

A*=3. int C. (%). B y C en la variable C. int B. int C. int B. C = A*B*C. C = A%B. la aplicación de está operación se puede ver en el siguiente ejemplo: int A. //Este operador divide el valor de A en 4. //Está expresión guarda la división A entre B en la variable C. //Este operador decrementa en una unidad el valor de A. el cálculo del módulo se denota con el carácter de porcentaje. //Está expresión es equivalente a C = (A+B)÷C. int B. la operación se relaciona con el carácter asterisco (*). int B. Las operaciones aritméticas pueden ser usadas en forma combinada. int C. Para entender de forma más clara este concepto observe los siguientes ejemplos: int A=100. de igual manera es posible hacer está operación con la multiplicación y la división. La operación módulo calcula el módulo de una división aritmética es decir calcula el residuo de una división. int B=10. para ver esto con mayor claridad observe los siguientes ejemplos: int A. Otros operadores matemáticos abreviados pueden ser utilizados cuando se requiere de conteos o cambios de una variable de forma constante. C = A/B.La operación matemática de multiplicación se puede realizar entre dos o más números. A--. está expresión se puede apreciar con mayor claridad en los siguientes ejemplos: int A. 24 . A++. //Está expresión guarda la multiplicación entre A y B en la variable C. //Este operador decrementa en 5 el valor de A. C = A*B. A/=4. La división aritmética en lenguaje C se especifica por medio de la barra inclinada (/). por ejemplo es posible incrementar o decrementar una variable en términos de un número. //Este operador incrementa en una unidad el valor de A. // Está expresión es equivalente a C = (A÷B) X C. //Este operador multiplica el valor de A por 3. es decir que se pueden mezclar varias operaciones en una misma expresión. en el siguiente ejemplo se puede observar su implementación: int A. //Este operador incrementa en 4 el valor de A. C = (A/B)*C. C = (A+B)/C. //Está expresión guarda el residuo de la división de A entre B en la variable C. A+=4. A-=5. //Está expresión guarda la multiplicación entre A. int C.

Las operaciones lógicas se realizan en lenguaje C entre dos variables o constantes. NOR. en el siglo XIX. AND. está temática será tratada posteriormente cuando se estudie el uso y declaración de funciones. A*=B. Estás operaciones son ampliamente estudiadas en los cursos de sistemas digitales combinacionales y secuenciales. estás se hacen bit a bit. XOR. del mismo peso en una variable o número.A+=B. Para ver y entender los ejemplos recuerde primero las tablas de verdad de las operaciones lógicas que se muestran a continuación: Inversor NOT Entrada Salida 0 1 1 0 OR inclusiva Entrada 1 Entrada 2 0 0 0 1 1 0 1 1 Salida 0 1 1 1 NOR inclusiva Entrada 1 Entrada 2 0 0 0 1 1 0 1 1 Salida 1 0 0 0 OR exclusiva o XOR Entrada 1 Entrada 2 Salida 0 0 0 0 1 1 1 0 1 1 1 0 AND Entrada 2 0 1 0 1 NOR exclusiva o XNOR Entrada 1 Entrada 2 Salida 0 0 1 0 1 0 1 0 0 1 1 1 NAND Entrada 1 Entrada 2 0 0 0 1 1 0 1 1 Entrada 1 0 0 1 1 Salida 0 0 0 1 Salida 1 1 1 0 25 . //Este operador incrementa el valor de A en el valor de B unidades. por medio de funciones predefinidas en la librería math. NAND. NOT. Los operadores lógicos permiten realizar acciones u operaciones que respetan la lógica digital planteada por el matemático inglés George Boole. Boole planteó las operaciones OR. XNOR. //Este operador multiplica el valor de A por B veces. Otras operaciones matemáticas de mayor complejidad se pueden realizar en lenguaje C.

unsigned short VALOR1=0b01010000. es guardado en RESULTADO. está se denota con el carácter acento circunflejo (^). RESULTADO = ~(VALOR1^VALOR2). unsigned short RESULTADO. está se denota con el carácter barra vertical (|). son similares a AND. RESULTADO = VALOR1^VALOR2. en el siguiente ejemplo pueden verse las aplicaciones de los operadores lógicos: Operación lógica NOT. //El valor de la operación lógica o negada // exclusiva negada. negación. 95. Operación lógica XOR. OR. unsigned short RESULTADO. RESULTADO = ~VALOR1. //Variable inicializada en binario con el número 80. a agregando el carácter de negación virgulilla. Operación lógica AND. // es guardado en RESULTADO. 15. XNOR. que denotan cada una de ellas. es guardado en RESULTADO. en lenguaje C se realiza por medio del doble carácter mayor que. unsigned short VALOR2=0b01011111. o inclusiva. (>>). unsigned short VALOR2=0b01011111. unsigned short VALOR1=0b01010000. //Variable inicializada en binario con el número 80. se denota con el carácter virgulilla (~). //La variable RESULTADO guarda el complemento de //VALOR1. o exclusiva. //Variable inicializada en binario con el número 95. //Variable inicializada en binario con el número 95. El corrimiento a la derecha en una variable o valor constante. RESULTADO = VALOR1&VALOR2. unsigned short VALOR2=0b01011111. RESULTADO = ~(VALOR1&VALOR2). La implementación de las operaciones lógicas NAND. La operación de corrimiento hace perder los valores de los bits que salen. //El valor de la operación lógica AND //es guardado en RESULTADO. está se denota con el carácter ampersand (&). NOR. unsigned short RESULTADO. //El valor de la operación lógica o //exclusiva. unsigned short VALOR1=0b01010000. //El valor de la operación lógica y negada.Las operaciones lógicas en lenguaje C. e ingresa ceros en 26 . //Variable inicializada en binario con el número 80. //Variable inicializada en binario con el número 80. unsigned short RESULTADO. 175. 80. unsigned short RESULTADO. Operación lógica OR. //Variable inicializada en binario con el número 80. //El valor de la operación lógica o negada // exclusiva. (<<). RESULTADO = ~(VALOR1|VALOR2). observe los siguieres ejemplos: unsigned short VALOR1=0b01010000. //Variable inicializada en binario con el número 95. //El valor de la operación lógica o. //Variable inicializada en binario con el número 95. unsigned short VALOR1=0b01010000. y XOR. y. de la misma manera el corrimiento a la izquierda se ejecuta con el doble carácter menor que. El desplazamiento de bits dentro de una variable es útil para realizar procesos y tareas que impliquen la manipulación de datos a nivel de los bits. es guardado en //RESULTADO. se realizan con caracteres específicos. es guardado en RESULTADO. unsigned short VALOR2=0b01011111. RESULTADO = VALOR1|VALOR2.

4 Funciones en lenguaje C Una función es una fracción de código que realiza una tarea específica cada vez que está es invocada por el flujo del programa principal. } //Cierre de la función con corchete. //Está operación guarda en la variable Resultado el corrimiento de 4 bits //a la izquierda de la variable Dato.los nuevos bits. { // Apertura de la función con corchete. de igual manera estos parámetros de entrada y salida pueden ser vacíos. uno o más parámetros de entrada. En los siguientes ejemplos se puede observar la implementación de estos operadores: short Dato=0xFF. En la siguiente tabla se resumen las operaciones lógicas y aritméticas contempladas anteriormente: Descripción de la operación Suma Restá División Módulo o residuo de la división entera Incremento Decremento Operación OR Operación AND Operación XOR Complemento a 1 Corrimiento a la derecha Corrimiento a la izquierda Tabla 2-2 Operador + / % ++ -| & ^ ~ >> << 2. Resultado = Dato>>4. //Está operación guarda en la variable Resultado el corrimiento de 4 bits //a la derecha de la variable Dato. Cualquiera de estás dos características puede ser una variable o un arreglo de ellas. //Porción de código donde se ejecutan la rutina de la función. valor final de Resultado es 0x0F. //Declaración de variables. { // Apertura de la función con corchete. Las funciones cuentan con dos características fundamentales. valor final de Resultado es 0xF0. Resultado = Dato<<4. 27 . //Porción de código donde se ejecutan la rutina de la función. } //Cierre de la función con corchete. y un parámetro de salida. short Resultado. void Funcion ( int A ) //Función con un parámetro de entrada y salida vacía. La estructura de las funciones se puede apreciar en los siguientes ejemplos: void Funcion ( void ) //Función con parámetros de entrada y salida vacíos.

El siguiente ejemplo muestra una función que es capaz de calcular el producto de dos números con punto decimal: float Producto ( float A. float PI=3. //Porción de código donde se ejecutan la rutina de la función.1416. { // Apertura de la función con corchete. Los nombres que se designan a las funciones cumplen con las mismas reglas para nombrar las variables. 28 . } //Cierre de la función con corchete. { // Apertura de la función con corchete. { // Apertura de la función con corchete. Una función puede recurrir a otra función para realizar funciones más complejas. } //Cierre de la función con corchete. float RESULTADO. { // Apertura de la función con corchete. return RESULTADO. float Valor_PI ( void ) //Función que retorna el valor de π.1416. para demostrar está situación se puede ver el siguiente ejemplo. antes de realizar cualquier otra acción o instrucción. En el caso en que una función requiera variables internas está declaración se debe hacer al principio de la misma. } //Cierre de la función con corchete. que realiza el cálculo del área de una circunferencia en función de su radio: Figura 2-1 El área de la circunferencia se calcula por medio de la ecuación A=πr². float Area_Circunferencia ( float Radio ) //Función para calcular el área del circulo. } //Cierre de la función con corchete. RESULTADO = A*B. return PI. donde el valor de π es aproximadamente 3. { // Apertura de la función con corchete. //Producto de A y B. int Funcion ( int A ) //Función con un parámetro de entrada y salida enteras.int Funcion ( void ) //Función con parámetro de entrada vacío y salida entera. float Area. //La función retorna el resultado guardado en RESULTADO. flota B ) //Función para calcular el producto de A y B. //Porción de código donde se ejecutan la rutina de la función.

La función main en el caso particular de los microcontroladores no contiene parámetros de entrada ni de salida. sin embargo existen algunas que están predefinidas en el compilador y pueden ser consultadas e implementadas respetando los parámetros de entrada y salida. { // Apertura de la función main. //Declaración de funciones propias del desarrollador. su declaración se puede apreciar en el siguiente ejemplo: void main( void ) //Declaración de la función main. } 29 . return Area. exponenciales. la más importante de ellas es la función main o principal. 2. primero es indispensable declarar las variables globales que el desarrollador considere necesarias para el funcionamiento del programa. Posteriormente se declara la función main y al comienzo de está se deben declarar las variables que se requieran dentro de la misma. //Declaración de variables globales. } //Cierre de la función main. Por último se edita el código que contiene el aplicativo concreto del programa. La importancia de está función radica en el hecho de que es sobre ella que corre todo el programa del microcontrolador y está se ejecuta automáticamente cuando el microcontrolador se energiza. estás variables globales son reconocidas por todos los puntos de código del programa incluidas las funciones propias del desarrollador y la función main. Las funciones son creadas por el desarrollador. //Código del programa principal del microcontrolador. Las funciones matemáticas trigonométricas y otras como logaritmos. //Cierre de la función con corchete. return PI. En el siguiente ejemplo se puede ver cómo hacer una estructura para un programa: int Varible1.} Area = Valor_PI()*Radio*Radio. float Valor_PI ( void ) { float PI=3. El paso a seguir es hacer las declaraciones de funciones diseñadas por el desarrollador para las tareas específicas en su programa. //Cálculo del área del circulo.5 Creación de un programa en lenguaje C La estructura de un programa en lenguaje C es relativamente simple. El código que sigue debe configurar e inicializar los puertos y módulos del microcontrolador que sean indispensables en la aplicación. y en el caso de una función que invoque a otra función debe ser declarada antes de la función que hace el llamado. Está librería se denomina C_Math. float Variable3.1416. char Variable2. Las declaraciones de funciones hechas por el desarrollador deben ser declaradas antes de la función main. y potenciación pueden ser usadas con la librería predefinida por el compilador.

e igual (<=). float Valor_Area. permiten crear bucles iterativos. Para entender con claridad estos operadores observe y analice los siguientes ejemplos: short A. mayor o igual que. permiten realizar menús. e igual que. la condición es falsa cuando su valor es 0 y retorna verdadero para cualquier otro valor diferente de 0. while( 1 ) //Código concreto del aplicativo. { Valor_Area=Calculo_Area_Circulo( Valor_Radio ). la condición menor que. la condición mayor que se usa con el carácter que tiene su mismo nombre (>). que cuentan o gobiernan tareas y procesos desarrollados en el programa. 30 . Area = Valor_PI()*Radio*Radio. la condición mayor o igual que. la condición AND o y. se realiza con los caracteres doble barra vertical (||). se usa con los caracteres doble ampersand (&&). e igual (>=). menor que. Las formas condicionales usadas en lenguaje C son: NOT. se usa con los caracteres de igualdad y admiración (!=). y controlar el flujo del programa o aplicativo. Las condiciones lógicas solo retornan dos posibles valores: falso o verdadero. TRISC=0. Las estructuras iterativas while.1 Creación de condiciones lógicas en lenguaje C Las condiciones lógicas permiten tomar decisiones en función de una evaluación. //Cálculo del área. PORTC=0. se hace con el carácter menor que (<). Las sentencias condicionales if.6. //Configuración de puertos y módulos. se realiza con los caracteres menor que. y la condición diferente que. PORTB=0. El uso de la negación se realiza con el carácter admiración (!). También se pueden usar comparaciones de magnitud entre dos valores tales como: mayor que. menor o igual que.float Calculo_Area_Circulo( float Radio ) { float Area. la condición de igualdad se hace con los caracteres doble igual (==). diferente que. la condición OR.6 Condicionales e iteraciones en lenguaje C Las formas condicionales e iterativas hacen parte vital de las estructuras de cualquier código en lenguaje C. la condición menor que. y switch case. do while y for. o negación. 2. return Area. } } 2. se realiza con los caracteres mayor que. Estás permiten hacer ciclos en función de conteos y condiciones de variables que hacen efectivo el flujo del programa. if else. OR o o inclusiva. TRISB=0. //Declaración de variables de la función main. tomar decisiones. AND o y. } void main( void ) //Declaración de la función main o principal { float Valor_Radio. Cuando la condición lógica se hace directamente sobre una variable o número.

} Las sentencias if e if else se pueden anidar para crear estructuras más completas y complejas. (A<=4)&&(B>=6) //La condición es verdadera si A no es mayor que 3 y si B es diferente de 4. (A==10)&&(B==20) //La condición es verdadera si A es diferente de 5 y B es mayor que 2. if( Valor>100 ) //Este if evalúa si la variable Valor es mayor que 100. { // Porción de código que se ejecuta si el if es verdadero. (A!=5)&&(B>2) //La condición es verdadera si A es menor o igual que 4 o si B es mayor o igual que 6. { // Código que se ejecuta cuando el primer if tiene una condición verdadera. //Declaración de variables. está característica se puede ver con claridad en el siguiente ejemplo: short Valor1. Observe la estructura del la sentencia if en el siguiente ejemplo: short Valor. if( Valor>100 ) //Este if evalúa si la variable Valor es mayor que 100. } else { // Porción de código que se ejecuta si la condición del if es falsa. Para entender está situación observe el siguiente ejemplo: short Valor. //La condición es verdadera si A vale 10 y B vale 20. if( Valor1>30 ) //Sentencia if. short Valor2. { //Código que se ejecuta cuando el segando if tiene una condición verdadera.6. su única diferencia es que en el dado caso de que la evaluación de la condición sea falsa se ejecuta la porción de código asociada al else.2 La sentencia condicional if e if else La estructura de la sentencia if evalúa una condición lógica y ejecuta una porción de código si y solo si el resultado de la evaluación es verdadera.short B. { // Porción de código que se ejecuta si la condición del if es verdadero. if( (Valor2==4)&&(Valor1<50) ) //Segunda sentencia if anidad. } La estructura if else es igual que la sentencia if. !(A>3)&&(B!=4) 2. } } 31 .

funciona de manera similar a la sentencia if. difiere en que no se evalúa una condición si no el valor de una variable.6. es similar al ciclo while. break. //Ruptura del caso 20. Para conocer la forma de declarar este tipo de ciclo observe el siguiente ejemplo: short CONT=0. //Declaración de variable entera que cuenta hasta 100. case 1: //Fragmento de código correspondiente al valor 1 de la variable Valor. //Ruptura del caso 1. Los valores contemplados en los casos o case posibles de la variable los escoge el desarrollador arbitrariamente en función de su criterio y necesidad. //Incremento de la variable CONT. En conclusión el código se ejecuta por lo menos una vez antes de evaluar la condición. //Ruptura del caso 50. por medio de la directriz default. switch( Valor ) //Evaluación de la variable Valor. En el siguiente ejemplo se puede ver la forma de usar este tipo de ciclo: short CONT=0.4 El ciclo iterativo while y do while El ciclo iterativo while repite o ejecuta un fragmento de programa siempre y cuando la condición contenida en el while sea verdadera. break. break. Cada uno de los fragmentos de código editados deben terminar con la directriz break para romper el flujo de la estructura switch case. Los casos o valores que no son de interés para el desarrollador se ejecutan en un fragmento de código por defecto. con la diferencia puntual de que en este ciclo el fragmento de código se ejecuta y después evalúa la condición del while. //Ruptura del default. while( CONT<100 ) //Declaración del ciclo while. //Ruptura del caso 10. break. y ejecuta una porción de código para cada valor posible de la variable. case 20: //Fragmento de código correspondiente al valor 20 de la variable Valor. } 2. case 10: //Fragmento de código correspondiente al valor 10 de la variable Valor. do //Declaración del ciclo do while. está acción es necesaria dado que si dos casos están seguidos los fragmentos de código seguidos se ejecutarán hasta encontrar la directriz break o hasta finalizar la estructura switch case. { 32 . default: //Código correspondiente para cualquier otro valor por defecto. { CONT++.3 La sentencia switch case La sentencia switch case. } La implementación de un ciclo do while.2. { case 0: //Fragmento de código correspondiente al valor 0 de la variable Valor. break. //Ruptura del caso 0. //Declaración de variable entera que cuenta hasta 100. break. case 50: //Fragmento de código correspondiente al valor 50 de la variable Valor.6. Para comprender de forma clara el funcionamiento de la sentencia switch case observe el siguiente ejemplo: short Valor.

{ //Fragmento del primer ciclo. COL<10. for( COL=0. COL++ ) //Declaración de ciclo for. short MATRIZ[10][10]. //Incremento de la variable CONT. //Declaración de variables. el último espacio realiza cambio sobre una o más variables. //Declamación de un vector bidimensional. short FIL. //Declaración de variables.CONT++. tal como: incrementos. for( A=0. pero está no es una regla. guarda valores en una matriz de 10 por 10. } 2. el primero es un espacio de código para dar valores iniciales a una o más variables. (A<100)||(B>20). A++. Para comprender este concepto de mejor manera observe el siguiente ejemplo: short COL. recorriendo una a una las posiciones de la misma. _CAMBIO_ ) { } Para conocer el funcionamiento del ciclo for observe el siguiente ejemplo: short A. _CONDICION_.) //Estructura del ciclo for.6. Un ciclo puede contener uno o más ciclos anidados. La condición cuenta con tres parámetros.5 El ciclo iterativo for El ciclo iterativo for es una estructura parecida al ciclo while. for( FIL=0. } while( CONT<100 ). } } El ejemplo anterior.6. la diferencia es una estructura más compleja en los paréntesis de la condición. B=100. La anidación de ciclos consiste en ejecutar un ciclo dentro de otro. short B. 33 . decrementos. 2. FIL++ ) //Declaración de ciclo for anidado. Observe la estructura del for en el siguiente modelo: for( _INICIO_ . MATRIZ[COL][FIL] = COL*FIL. el segundo campo permite crear una condición lógica que hace repetir el fragmento de código del for cuando está es verdadera. { //Fragmento de código que se ejecuta cuando la condición del for es verdadera. B-.6 Uso anidado de ciclos iterativos Muchas de las aplicaciones incrementan su funcionalidad y optimizan el tamaño del código de máquina final al usar los ciclos iterativos de forma anidada. etc. { //Fragmento del ciclo anidado. Las variables que se cambian en el último campo son generalmente las mismas que se inician en el primer campo. FIL<10.

entre los dispositivos digitales es posible simular displays de siete segmentos. el ISIS puede simular una gran variedad de dispositivos digitales y analógicos. que se ha posicionado desde hace mas de 10 años. y luminosidad. El simulador permite. osciloscopios. En la siguiente imagen se puede apreciar la apariencia visual del entorno de desarrollo del ISIS: Figura 3-1 El uso y trabajo bajo en el torno de ISIS demanda una amplia cantidad de herramientas y opciones que se deben conocer pero se conocerán paulatinamente en el transcurso de los ejemplos. simular actuadores como: motores dc. Además de los PIC. una herramienta ideal para el diseño y estudio de los PICMicro. Es posible simular periféricos de entrada y salida como teclados. En conclusión estás y otras características hacen del ISIS de Proteus. como una de las herramientas más útiles para la simulación de los microcontroladores PICMicro. El ISIS permite la simulación de las familias de los PICMicro más populares tales como la: 12F. y puertos físicos del ordenador como: RS232. Este simulador cuenta con una amplia variedad de instrumentos de medición como voltímetros. 34 . humedad.com. amperímetros. de caracteres. ISIS puede simular sensores de temperatura. presión. 18F. 16F. y USB. entre otros. Una versión demostrativa del paquete de software se puede descargar de la página: www.labcenter. y analizadores de señal. luces incandescentes entre otros. servo motores.3 El simulador ISIS de Proteus El simulador ISIS de Proteus es un poderoso paquete de software. desarrollado por la compañía labcenter electronics. y gráficos.

LED-RED. en está paleta el desarrollador debe identificar el botón P. en las 35 . Para buscar un dispositivo por medio de la referencia se digita la referencia en la casilla: Keywords. para este fin se debe pulsar el icono siguiente: este se encuentra en la paleta de herramientas que está en la parte izquierda de la ventana del programa. Para seleccionar un dispositivo de está lista se da doble clic sobre cada uno de los numerales de la lista. para mayor claridad observe la siguiente imagen: Figura 3-2 Al picar el botón P. Como primera medida se debe identificar la paleta de dispositivos.1 Características básicas del ISIS para simular En la siguiente sección se mostrarán las características básicas para hacer la primera simulación en ISIS. que está a la izquierda de la pantalla. en la paleta de dispositivos debe verse lo siguiente: Figura 3-3 El paso siguiente es buscar las terminales de referencia y poder.3. y una resistencia. Para iniciar busque los siguiente dispositivos: BUTTON. Cuando este botón se pica permite ver una lista de terminales. el programa genera una lista en la parte derecha de la ventana con los dispositivos relacionados a la solicitud del usuario. o bajo la clasificación que el mismo ISIS tiene. el programa abre una nueva ventana que permite buscar los dispositivos electrónicos por medio de la referencia comercial. RES. que corresponden a un pulsador. un LED de color rojo. Después de este proceso.

Para pegar los dispositivos en el área de trabajo se sigue el mismo procedimiento de las terminales. para este ejemplo se conectan todos los elementos en serie.cuales se encuentra GROUND. Para colocar estás terminales en el área de trabajo se pica los ítems en la paleta de terminales y posteriormente en el área de trabajo. Al terminar las conexiones se ve la siguiente vista: 36 . La terminal de poder tiene una diferencia de potencial por defecto de 5 voltios. al finalizar se bebe tener la siguiente vista: Figura 3-5 El paso siguiente es realizar las conexiones eléctricas. para hacer la conexión entre dos terminales se pica una terminal y después la siguiente. las cuales corresponden respectivamente a la referencia eléctrica y al poder o Vcc. y POWER. El programa termina rutiando la conexión. para este fin el cursor del ratón adopta la forma de lápiz. después de está acción en el área de trabajo se debe ver lo siguiente: Figura 3-4 El paso siguiente es pegar los dispositivos en el área de trabajo para esto pique el botón: que se encuentra en la paleta de herramientas de la izquierda.

Figura 3-6 Para finalizar se puede cambiar el valor de la resistencia dando clic izquierdo sobre la resistencia. lo que se debe hacer es cambiarlo por 330. La creación de circuitos en ISIS. que serán tratadas en el transcurso de los ejemplos. y ver el efecto de la simulación. que por defecto es de 10k. para su posterior simulación. implica hacer otra serie de acciones. cuando la simulación este corriendo se puede pulsar el BUTTON. 37 . con está acción sale una ventana que permite editar el valor de la resistencia. Este simple ejemplo permite mecanizar el proceso de creación de un circuito digital. El paso siguiente es ejecutar o correr la simulación para esto se utiliza la paleta de reproducción que está en la parte inferior izquierda de la pantalla. La apariencia de está paleta es la siguiente: Figura 3-7 Por último se pica el botón de play y la simulación se ejecuta.

38 . El último ítem del asistente pregunta si el desarrollador quiere seleccionar las librerías que usará en este trabajo. En está opción seleccione el PIC P16F84A. Al correr el MikroC PRO. por defecto este ítem selecciona todas las librerías habilitadas para este PIC.hex. El siguiente paso solicita adicionar archivos que serán anexados al proyecto. que se desea usar. Cuando se realiza un proyecto nuevo este paso se puede omitir y se pulsa Next. con está acción el programa despliega un asistente fácil de usar para crear el nuevo proyecto. El siguiente paso es definir la frecuencia se oscilación con la cual trabajará el PIC. en este directorio el programa guardará todos los archivos requeridos. La siguiente opción permite definir el directorio en donde el desarrollador guardará el proyecto. se identifica en el menú superior el ítem Project. y el ejecutable del PIC es el archivo . en este ejemplo se selecciona 4. en los cuales la fuente del código será el archivo con extensión . se pica y dentro de este se pica New Project….000000 MHz. con este paso el asistente muestra una casilla para seleccionar la referencia del PICMicro.c.4 Creación del primer programa en MikroC PRO El proceso siguiente debe ser mecanizado para ser implementado cada vez que se haga un proyecto o programa nuevo para un PICMicro. La apariencia visual de este asistente es la siguiente: Figura 4-1 La siguiente acción es pulsar el botón Next.

lo mejor es dejar todas las librerías activas. Por último se termina la configuración y se crea el proyecto, al terminar debe aparecer una vista como la siguiente:

Figura 4-2

Cuando un cambio se realice sobre el código del programa se debe compilar el código picando el siguiente botón: que está ubicado en la parte superior del programa dentro de la paleta de herramientas. Está acción genera los resultados de la compilación que se encuentran en la parte inferior de la ventana del programa. Los mensajes se deben terminar con un texto de finalización exitosa. Para iniciar la edición del proyecto, se configuran los puertos del PIC, y posteriormente se encierra el programa en un bucle infinito. El PIC 16F84A, tiene dos puertos él A y el B, para configurar los puertos como salida o como entrada se manipula el registro TRIS. Cada puerto cuenta con su respectivo registro TRIS, que hace alusión a los tres estádos posibles, alto, bajo, y alta impedancia. Los registros TRIS cuentan con el mismo número de bits del puerto, por ejemplo el puerto B o PORTB de este PIC tiene 8 bits, por lo tanto el TRISB también tiene 8 bits. Los bits de los registros TRIS, son correspondientes al puerto, y definen bit a bit el estádo del puerto. Si un bit en el TRIS es 0 el mismo bit en el puerto es de salida, y si el bit del TRIS es 1 el mismo bit del puerto es de entrada o está en alta impedancia. Para entender este concepto con mayor claridad observe y analice el siguiente ejemplo: TRISB = 0b11110000; // Configura los cuatro bits de menor peso como salida, y //los cuatro bits de mayor peso como entrada. PORTB=0b00000000; //Los bits de salida asumen un 0 lógico. Este ejemplo usa un pulsador y un par de LEDs para ver el comportamiento del programa. Observe y analice el programa a continuación: void main ( void ) 39

{ unsigned int CONTADOR=0; TRISB = 0b11110000; // Configura los cuatro bits de menor peso como salida, y //los cuatro bits de mayor peso como entrada. PORTB=0b00000000; //Los bits de salida asumen un 0 lógico. while( 1 ) //Bucle infinito { if( PORTB.F7==0 ) //Evalúa si bit RB7 es 0 { if( PORTB.F0==1 ) //Evalúa el valor del bit RB0 y conmuta su valor. PORTB.F0=0; else PORTB.F0=1; while( PORTB.F7==0 ); //Espera a que el RB7 cambie a 1. } CONTADOR++; //Incrementa el valor del CONTADOR. //La siguiente condición if cambia automáticamente el estádo del bit RB1 if( CONTADOR&0x0100 ) //Evalúa si el bit 8 del CONTADOR es 1 PORTB.F1=1; else PORTB.F1=0; } } El paso siguiente es hacer la simulación en ISIS, las resistencias de los LEDs deben ser cambiadas a 330Ω, la terminal Master Clear, MCLR debe ser conecta a Vcc para que el PIC, no se reinicie al terminar se debe ver de la siguiente forma:

Figura 4-3

40

Antes de correr la simulación se debe cargar el archivo .hex, para este proceso se hace clic izquierdo sobre el PIC, y aparece una ventana que permite buscar el archivo .hex, en está ventana también se ajusta la frecuencia de oscilación. Por defecto este valor es 1MHz, para efectos de simulación en este caso se debe seleccionar 4MHz, la vista de está ventana es la siguiente:

Figura 4-4

Para fines de la programación física del microcontrolador es importante tener presente las condiciones de configuración del PIC, cada uno de los microcontroladores de Microchip, poseen un campo de memoria que caracteriza el comportamiento de real del PIC, entre estos se pueden destácar; el tipo de oscilador o reloj de procesamiento, por ejemplo cuando se usa un cristal de 4M Hz, el oscilador del PIC se debe configurar como XT, si el oscilador es de 20M Hz se usa la configuración HS, si se trata de un cristal de baja velocidad como 400K Hz se usa la configuración LP, y si se implementa un oscilador por medio de circuito RC, o circuito de resistencia en serie con capacitor se usa la opción RC. De la misma forma es posible configurar otras opciones como el uso del perro guardián, la protección de código en memoria FLASH y EEPROM, está última opción es de suma importancia para proteger la información del microcontrolador y evitar que el programa o los datos de la memoria sean leídos. Para modificar la configuración del PIC, se debe buscar el ítem Edit Proyect… que se encuentra en la pestáña Proyect, del menú principal del programa MikroC PRO. El acceso a este ítem se puede apreciar en la siguiente figura:

41

Figura 4-5

Después de picar está opción el compilador MikroC PRO, despliega una ventana con casillas de selección para editar las opciones de configuración del PIC, en función del microcontrolador está ventana puede cambiar de apariencia dado que no todos los PIC, cuentan con las mismas configuraciones, sin embargo en la siguiente figura se puede apreciar la apariencia de esta ventana para un PIC 16F877A:

42

Figura 4-6

43

A. o. para este fin se pueden usar display 7 segmentos. Está designación y la apariencia física de estos displays se pueden apreciar en las siguientes figuras: Figura 5-1 Los display de 7 segmentos son fabricados en dos formatos.5 Visualización de datos El diseño de sistemas microcontrolados implican en algunos casos la visualización de datos al usuario. -. Los display 7 segmentos son un arreglo de diodos LED. F. estos últimos usan dos o más dígitos en un solo encapsulado conectando todos los segmentos en paralelo pero con los terminales comunes por separado. E. de ánodo común y de cátodo común. En este capítulo se estudia y se ejemplarizan estos dispositivos. display LCD de caracteres y display gráficos LCD. organizados de tal forma que permiten visualizar los caracteres según los segmentos que estén activos. d. Los displays de 7 segmentos tienen una designación estándar de cada segmento que es consecutiva de la „a‟ hasta la „g‟.1 Display de 7 segmentos Un display 7 segmentos es un dispositivo que permite visualizar un número limitado de caracteres esencialmente numéricos. sin embargo es posible visualizar unos pocos caracteres mas como: b. los display de 7 segmentos también existen en un formato dinámico. Las siguientes figuras muestran displays de 7 segmentos en su formato dinámico: Figura 5-2 44 . C. 5.

La apariencia visual del editor es la siguiente: Figura 5-3 El uso de está herramienta. para este fin se pica el ítem Tools en el menú del MikroC PRO. El editor permite implementar las constantes para un display de ánodo o de cátodo común. el segmento „b‟ al pin RB1.1 Control de display de 7 segmentos Para realizar el primer ejemplo con este display se bebe conocer como realizar la asignación de pines del PIC. Sin embargo la designación de pines la puede hacer el desarrollador de manera arbitraria. En el siguiente ejemplo se declarará un arreglo constante con los códigos de cada dígito. en el caso de este ejemplo del 0 al 9. y en orden consecutivo como se comentó anteriormente. A medida que se edita el display aparece el valor constante que debe ser usado en las salidas del puerto que se designe para su control. que se puede apreciar en la figura anterior es un octavo segmento que en algunos displays es implementado y corresponde al punto decimal. Con está acción emerge una nueva ventana que permite editar de manera simple los segmentos del display de 7 segmentos. Para la visualización de los dígitos en el display se debe organizar la información de los dígitos en orden consecutivo. para hacer un diseño optimo se puede asignar los pines en orden consecutivo de un puerto por ejemplo el segmento „a‟ se conecta al pin RB0. que permite editar los dígitos del display. dentro de este nuevo menú se pica el ítem Seven Segment Editor. Es importante conocer una herramienta con la que cuenta el paquete de software MikroC PRO. a cada uno de los segmentos del display se debe conectar un pin del PIC. y así sucesivamente hasta el segmento „g‟ al pin RB6. 5. Para este fin la forma de mayor simplicidad es con el uso de una cadena de datos que contenga los 10 códigos de los dígitos.1. para este ejemplo se usará un display de 45 . este se usa si la aplicación así lo requiere.La terminal DP. implica que todos los pines del display estén asignados a un mismo puerto. de la misma forma es posible usar las constantes en formato decimal o hexadecimal.

para este ejemplo: const unsigned short DIGITOS[] = { 0x3F. //Se visualiza el digito correspondiente al //número guardado en la variable CONTADOR. //Código del dígito 8 0x6F. //Código del dígito 8 0x6F. //Código del dígito 9 }. //Código del dígito 5 0x7D. A continuación se muestra el código fuente del PIC. //Código del dígito 0 0x06. //Código del dígito 9 }. void main ( void ) { unsigned short CONTADOR=0. TRISB = 0. //Código del dígito 1 0x5B. //Código del dígito 6 0x07. //Código del dígito 1 0x5B. Observe la forma de declarar las constantes que deben ser insertadas antes de la función main: const unsigned short DIGITOS[] = { 0x3F. //Código del dígito 6 0x07. CONTADOR++. //Código del dígito 5 0x7D. durante los cuales el PIC ejecutará un retardo. //Código del dígito 2 0x4F. //Código del dígito 2 0x4F. //Código del dígito 7 0x7F. que es idéntica a delay_ms pero en micro segundos. Para visualizar los dígitos en el display de 7 segmentos. // Configura el puerto B como salida while( 1 ) //Bucle infinito { PORTB = DIGITOS[CONTADOR]. //Código del dígito 4 0x6D. este ejemplo usará el puerto B del microcontrolador PIC 16F84A.cátodo común. de la misma forma se puede usar la función delay_us. //Se incrementa el valor del conteo. Está función tiene como parámetro de entrada un número entero que representa el tiempo en milisegundos. //Código del dígito 7 0x7F. la visualización de los dígitos estárá controlada por una rutina que cambia temporizadamente por medio de la función delay_ms que está predefinida por las librerías del compilador. //Código del dígito 3 0x66. //Código del dígito 0 0x06. 46 . //Código del dígito 4 0x6D. //Código del dígito 3 0x66.

Este arreglo minimiza las conexiones eléctricas y el consumo de energía. dado que en realidad solo un dígito está activo para todo tiempo. //Se hace un retardo de 1 segundo. y luego restár las unidades de mil del número completo.delay_ms(1000). en este caso es 40m segundos. por esto el número puede ser consignado en una variable de tipo entera. Para comprender este proceso observe y analice la siguiente función: 47 . por lo tanto todos los dígitos deben verse durante un periodo igual al inverso de 25Hz. para este fin se buscan los siguientes dispositivos: 16F84A. Posteriormente se arma el siguiente circuito: Circuito 5-1 Al correr la simulación se debe ver un conteo en el display del 0 al 9. La activación de los displays se debe hacer por medio de otro puerto para este ejemplo se hará por el puerto A. RES. Con 4 dígitos es posible visualizar un número de 0 a 9999. posterior mente se apaga y se activa el dígito de las centenas. } } El paso siguiente es realizar la simulación en ISIS. A la vista del ojo humano los cambios deben ser de 25Hz o más. Para este ejemplo se usarán 4 displays por lo tanto el tiempo visible de cada dígito debe ser la cuarta parte del periodo es decir 10m segundos.2 Control de displays 7 segmentos dinámicos La implementación de displays dinámicos usa la misma teoría de un solo display. y por último se hace lo mismo con las unidades de mil. Este proceso se debe hacer con una velocidad de tal manera que engañe al ojo humano y así se verá como si todos los dígitos estuvieran activos. la visualización dinámica consiste en mostrar un solo dígito al mismo tiempo. por ejemplo para deducir las unidades de mil vasta con dividir el número en mil. y el display se deben usar resistencias de 330Ω. Para ver cada dígito por separado se hace necesario calcular cada uno de los dígitos. con una cadencia de un segundo entre dígito y dígito. y 7SEG-COM-CATHODE. La forma más eficiente de mostrar los números en el display es por medio de una función. Por ejemplo si se muestran cuatro dígitos se activa el display de las unidades. 5. para la conexión entre el PIC. este proceso se repite hasta llegar a las unidades.1. después se apagan y se activa el dígito de las decenas.

unsigned short C. //Código del dígito 6 0x07. //Código del dígito 0 0x06. unsigned short UM. PORTB = DIGITOS[D]. //Cálculo de las centenas. //Código del dígito 1 0x5B. const unsigned short DIGITOS[] = { 0x3F. //Activa en alto el tercer display delay_ms(10). //Código del dígito 8 0x6F. UM = Numero/1000. //Retado de 10m segundos PORTA=0. //Retado de 10m segundos PORTA=0. PORTA. //Cálculo de las unidades. //Visualiza las centenas. void VerDisplay( int Numero ) { unsigned short U. D = (Numero-UM*1000-C*100)/10. //Código del dígito 5 0x7D. } Por último se completa el código fuente con la función main de la siguiente forma: 48 . PORTA. //Visualiza las unidades de mil.F0=1. //Activa en alto el segundo display delay_ms(10). //Visualiza las decenas.// Declaración de las constantes para el display. //Activa en alto el cuarto display delay_ms(10).F3=1. unsigned short D. //Retado de 10m segundos PORTA=0. PORTB = DIGITOS[C]. PORTA. //Cálculo de las decenas. //Variable para guardar las centenas. U = (Numero-UM*1000-C*100-D*10). //Cálculo de las unidades de mil. //Visualiza las unidades. //Código del dígito 4 0x6D. PORTB = DIGITOS[U]. PORTA. //Variable para guardar las unidades. //Desactiva todos los displays. //Variable para guardar las decenas. //Desactiva todos los displays. //Código del dígito 3 0x66. //Desactiva todos los displays. PORTB = DIGITOS[UM]. //Variable para guardar las unidades de mil. //Desactiva todos los displays. C = (Numero-UM*1000)/100. //Activa en alto el primer display delay_ms(10). //Código del dígito 7 0x7F. //Retado de 10m segundos PORTA=0.F2=1. //Código del dígito 9 }. //Función para visualizar el display dinámico. //Código del dígito 2 0x4F.F1=1.

//Se incrementa el valor de Número. //Configura el puerto A como salida PORTA = 0. //y se reinicia en 0 si es 10000. TRISB = 0. 7404. 7SEG-MPX4-CC. if( Numero==10000 ) //Se evalúa si Número vale 10000 Numero=0. //Variable de conteo. Finalmente se construye el siguiente circuito: Circuito 5-2 49 . VerDisplay( Numero ). int Numero=0. //Se reinicia el conteo de N.void main ( void ) { unsigned short N=0. if( N==12 ) { N=0. //Se desactiva todos los displays while( 1 ) //Bucle infinito { //Se visualiza el valor de Número. //Configura el puerto B como salida TRISA = 0. RES. N++. } } } Al terminar y compilar el programa se realiza la simulación en ISIS. Numero++. con los siguiente dispositivos: 16F84A. //Está función dura aproximadamente 40m segundos. //Se cuentan 12 incrementos en N para hacer un incremento //en Número aproximadamente cada 500m segundos.

Además del código ASCII. Estos displays cuentan con un bus de datos y un bus de control. 50 . 5. los displays LCD admiten graficar hasta 8 caracteres diseñados por el desarrollador. para el manejo de estos dispositivos el compilador MikroC PRO. La apariencia física y la vista en ISIS de estos LCD es la que se puede apreciar en las siguientes gráficas: Figura 5-4 Los displays LCD. pero la velocidad de trabajo es mayor. La librería predefinida en MikroC PRO. permiten graficar los caracteres contemplados en el código ASCII. La conexión de 8 bits implica una mayor cantidad de cables para su uso. por consiguiente la conexión de 4 bits minimiza las conexiones pero disminuye la velocidad de trabajo. funciona con la configuración de 4 bits. pero es posible configurar las conexiones con solo 4 bits. otra característica fundamental de los LCD. son módulos prefabricados que contienen controladores incluidos. es la conexión del bus de datos.2 Display LCD de caracteres Los display de caracteres LCD. tiene una librería predefinida para el control de estos LCD. físicamente tienen 8 bits.Para fines prácticos los inversores 7404 se pueden remplazar por un arreglo de transistores como se ven en la siguiente imagen: Circuito 5-3 Cuando la simulación este en marcha el circuito debe mostrar un conteo de 0 a 9999 con una cadencia de 500m segundos entre cada incremento.

minimiza este trabajo dado que está se encarga de hacer todas estás configuraciones. está paleta se identifica con una pestáña que tiene el titulo: Library Manager. haciendo mucho más simple el trabajo del desarrollador. Al picar está pestáña se despliega un menú que muestra las diferentes librerías habilitadas para el microcontrolador que se está trabajando. se debe picar la paleta de herramientas ubicada en la parte derecha del programa. Lcd_Init(). requiere del uso de instrucciones y comandos secuénciales para la configuración y desempeño del display. posteriormente se puede picar cualquiera de las funciones que contiene la librería para ver la ayuda correspondiente. sin embargo la librería de MikroC PRO. se requiere definir los pines de conexión y la ejecución de la función predefinida de inicio del LCD. En este nuevo menú se identifica el ítem Lcd. Como primer paso para el uso del LCD. La apariencia visual de está paleta se puede apreciar en la siguiente imagen: Figura 5-5 La implementación del display LCD. Para cumplir este objetivo se usa la siguiente declaración de constantes: 51 .Para ver la librería predefinida para este dispositivo y otros que tiene MikroC PRO. La definición de los pines de conexión la asume el desarrollador de forma arbitraria según su criterio.

entre otros. //Bits de configuración TRIS sbit LCD_RS_Direction at TRISB4_bit. La referencia LM044L en ISIS corresponde a un LCD de 4x20. 2x8. sbit LCD_EN at RB5_bit.//Pines de salida para el LCD sbit LCD_RS at RB4_bit. Para iniciar la simulación del display LCD. Los displays de caracteres se fabrican en diversas formas y colores se pueden conseguir con pantallas de color verde. azul. y el PIC 16F84A en el simulador ISIS. el display cuenta con un tercer pin de control denotado como WR. La declaración de la función main debe quedar de la siguiente forma: void main( void ) { Lcd_Init(). sbit LCD_D4 at RB0_bit. Para fines de la librería este pin no es requerido. sbit LCD_D6_Direction at TRISB2_bit. sbit LCD_D4_Direction at TRISB0_bit. sbit LCD_D5_Direction at TRISB1_bit. 16 columnas estos se conocen como 2x16. Por último se debe invocar la función de inicio del display dentro de la función main después de la configuración de puertos. Finalmente se realizan las conexiones como se muestra en el siguiente circuito: 52 . y de la misma forma se pueden encontrar de 1x16. while(1) //Bucle infinito. posicionándose en la primera fila y primera columna. con 4 bits de datos y 2 bits de control. //Inicio del LCD. sbit LCD_D6 at RB2_bit. Los pines de la LCD. Para cambiar los pines solo se requiere cambiar el nombre de las declaraciones del ejemplo anterior. se inicializa y debe quedar listo para empezar a graficar caracteres. 2x20. 4x20. Para los ejemplos empleados en este capítulo se usará el display de 4x20. este pin permite leer si el display está ocupado realizando alguna operación o permite escribir comandos y caracteres. Se fabrican con distribuciones de caracteres de forma matricial como 2 filas. 2x16. mostrando el cursor parpadeante. sbit LCD_EN_Direction at TRISB5_bit. y amarillo. sbit LCD_D5 at RB1_bit. { } } Terminada la edición del código anterior el LCD. y simplemente se conecta a referencia. sbit LCD_D7 at RB3_bit. no se deben configurar. sbit LCD_D7_Direction at TRISB3_bit. Como se puede ver en la declaración inicial solo se necesitan 6 pines para gobernar el LCD. está tarea ya la realiza la función: Lcd_Init(). se debe buscar el dispositivo LM044L.

pero para fines de la simulación no tiene efecto. para este objetivo se pueden usar cuatro funciones. en síntesis. este pin funciona como controlador de contraste de la pantalla. Dos de estas funciones permiten imprimir caracteres. cuanto más negativo sea el voltaje en Vee más oscuro será el contraste en la pantalla. y las otras dos imprimen cadenas 53 . este pin puede ser conectado a referencia para generar el mayor contraste. Para fines prácticos el contraste se puede ajustar por medio de un potenciómetro como se ve en la siguiente circuito: Circuito 5-5 El paso siguiente es imprimir en la pantalla información. algunas pantallas de gran tamaño requieren de un voltaje negativo externo para el control de este contraste.Circuito 5-4 El display LCD cuenta con un pin denominado vee.

Lcd_Chr_Cp(„a‟). Para imprimir los caracteres se puede hacer de dos maneras. incrementa automáticamente el cursor en una posición.2. { } } Después de correr la simulación se debe observar lo siguiente en la pantalla del LCD: Figura 5-6 Para realizar la impresión de caracteres por medio de una coordenada fila. en la columna column y en la fila row. la primera simplemente imprime los caracteres en el orden consecutivo que asume el display. //Inicio del LCD. char out_char). Lcd_Chr_Cp(„H‟). y la siguiente función imprime los caracteres en la fila y columna que se designe por el desarrollador. cuando está función es invocada imprime en la pantalla el carácter correspondiente al código ASCII. char column. while(1) //Bucle infinito. se implementa la función: Lcd_Chr(char row. está función imprime el carácter out_char. Lcd_Chr_Cp(„l‟). Lcd_Chr_Cp(„o‟). Para contextualizar el funcionamiento de esta función observe y analice el siguiente ejemplo: void main( void ) { Lcd_Init(). Con la impresión de un nuevo carácter la pantalla LCD. 5.de texto.1 Funciones para imprimir caracteres La primera función de impresión de caracteres es: Lcd_Chr_Cp(char out_char). columna. que está en el parámetro de entrada out_char. En el siguiente ejemplo se puede ver cómo usar está función: 54 . //Estás funciones imprimen letra a letra la palabra “Hola”.

„4‟). Lcd_Chr_Cp(„o‟). 6. while(1) //Bucle infinito. //Imprime el carácter 3. o en un punto arbitrario por medio de las coordenadas de filas y columnas. para esto se pueden imprimir cadenas de texto en el punto donde está el flujo de la impresión del LCD. Lcd_Chr_Cp(„a‟). en la fila 4.void main( void ) { Lcd_Init(). 7. //Inicio del LCD. „3‟). Lcd_Out_Cp(“Hola Mundo…”). Para hacer la impresión de una cadena de texto en el punto de impresión se utiliza la siguiente función: Lcd_Out_Cp(char *text). 55 . { } } Después de ejecutar la simulación se debe tener la siguiente vista en la pantalla del LCD: Figura 5-7 5. Lcd_Chr( 1. columna 9 while(1) //Bucle infinito. 8.2 Funciones para imprimir cadenas de texto El uso de cadenas de texto es similar a las dos funciones anteriores. //Inicio del LCD.2. en la fila 1. Lcd_Chr_Cp(„H‟). „1‟). columna 8 Lcd_Chr( 4. „2‟). Lcd_Chr_Cp(„l‟). //Estás funciones imprimen letra a letra la palabra “Hola”. columna 7 Lcd_Chr( 3. //Imprime el carácter 1. en la fila 3. en la fila 2. columna 6 Lcd_Chr( 2. //Imprime el carácter 2. 9. Para el uso de está función observe y analice el siguiente ejemplo: void main( void ) { Lcd_Init(). está cuenta con un único parámetro de entrada que es un apuntador a la cadena de caracteres en este caso la función la denota como text. //Imprime el carácter 4.

while(1) //Bucle infinito. Columna 1” ). Columna 2” ). Lcd_Out( 2. que hacen referencia a las filas y las columnas respectivamente. Para comprender de forma clara el funcionamiento de esta función observe y analice el siguiente ejemplo: void main( void ) { Lcd_Init(). “Fila 2. 1. las cadenas constantes se denotan por medio de las dobles comillas al inicio y al final del texto por ejemplo: “Hola Mundo…”. el formato variable se declara con la forma: char Text[20]=”Hola Mundo…”. Está función trabaja de forma similar a la anterior. { } } Después de compilar y simular este ejemplo se debe observar una vista en la pantalla del LCD como la que se puede apreciar en la siguiente figura: 56 . “Fila 1.. Después de correr la simulación se bebe ver una vista como la que aparece en la siguiente figura: Figura 5-8 Para imprimir una cadena de caracteres con una coordenada como punto de inicio se implementa la función: Lcd_Out(char row. 2.{ } } La implementación de está función puede hacerse con cadenas de caracteres constantes o variables. y column. //Inicio del LCD. char column. Lcd_Out( 1. char *text).. con la diferencia que se incluyen los datos de row.

un sensor como: temperatura.Text ). char *output). que es un valor entero a visualizar. y output. char Text[20]. { } } La impresión de los números enteros en la cadena de texto con está función siempre asume un campo fijo de 7 caracteres. Si se requiere visualizar un valor entero se usa la función: IntToStr(int input. como la que se puede ver en la siguiente figura: 57 . Para esto se puede utilizar la librería: Conversions. está librería cuenta con funciones que hacen la conversión de un valor numérico a una cadena de caracteres. //Declaración de una variable entera con valor inicial 123. //Función que hace la conversión. humedad.3 Impresión de valores numéricos La impresión de valores numéricos es de vital importancia en muchos de los desarrollos. Después de compilar y simular el programa se tendrá una vista en ISIS. que es el apuntador a una cadena de caracteres donde se quiere escribir el forma de texto el valor que contiene el valor intput.Figura 5-9 5. Lcd_Init().. while(1) //Bucle infinito. presión. Por ejemplo cuando se desea visualizar. el estádo de una variable. //Cadena de caracteres para impresión de datos. Para entender este tipo de conversión observe y analice el siguiente ejemplo: void main( void ) { int ENTERO=123. o cualquier otro que se esté manipulando. es decir que si el número tiene menos de 7 dígitos el resto del texto se completa con espacios vacíos. IntToStr( ENTERO. está función tiene dos parámetros de entrada que son: input. Lcd_Out_Cp(Text). //Inicio del LCD. //Impresión del texto en la pantalla LCD. Para lograr este objetivo se puede recurrir a funciones de conversión predefinidas por el compilador.2.

//Declaración de una variable entera con valor inicial 123. Para realizar el siguiente ejemplo se debe cambiar la referencia del microcontrolador. A partir de este punto se usara el PIC 16F628A. char Text[20].Figura 5-10 La impresión de números con punto decimal se puede hacer con la función: FloatToStr(float fnum. FloatToStr( DECIMAL. pero cuenta con una capacidad mayor de memoria y con módulos integrados superiores como la USART. //Impresión del texto en la pantalla LCD. while(1) //Bucle infinito. Lcd_Out(2.76543. float DECIMAL=12.1. { } } Terminada la simulación se de tener una vista como la que se aprecia en la figura siguiente: 58 . //Impresión del texto en la pantalla LCD. no es suficiente para los siguientes ejercicios. Lcd_Out(1. //Cadena de caracteres para impresión de datos.1.Text ). esto se debe a que la capacidad de memoria del PIC 16F84A. IntToStr( ENTERO. //Función que hace la conversión decimal. //Función que hace la conversión entera. //Inicio del LCD. Lcd_Init(). //Declaración de una variable con punto decimal //inicializada en 12.Text ). char *str).76543.Text). que tiene la misma distribución de pines del 16F84A. Para contextualizar su funcionamiento observe el siguiente ejemplo: void main( void ) { int ENTERO=123.Text). La filosofía de funcionamiento de esta función es idéntica a la anterior que hace las conversiones de números enteros..

La edición y programación de estos datos resulta dispendioso y cuidadoso con las órdenes de bajo nivel que se beben dar a la pantalla LCD. o Lcd_Chr(1.1. requerida para programar el carácter en la pantalla.Circuito 5-6 El mismo proceso se puede seguir para otros tipos de variables como son: short con la función: ShortToStr(short input. Los caracteres son un conjunto de valores binarios que forman un mapa de bits. char *output). char *output). En conclusión se edita el carácter y posteriormente se pega el código en la fuente 59 .4 Creación de caracteres propios Las pantallas de caracteres LCD. 5.2. cuentan con la capacidad de almacenar hasta 8 caracteres diseñados por el desarrollador. de la pantalla LCD. char *output). y dan como resultado la imagen en la pantalla con una resolución de 5 x 7 píxeles. Sin embargo el compilador MikroC PRO. Los valores ASCII. char *output). por está razón deben ser reprogramados cada vez que se inicializa la pantalla LCD. Las variables unsigned long con la función: LongWordToStr(unsigned long input. Las variables unsigned int con la función: WordToStr(unsigned int input. cuenta dentro de sus herramientas con un editor para los caracteres diseñados por el desarrollador. si se desea imprimir el primer carácter diseñado por el usuario se hace con las funciones de impresión de caracteres: Lcd_Chr_Cp(0). char *output). En este editor de forma simple se edita el mapa de bits del carácter y este genera la fracción de código en lenguaje C. Las variables unsigned short con la función: ByteToStr(unsigned short input. para los caracteres diseñados por el usuario corresponde a los valores numéricos del 0 al 7.0). Por ejemplo. Las variables long con la función: LongToStr(long input.. La creación de caracteres propios creados por el usuario se guardan en un campo de memoria RAM.

Terminado este proceso se pulsa el botón GENERATE.del proyecto que se está desarrollando. Para usar este editor se busca el ítem Tools dentro del menú superior del programa MikroC PRO. Las siguientes gráficas muestran la vista del editor cuando se hace un carácter en la dirección ASCII 0: Figura 5-12 Como último paso se debe integrar la información entregada por el editor y el programa del desarrollador. y dentro de este se pica el submenú: LCD Custom character. para este fin observe el siguiente ejemplo: 60 . está acción despliega la ventana del editor que tiene la apariencia que se puede ver en la siguiente figura: Figura 5-11 En este editor el usuario pica los cuadros de la cuadricula de color verde y al terminar la edición del carácter. se selecciona el valor ASCII que se asignará al carácter por medio de la casilla Char:. y con está acción el editor mostrará la fracción de código para usar en el programa que se está desarrollando. este valor debe ser un número entre 0 y 7.

14.4. //Función para guardar en la memoria RAN del LCD //el carácter del editor. //Bucle for para guardar el mapa de bits. void CustomChar(char pos_row.27. { } } Transcurrida la edición compilación y simulación se debe tener la siguiente vista en la simulación de ISIS: 61 . sbit LCD_D7 at RB3_bit. //Impression del codigo ASCII 0.31.//Impresión del Carácter 0. //LCD_Cmd(LCD_RETURN_HOME). 1). for (i = 0. i++) LCD_Chr_Cp(character[i]). sbit LCD_D6 at RB2_bit. while(1) //Bucle infinito. sbit LCD_EN at RB5_bit.14. 0). sbit LCD_D6_Direction at TRISB2_bit. sbit LCD_D7_Direction at TRISB3_bit. sbit LCD_D4 at RB0_bit. //Dirección del carácter 0. LCD_Chr(pos_row. sbit LCD_D4_Direction at TRISB0_bit. } void main( void ) { Lcd_Init(). pos_char. //Pines de salida para el LCD sbit LCD_RS at RB4_bit. const char character[] = {17. sbit LCD_D5 at RB1_bit. sbit LCD_D5_Direction at TRISB1_bit. char pos_char) { char i. //Para fines prácticos está función puede ser ignorada.//Constantes creadas por el editor.21. está contiene el mapa de bits del carácter. CustomChar(1. LCD_Cmd(64). //Bits de configuración TRIS sbit LCD_RS_Direction at TRISB4_bit. //Inicio del LCD. sbit LCD_EN_Direction at TRISB5_bit. i<=7.0}.

sbit LCD_D7_Direction at TRISB3_bit. sbit LCD_D7 at RB3_bit.4. al 7. e invocar las funciones de impresión de caracteres cuando sea necesario.21.21. Para esto se debe tener presente el arreglo constante de datos que corresponden a los mapas de bits. 62 . //Bits de configuración TRIS sbit LCD_RS_Direction at TRISB4_bit. sbit LCD_EN at RB5_bit.31.0}.31.10. sbit LCD_D4 at RB0_bit. Para formalizar está idea observe y analice el siguiente ejemplo que imprime en la pantalla la imagen de 4 caracteres con forma de extraterrestres: //Declaración de constantes.Figura 5-13 Para optimizar el uso de la generación de caracteres arbitrarios el desarrollador puede simplemente grabar la memoria al principio de las configuraciones y después hacer la impresión de los caracteres. la dirección inicial que muestra el editor.4.14.30. const char Marciano2[] = {17. const char Marciano3[] = {17.0}.0}. //Dirección inicial 64.14.10. sbit LCD_D6_Direction at TRISB2_bit.21. editados del 0. sbit LCD_D6 at RB2_bit. sbit LCD_D5 at RB1_bit. sbit LCD_EN_Direction at TRISB5_bit. //Dirección inicial 88.14.27. Estos código son el resultado del editor de caracteres de //MikroC PRO. //de un marcianito animado. //Pines de salida para el LCD sbit LCD_RS at RB4_bit. sbit LCD_D5_Direction at TRISB1_bit. const char Nave[] = {24. sbit LCD_D4_Direction at TRISB0_bit.31.14.21.28. const char Marciano1[] = {17.0}. //Dirección inicial 80.14.30.31. representan los mapas de bits. //Dirección inicial 72.28.24.14.

0). Lcd_RAM_Car( 64. Con el fin de realizar cambios de características en el LCD. //Bucle para guardar el mapa en la RAM del LCD. for( i=0.//Se guarda el character 1 Lcd_RAM_Car( 72. 3). 3.//Se imprime el carácter Marciano 1. //Inicio del LCD. Marciano1 ). 2. { } } Terminada la compilación y corrida la simulación se bebe tener una vista como la siguiente en el simulador ISIS: Figura 5-14 La impresión de información en la pantalla LCD. } void main( void ) { Lcd_Init(). Para esto. Nave ). LCD_Chr(1.//Se guarda el character 4 LCD_Chr(1. incluso se puede simplemente desactivar el cursor. i<8. 1). se usa la función: 63 . Marciano2 ).//Declamación de función para guardar en RAM los caracteres. LCD_Chr(1. //Dirección inicial. const char *Mapa ) { char i. //Está función es diseñada por el desarrollador. //Se imprime el carácter Marciano 2.//Se guarda el character 3 Lcd_RAM_Car( 88. i++ ) LCD_Chr_Cp( Mapa[i] ). es posible sustituir el cursor por uno que no es sólido o desactivar el efecto parpadeante del mismo. while(1) //Bucle infinito. implica en algunos casos la necesidad de cambiar las características del cursor. //Se imprime el carácter Marciano 3. LCD_Chr(1. 2). 4. Marciano3 ). LCD_Cmd(IntDir). //Se imprime el carácter Nave.//Se guarda el character 2 Lcd_RAM_Car( 80. void Lcd_RAM_Car( char IntDir. 1.

Lcd_Cmd(char out_char). Para desactivar el cursor y hacerlo no visible se puede invocar el comando: Lcd_Cmd(_LCD_CURSOR_OFF). 5. y para ello cuentan con un bus de datos y un bus de control.. La apariencia física y la vista desde el simulador ISIS. Para colocar un cursor no sólido. horizontales o verticales armando en conjunto una imagen total. se puede apreciar en las siguientes imágenes: Figura 5-15 Estos dispositivos cuentan con 20 pines de los cuales 8 son el bus de datos. se usa el siguiente comando: Lcd_Cmd(_LCD_UNDERLINE_ON). en la cual se ingresan los comandos de configuración.3 Display LCD gráficos Los display gráficos. su particularidad principal es que permiten graficar un mapa de bits de mayor resolución. De la misma forma que el LCD de caracteres para fines de simulación los pines de contraste son ignorados. o raya al piso ( _ ). 6 son de control. son módulos similares a los LCD de caracteres. permiten imprimir fragmentos de líneas. 2 son de polarización. Sin embargo para fines prácticos los pines de contraste se deben instalar con un potenciómetro como se muestra en la siguiente figura: Figura 5-16 64 .. Los LCD gráficos. Estos se pueden adquirir comercialmente en tamaños como: 128x64. 128x128. Estos dispositivos de manera similar a los displays de caracteres requieren un protocolo de configuración. para activar el cursor sólido parpadeante se implementa el comando: Lcd_Cmd(_LCD_BLINK_CURSOR_ON). y 2 son para ajustar el contraste. por medio del parámetro: out_char. Para fines de estudio y análisis en este capítulo se demostrará el control sobre un LCD gráfico de 128x64 píxeles.. 240x320. píxeles entre otros.

El paso siguiente es cargar alguna gráfica.Para los ejemplos tratados en este capítulo. Como primera medida para configurar el módulo gráfico. con el microcontrolador 16F877A. Este dispositivo es un PIC de 40 pines y 8K bytes de memoria de programa. Posteriormente se realizan las conexiones respectivas como se puede apreciar en la siguiente gráfica: 65 . ubicada en la paleta de librerías. sbit GLCD_CS1_Direction at TRISB0_bit. brindan una librería especializada en el uso y control de este display gráfico. después de las configuraciones del PIC dentro de la función main. sbit GLCD_RST at RB5_bit. En el siguiente ejemplo se puede apreciar está declaración: // Declaración del puerto con el bus de datos. se creará un proyecto nuevo en MikroC PRO. para este ejemplo se dibujará en el display la siguiente imagen: Figura 5-17 La configuración antes citada se debe respetar en las conexiones físicas con el display gráfico. se implementan los dispositivos: PIC 16F877A. sbit GLCD_RS at RB2_bit. se definen de manera arbitraria los pines por los cuales se conectará el display. sbit GLCD_RW at RB3_bit. char GLCD_DataPort at PORTD. sbit GLCD_EN at RB4_bit. Para esto se cuenta con la librería Glcd. sbit GLCD_RS_Direction at TRISB2_bit. sbit GLCD_CS2_Direction at TRISB1_bit. //Declaración de los pines de control. Para inicializar el display se usa la función: Glcd_Init(). En la creación del circuito electrónico en el simulador ISIS. //Declaración de los registros de TRIS de control. sbit GLCD_CS1 at RB0_bit. sbit GLCD_RW_Direction at TRISB3_bit. sbit GLCD_RST_Direction at TRISB5_bit. sbit GLCD_CS2 at RB1_bit. y el módulo gráfico: AMPIRE 128x64. la cual es suficiente para realizar las primeras practicas con el LCD gráfico. Para esto se hace una declaración de bits constantes similar a la que se hace con el LCD de caracteres. sbit GLCD_EN_Direction at TRISB4_bit. Nuevamente las bondades del compilador MikroC PRO.

Estos archivos deben respetar las dimensiones de la pantalla gráfica en este caso de 128x64 píxeles. se puede usar un herramienta incluida en el MikroC PRO. denominada: GLCD Bitmap Editor. este editor se ejecuta buscando en el menú principal dentro del ítem Tools. y su submenú GLCD Bitmap Editor. Por último el archivo debe ser guardado en formato bmp. y color monocromático.Circuito 5-7 Las imágenes que se desean cargar deben ser editadas con anterioridad en algún editor de imágenes como el Paint de Windows. Para el ingreso del mapa de bits. Al correr está aplicación se debe tener una vista como la siguiente: Figura 5-18 66 .

248. 6.192. 96. 1. 0. 0. 0. 0. 0.129. 255.255. 0. 1. 0. 33. 1. 65. 0. 0. 0. 2. 1. 0.127.248. 0. 1. 0. 0. 48. 0. 0.240.bmp // GLCD Model: KS0108 128x64 // -----------------------------------------------------unsigned char const Imagen_bmp[1024] = { 255. 8. 0. 0.254. 33. 2. 0. 0. 33. 0. 0. 1.240. 0. 1.255. 1. 1. 1. 0. 0. 3. 0. 0. 0. 0. 0. 0.240. 63.126.240. 0. 0.192. 255. 255. 1. 0. 0. 0. 0. 16. 6.184.254. 0. 0. 1. 64. 0. 3. 65. 2. 0. 0. 16.192. 0.240.128. 0. 0. 0. 0.254. 0.128. 1. 2. 0. 1. 0. 0. 2. 16. 1. y se carga el archivo de imagen previamente diseñado en el editor de imagen.255. 0. 16. 4. 0. 1. 0. 1. 1. 0. 1. 0. 0. 193. 0. 7. 65. 0. 8.240. 0. Para el caso práctico de este ejemplo el resultado es el siguiente: // -----------------------------------------------------// GLCD Picture name: Imagen. 0. 33. 129. 0. 0. 1. 0. 0. 6. 0.240. 1.248.192. 33. 0. 0. 0. 0. 0. 0. 0. 1. 2.255.255. 0. 0. 48. 33.255. 1.129. 0. 0. 33. y se asegura la selección circular de 128x64 (KS0108).192.240. 0. 0.254. 4. 0. 1. 0. 0. 16. para agregar al código de programa en el inicio en la parte de la declaración de constantes. 0. 0. 1. 1. 0.112. 0. 1. 1. 0. 0. 0. 33. 1. 1. 2. 0.240. 1.129. 0.128.240.255. 0. 0. Está acción genera el código en lenguaje C. 240. 1. 6. 0.192. 1.252. 1. 0. 3. 1. 1.255. 0. 0.254. 33. 64. 0. 63. 0. 64. 0. 0. 0. 188.255. 0. 0.241. 0. 0.192. 0. 1.129. 0. 0.142. 0. 1. 0. 1. 0. 64. 0. 0. 54. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0.204.156. 0. y se pulsa el botón: Create CODE. 0. 0. 1. 32. 1. 0. 0. 255. 33.240. 1. 33. 1. 65.255. 0. 0. 0. 0. 0. 6. 1. 1. 1. 65. 1. 0. 0. 1. 142. 0. 1. 0. 12. 0. 0. 0. 0. 0. 0.255. 65. 16. 0. 0. 0. 0.112.252. 1. 3. 1. 16.252. 0. 3.112. 0. 3. 0.255. 0. Seguidamente se selecciona el círculo con la opción mikroC Code. 0.129. 1.127. 4. 0.192. 1. 0. 0. 32. 0. 255.238. 1. 0.240. 1. 0.255.255. 0. 0. 0.254. 67 . 1.192.128. 0. 0.255. 0. 0. 0. 1.129. 0.252. 33. 0. 8. 1. 16. 0. 0. 8. 0. 0. 0. 0.129.112. 2. 0. 0. 1. 33. 0. 0. 0. 0. 1. 16. 0.118. 3. 65. 0.240.En está aplicación se pica la pestáña: KS0108. 0. 3. 0. 1.240. 0. 0.129.129.128. 1. 65. 0. 1. 2. 0. 0. 3.182.255. 0. 3. 0.192. 1. 33. 1. 1. 2. 1.240. 0. 1.254. 0. 0. 1. 0. 8.129. 1.127. 0. 0.255. 0. 0. 0. 0.102. 32. 1.127. 0.248.240. 1.248. 0. 0. 1. 0. 0. 0. 1. 1. 65. 1. 0. 0. 14.192.248. 0. 0. 12. 0. 1. 54. 0. 33. 0. 0. 0. 0. 0. 33. 54. 0. 1. 1. 1.193. 1.252. 1. 1. 0. 0.255. 0. 1. 0. 1. 255. 0. 64. 0. 0. 32. 0. 0. 0. 0. 33. 0. 0. 0. 1. 33. 3. 0. 1. 0. 0. 0. 1.129. 0.192. 12. 4. 1. 65. 0.255. 31. 0. 0. 0. 1.192. 0. 0. 3. 0. 1.241. 33. 0. Está declaración es un arreglo de tipo: const unsigned char. 0. 1.240.192. 65.190. 0. 32. 0. 0. 1. 0. 0. 0. 0. 0.255. 4.128.241. 65. 0. Posteriormente se pulsa el botón Load BMP Picture. 1. 0. 65. 0.

0.128. 0. 0.128.128. 0.128. 0.255.128.128. 0. 0. 255.128. 0. 0. 0. 0. 4.128.128.128. 30. 32. 0. 0. 0. 3. 0. 0.128. 0.128.128. 0. 1.128. 1. 0.128. 3. 0.128. 3. 0.128. 0. 0.136. 0.128.128. 32. 0. 0. 0.255.255. 0.128. 0. 0.128. 0. 0. 64. 3. 2.128. 0.128. 0. 31.128.128.128.128.128. 0. 0. 3. 16.128. 32. 7. 0. 0. 32. 0. 0. 0. 0. 31.128. 0. 32. 32.128. 128.128.128. 0. 16.128. 0.255. 0. 0. 0.255.128.240. 0.129. 0. 255. 0. 0.128.128. 8. 0.128.128. 0. 0. 32.255. 0. 0. 0.128. 128. 0. 32.128.128.128. 0. 0. 0. 1. 3.136.130.131.240. La vista final del generador debe ser la siguiente: Figura 5-19 68 . 0. 0. 2.128. 64. 0. 0. 32. 0. 0. 0.128. 0. 32. 0. 8. 0.128. 16. 2. 3. 0. 64. 0. 48. 0. 0. 0. 32.128. 128.128.128. 32. 0. 0. 32. 0. 16. 0. 0.128. 0. 159. 0. 32. 0. 0. 8. 32. 128. 0.128. 0.159. 0. 0. 0.255 }.128. 0.255. 3. 0. 0. 0.128. 16. 0. 0.130. 0.128.128. 0.128. 64.128. 0.255. La declaración anterior contiene codificada la información del mapa de bits cargado en el editor.128.128. 0. 1. 0.128. 0. 0.240. 4. 8. 7.128.128. 1.128. 128. 32. 0. 0. 0.129.128. 0. 64. 128. 0. 0. 0. 0.255.136.128. 0.128. 2. 0. 0.128. 0. 0. 0.128. 0. 1. 1.128. 0. 8.128. 0. 0.128. 3.128.128. 0. 0. 0. 0. 0. 64. 0. 1. 0.128.128.128. 0. 0. 0. 0. 0. 0. 0.128. 0.128. 0.128. 0.128. 0. 0.128. 16. 0.132. 4. 8. 64. 64.128. 32.255. 32. 0.128. 3. 0.128. 1. 3. 255. 1. 32.128. 48. 3.128.128.128.128. 0.128. 0.128. 3. 32. 32.128.128. 0. 0. 0. 0.128.128. 0.128. 4.255. 0. 0. 0.128. 0. 0.128.131. 0.226. 0.128.128.128.255. 0.0. 0. 0. 0. 16. 0. 0.255.128. 2. 0.128. 32. 1. 0.128. 0. 16. 8. 3. 0.128. 0. 3. 0. 32.135. 48. 32. 32. 8. 0. 3.128.255. 0. 0. 0. 1.128. 0.255. 0. 255. 0.128.128. 0. 32. 1. 2. 255. 0. 0. 0. 4. 14. 0. 32. 0.132. 0.129. 0.128. 0.128.152. 0.240. 0.

1.128. 1.241. 0. 0.240. 1. 0. 129. 0. 0.248. 33. 0. 1. sbit GLCD_RST at RB5_bit. sbit GLCD_EN at RB4_bit. 0. 0. 0. 65.240. 0. 16. 0. 1.129. 0. 1. 0. 0. 0. 0. 1. //Declaración de los registros de TRIS de control. sbit GLCD_CS1_Direction at TRISB0_bit.241. 0.240. 0.240. 0. 1. 1. 1. 16. 0. 0.240.192. 1. 0. 2. 33.240. 0. 1. 33. 1. 3. 8. 0. 1. 0. 0.129. 0. 0. 1. 1. 0. 0. 8. 1. 0. 0.129. 0. 8. 0. 0. 1. 0. 1.128. 1.240. sbit GLCD_EN_Direction at TRISB4_bit. 1. 65. 1.241. 0. 0. sbit GLCD_RW_Direction at TRISB3_bit.112. 1. 1. 0. 1. 4.255. 0. 33. 0. 33.127. 0. 65. 2. 1. 0. 1. 1. 1.192. 0.129. 4.127. 1. 0. 1. 1. 0. 1. 33. 0. 65. 3. 1. 33.255. 0. 0. 0. 0.129. 12. 1. 0. 1. 1. 0. 0. 64. 0. 16.192. 0. 1. 2. 0. 3. 0.240. 33. 0.240. 0. 0. 0. sbit GLCD_RS at RB2_bit. 65. 1. 4.240. 0. 1. 0. 0. 64. 1. 0. 1.255. 193. 1.255. 0.112. 2.254. 65. 33.192. 0. 0.240. 0. 8. 65.192. 255. 0. 0. 1. 1. 1. 0. 0. 0.192. 0. 48.127. 64. 0. 0.128. 32. 0. 64. 1. 0.192. 0. 1. 33. 0. 1.255. 0.129.240. 1. 0. 65. 1. 33. 64.192. 0. 0. 0. 65. 63. 0. 33. 0. 1. 1. 65. 1. 1. 0. 0.192. 1. sbit GLCD_CS2_Direction at TRISB1_bit. 1.112. 0. 0. 0. 1.128. 0.255. 0. 33. 0. 69 . 0.240.129. 16. 0.193.128. sbit GLCD_RW at RB3_bit.248. 0. 0. 2. 1. 1. 0. 0. 1. sbit GLCD_RS_Direction at TRISB2_bit. 0. 65. 0. 2. 0. 1. 0. 1.127. 0. 63. 0. 1. 0.192.112. 1. 2. 0. 1. 0. 0. 0. 33. 33.Después de editar. 1. 0. 0. 0. 1. 0. 33. 32. sbit GLCD_CS1 at RB0_bit. 0. 65. 1.255. unsigned char const Imagen_bmp[1024] = { 255. 1. 0. 33. 0. 0. 255. 0. 16. 33. 0. 1. 0. 0. 1. 1. 0. 4. 0. 0. 32. 16.240. 1. 0. char GLCD_DataPort at PORTD. 16. 12.240. 1.240. 0. 0. //Declaración de los pines de control. sbit GLCD_RST_Direction at TRISB5_bit. 0. 0. inicializar.129. 1. 0. 0. 255. 1.129. 1.192. 2. 1. 0. 1.255. 0.192.129. 0. 0. 0. 2. 4. 32. 65. 255. 0. 0.192. 0. 0. 0. 0. 1. 0. 1. 0. 16.240. 0. 32. 1. 0.255. 0. 31. 1. y anexar el mapa de bits en el código fuente se tiene la siguiente fuente total: // Declaración del puerto con el bus de datos. 0. 48. 1. 8. 0.252. 240. 0.192.254. 33. 1. 1. 1. 0. 2. 0. 1. 1. 0. 1. 1. 0. 0. sbit GLCD_CS2 at RB1_bit. 1. 0. 0. 0.

3. 188. 159.128. 0. 3. 0.131. 0. 4. 0. 0. 0.128.128.128. 0. 0.128.128. 0. 0.128. 0.128. 0.255.135. 0.128. 0. //Graficación del mapa de bits. 0.136.182. 32. 0. 0. 16. 8. 0. 0.128. 0. 32. 1. 0. 0.128. 0.128. 0. 3. 0. 3.248. 0. 0.255. 0. 0. 0. 255. 0. 0. 8. 16.128. 0. 0. 0. 1. 0.128.128. 0.238. 6. 54.126.128. 0. 0.128.128. 0. 0. 0.128. 0.128. 0.128. 0. 0.131.128. 0. 0. 0.128.254. 0. 0.204. 4. 0. 0.255. 0.128.128. 0.128. 0. 4.255. 12. 3. 0. 8. 0.128. 0. 31.128. 0. 32.252. 2.128.128. 4. 0. 64. 0.136. 0.128.128. 7. 0.254. 255. 6.130. 3. 64. 48. 0.128. 0.128. 32. 8. 1. 0. 1.240.128. 16.128.128. 0.128. 0. 0.128. 32.255.128. 0.//Borra todo el display. 0. 0. 7. 32.255. 0. 2.128. 3.128. 0.128. 1.226.128. 0. 14. 96. 32. 0. 2.128. 0. 0. 1. 0. 0. 14. 0. 0. 0. 0.129. 0.128. 128.240.128.0. 0. 0. 0. 0. 0.128. 128. 0. 2. Glcd_Fill(0). 48. 0. 0. 0.128. 3. Glcd_Image(Imagen_bmp). 0.128. 3. 0. 0.255. 6. 0. 0.128. 3. 0.128. 0. 0.255. void main( void ) { Glcd_Init(). 0. 0.128. 0. 32. 0.255. 0. 16. 0. 3. 3. 0. 32.128. 0. 0. 1. 0. 0. 0.128.254.156.128. 0. 8. 3. 0. 3. 255. 0. 0. 0. 0.128. 0. 0. 0. 0. 0. 2. 0.255. 128. 0.128.128. 32. 4. 1.255.128. 3. 32. 142. 0.128.128. 0. 0. 7.128. 0.128. 1. 0. 0. 0. 0.128. 0. 0.248. 16. 1. 0. 0. 54. 0.240. 0. 32. 0. 0. 32. 0. 16. 0. 0.255. 64. 32.190. 0. 0. 32. 0. 1. 54. 1.254.255. 0. 3. 1. 0. 8. 0. 32. 0. 0. 0. 1.128.128. 0. 0. 32. 0. 0.130.128. 0.128. 6. 0.128.255. 255. 0. 0. 0. 0. 0.128. 0.128.128. 0.142. 0. 3.128.102.159.128.128.129. 0. 0. 0. 0. 0.255 }. 0. 0. 32.248.128.129. 16. 0. 0.128. 0. 32. 3.128. 0.240.128. 0. 3.128.132.128. 0. 0. 0. 0. 0. 3. 0. 32.152. 0. 0. 0. 0. 0. 3. 0.128.255. 0. 0.252. 0. 0. 0. 0. 0. 255.255. 0.128.128.129. 0. 32.255. 64. 0. 0.128.255. 0. 3. 0.128. 0. 2. while(1)//Bucle infinito. 0. 0. 0.255. 64.128. 0. 0. 0. 0. 0. 0. 8. 0.255.128.128.128. 0. 0. 0.252.128.255. 0. 0. 0. 48. 0. 0. 32. 0.255. 0.128. 64.128. 0. 16. 0. 255.128. //Inicialización del display gráfico. 3. 31.252. 0. 0.128. 16. 0. 0.128. 0. 0. 0. 0.136.128. 0. 0. 0. 0. 255.128. 0.128.128. 0.128.184. 0. 0.132. 128.128. 32.254. 0. 64.128. 0. 0.128. 0. 0. 0. 0. 0. 0. 3. 32. 0. 3. 1. 0. 0. 0. 0. 30. 0.128.255. 0. 0. 0.128.128. 0. { } } 70 . 128.128. 0. 0. 0.255. 0. 0. 0.128. 0.118. 6. 0. 1. 0. 64. 8. 32. 32.248. 128. 0. 0. 0.128.255.255. 0. 0. 0. 0. 32. 0.128. 0.

rellena el contenido de la pantalla con datos binarios iguales a 0. Además de cargar mapas de bits.La función: Glcd_Fill(0). Finalizada la creación del código. como el software de la siguiente imagen: Figura 5-21 71 . por ejemplo el PIC 18F452. y compilado el proyecto la simulación en ISIS debe mostrar una vista como la siguiente: Figura 5-20 La carga de mapas de bits. Por otra parte permite cargar una o más fuentes de texto previamente creadas para la impresión de datos.. y rectángulos. soporte. permite dibujar líneas. este tiene la misma distribución de pines del PIC 16F877A. Si se requiere anexar más imágenes y la memoria ya no es suficiente. el PIC 16F877A se puede desempeñar hasta 20MHz. este proceso equivale a borrar toda la pantalla del LCD. pero cuenta con una memoria superior y puede procesar su unidad hasta 40MHz. es decir que se pueden anexar tantas imágenes como la memoria de programa del PIC. el compilador MikroC PRO. solo está limitada por el tamaño de las mismas. pero es posible diseñarlas o incluso cargarlas de las fuentes convencionales del sistema operativo Windows. círculos. se debe cambiar el microcontrolador por uno que cuente con mas campo de memoria de programa. la librería del MikroC PRO. Para lograr este cometido se requiere un editor de fuentes para GLCD. Lamentablemente. no cuenta con un editor de fuentes para este fin.

int radius. con la diferencia en el parámetro del color que es correspondiente a color de relleno. unsigned short color). definida como: Font_Glcd_System5x7 font5x7. unsigned short color). Para hacer la impresión de puntos se implementa la función: Glcd_Dot( unsigned short x_pos. Para demostrar el funcionamiento de estás funciones observe y analice el siguiente ejemplo: void main( void ) { 72 . unsigned short color ). int y_center. unsigned short color). unsigned short color).. rectángulos. La impresión de rectángulos se logra con la función: Glcd_Rectangle(unsigned short x_upper_left.. está función imprime píxeles. La graficación de círculos se realiza por medio de la función: Glcd_Circle(int x_center. círculos. 1 para hacer la línea de color negro. unsigned short y_pos. unsigned short y_bottom_right. unsigned short y_upper_left. Para el siguiente ejemplo se usarán las opciones de graficación por medio de puntos. int x_end. int y_end.. Su funcionamiento es equivalente a la función Glcd_Rectangle. definida como: Font_Glcd_Character8x7 Estás fuentes.. contiene las siguientes fuentes de texto predeterminadas:     System3x5. 1 para hacer un píxel de color negro. está función respeta las mismas condiciones del parámetro del color de las anteriores funciones. definida como: Font_Glcd_5x7 Character8x7. o 2 para invertir el color sobre el cual se dibuja la línea. Para dibujar líneas se utiliza la función: Glcd_Line(int x_start. unsigned short y_bottom_right. en conjunto con las capacidades de graficación y la importación de mapas de bits hacen de los módulos LCD gráficos. El parámetro color puede valer: 0 para hacer un píxel de color blanco. con el color especificado en los parámetros de la función. e impresión de textos. La impresión de rectángulos con relleno es posible por medio de la siguiente función: Glcd_Box(unsigned short x_upper_left.Sin embargo la librería del MikroC PRO. la diferencia radica en que las coordenadas ingresadas son los puntos diagonales del rectángulo. unsigned short x_bottom_right. líneas. tiene las coordenadas del centro del circulo y el radio del mismo. Está función trabaja de forma similar a la función de graficación de líneas. int y_start. una poderosa herramienta para dar una presentación profesional a los proyectos requeridos por el desarrollador. unsigned short x_bottom_right. definida como: Font_Glcd_System3x5 FontSystem5x7_v2. o 2 para invertir el color sobre el píxel dibujado. unsigned short y_upper_left. está función contempla como parámetros de entrada las coordenadas de inicio y final así como el parámetro color este puede valer: 0 para hacer la línea de color blanco.

6. 32.2). //Retardo de 500ms Glcd_Line(6.//Rectángulo relleno entre (40. 3.32) y radio de 20 Glcd_Write_Text( "Hola mundo!". 20. 7. //Se dibuja un píxel en la coordenada (4. El resultado en ISIS.30) delay_ms(500).7) while(1)//Bucle infinito.4.35. //Retardo de 500ms Glcd_Dot(4. 1).// Circulo con centro en (64. Sin embargo los resultados de la simulación son muy cercanos al comportamiento real del dispositivo de graficación.30. 32). 7.70.1). 2). //Retardo de 500ms Glcd_Box(40. //Dibuja una línea de (6. Glcd_Fill(0).30.1).6) a (30. //Borra el display Glcd_Set_Font( Font_Glcd_5x7 . { } } Lamentablemente los dispositivos LCD gráficos de Proteus tienen una falla de diseño que hace que la simulación no funcione correctamente.5) y (70. este es un problema que se espera que sea corregido en versiones futuras de ISIS. //Retardo de 500ms Glcd_Circle(64.35) delay_ms(500).// Carga fuente de texto 5x7 delay_ms(500). se pueden apreciar en las siguientes figuras: Figura 5-22 73 .Glcd_Init(). 5. //Inicialización del display gráfico. //Impresión del texto en las coordenadas (3.4) delay_ms(500). sobre la simulación y una vista del comportamiento real sobre el LCD gráfico.5.

activo en alto o activo en bajo. La duración promedio de un rebote es de 1 a 5 milisegundos. Los pulsadores pueden ser normalmente abiertos o normalmente cerrados al efecto eléctrico ante el flujo de corriente. que se puede encontrar en la paleta o pestáña de librerías. Este retardo se debe aplicar después de detectar el primer cambio sobre el pulsador. Un retardo adecuado para este efecto es un tiempo mayor o igual a 10 milisegundos. hacen que el PIC pueda detectar cambios o estádos lógicos no definidos. teclados matriciales. Para el tratamiento de los pulsadores y la eliminación de los rebotes el compilador MikroC PRO cuenta con la librería Button.6 Teclados y sistemas de entrada de datos La interacción de los microcontrolados requiere de sistemas de entrada para los datos con el usuario. unsigned short active_state). Dado el veloz procesamiento de los microcontroladores estos efectos de ruido. Para evitar el efecto del ruido o rebotes se debe realizar una pausa en espera de la estábilidad del estádo lógico. e incluso teclados PS2 como los que usan los ordenadores de escritorio. La función retorna 0 si el pulsador no está activo y 255 si el mismo está activo. unsigned short pin. y active_state. unsigned short time. o paralelos. Para comprender este concepto observe la siguiente gráfica que muestra el comportamiento de este ruido: Figura 6-1 Cuando los cambios de tensión cruzan la zona indefinida.1 Uso de pulsadores La implementación de pulsadores es una de las alternativas de mayor popularidad en las interfaces de acción con los usuarios. Los pulsadores son de uso simple y de costo económico en la implementación. en la siguiente figura se puede apreciar la forma de configurar estás dos posibilidades: 74 . La instalación de los pulsadores se puede hacer de dos formas. generan cambios de alto a bajo y viceversa que el microcontrolador detecta como un pulso.. Está librería tiene la única función: unsigned short Button(unsigned short *port. 6. pin es el bit del puerto donde se conecta el pulsador. es el estádo lógico para el cual se desea hacer la activación del pulsador. Este capítulo se centra en el estudio de estos dispositivos. lo que indica que se debe hacer un retardo superior a este tiempo para esperar la estábilidad. Donde port es el puerto donde se conecta el pulsador. La implementación de estos dispositivos es propensa a los efectos de los rebotes o ruidos de tensión eléctrica cuando cambian de estádo. time es el tiempo en milisegundo de espera del ruido. Para este propósito se pueden usar dispositivos como pulsadores.

Para analizar y estudiar el uso de está librería se creará un nuevo proyecto con los siguientes dispositivos: PIC 16F877A.Circuito 6-1 La decisión de usar la activación en alto o en bajo. 75 . y LED-RED. TRISB=0xF0. es el siguiente: Circuito 6-2 El programa del microcontrolador respectivo es el siguiente: void main( void ) { //Configuración de puertos. Cabe recordar que de todos modos la activación final puede ser invertida con la función de la librería. El circuito correspondiente en ISIS. BUTTON. depende del desarrollador quien debe analizar la forma de mayor simplicidad en el momento de hacer el diseño. RES. while(1)//Bucle infinito. PORTB=0.

Los Dip-Switch. else PORTB. else PORTB.F0=1. son dispositivos mecánicos que contienen múltiples interruptores en un solo encapsulado.{ if( Button(&PORTB. 0) )//Evalúa el estádo del pulsador por RB7. de estos dispositivos es la siguiente: Figura 6-2 76 .2 Uso de Dip-Switch Los Dip-Switch.F0=0. //Apaga el pulsador si el pulsador está no activo. while(1)//Bucle infinito. PORTB=0. TRISB=0xF0. para este se evalúa el estádo del pulsador y se espera que se suelte el mismo: void main( void ) { //Configuración de puertos. y número de interruptores diferentes. El siguiente ejemplo conmuta el estádo del LED. { if( Button(&PORTB. } } } 6.F0=0. while( Button(&PORTB. 7.F0==1 )// Se conmuta el estádo del LED. 0) )//Evalúa el estádo activo del pulsador. Estos dispositivos permiten configurar de forma simple las características binarias de los sistemas microcontrolados. //Prende el LED si el pulsador está activo. 7. //activado en bajo.F0=1. colores. PORTB. } } Al correr está simulación el LED debe prender cuando se pulsa el botón. 7. //Se espera a que el pulsador este no activo. 0) ). La apariencia física y la vista en el simulador ISIS. y apagarse cuando se suelta. 100. { if( PORTB. 100. 100. PORTB. se consiguen comercialmente en tamaños.

TRISC = 255. se usan los siguientes dispositivos: 16F877A. LEDRED. PORTB = ~PORTC. Seguidamente se implementa el siguiente circuito en ISIS: 77 . RES.El uso de los Dip-Switch es similar a los pulsadores y se puede configurar de la misma forma que un pulsador con activación en alto o en bajo. } } Para ejecutar la simulación en ISIS. { //Se guarda el valor complementado del puerto C en el puerto B. while(1) //Bucle infinito. observe y analice el siguiente ejemplo: void main( void ) { //Configuración de puertos. PORTB = 0. Las configuraciones antes citadas se pueden apreciar en el siguiente circuito: Circuito 6-3 Para comprender y demostrar el uso de los Dip-Switch con los microcontroladores PIC. TRISB = 0. DIPSW_8.

minimizando el número de conexiones eléctricas.3 Uso de teclados matriciales Las aplicaciones con microcontroladores. requieren en algunos casos el uso de teclas de entrada de datos.Circuito 6-4 Al correr la simulación los LEDs muestran el estádo del Dip-Switch. La opción me mayor practicidad es el uso de teclados matriciales. 6. para datos numéricos. funciones e incluso caracteres de texto. En las siguientes imágenes se puede ver la apariencia física de un teclado matricial de 4x4 y su equivalente esquemático: Figura 6-3 78 . estos consisten en un arreglos de pulsadores alineados en filas y columnas.

como el visualizado en la siguiente figura: Circuito 6-5 79 . está librería cuenta con tres funciones para este propósito. Para estudiar las características de este tipo de teclados. Está función retorna inmediatamente el estádo del teclado cada vez que es invocada. RES.. de igual manera el análisis se puede hacer invirtiendo las columnas con las filas. y las funciones que usa son: Keypad_Init(void). don de 0 representa el teclado totalmente inactivo. La librería que permite el uso de teclados 4x4 es: Keypad4x4.Los teclados matriciales pueden tener dimensiones mayores en función de la necesidad del desarrollador. su diferencia vital es que si la función detecta una tecla pulsada. LM016L. este último es un display de caracteres de 2x16. La función: char Keypad_Key_Press(void). solo retorna su valor hasta que está tecla es soltada. que requiere de los siguientes dispositivos: PIC 16F877A. BUTTON. de tal manera que suplan las necesidades del usuario.. Sin embargo los teclados 4x4 permiten hacer una interfaz lo suficientemente completa para muchas de las aplicaciones. Los teclados especiales pueden ser fabricados con membranas plásticas con la cantidad de teclas y la distribución de las mismas. El compilador MikroC PRO. Para iniciar el proyecto se debe construir un circuito en ISIS. Está función. los resultados del 1 al 16 representan las 16 posibles teclas. La última función de la librería para el teclado matricial es: char Keypad_Key_Click(void). Su comportamiento es idéntico a la función anterior. retorna un valor de 0 a 16. este proceso determina cual es la tecla pulsada. está inicializa el uso del teclado en función del puerto designado para este fin. contiene una librería que controla y lee un teclado de 4x4. Los teclados matriciales funcionan activando una de 4 columnas y revisando cual de las filas se activan.. se mostrará un ejemplo con un nuevo proyecto en ISIS. De la misma forma se debe crear el proyecto nuevo en el compilador MikroC PRO.

el valor retornado por el mismo. ByteToStr(Tecla. sbit LCD_D5 at RB1_bit. sbit LCD_RS at RB4_bit. Para este fin observe y analice el siguiente ejemplo: //Declaración del puerto para el teclado 4x4 char keypadPort at PORTC. Lcd_Out(1. //Definición pines para el LCD. sbit LCD_D7_Direction at TRISB3_bit. } } Al correr la simulación de este programa en la pantalla se pueden ver los valores retornados por el teclado. char Text[20]. 1. //Inicialización del LCD. unsigned short Tecla. sbit LCD_D6_Direction at TRISB2_bit. void main( void ) { //Declaración de variables. //Definición de registros TRIS para el LCD. //Visualización del valor retornado por el teclado. //Se imprime texto. Lcd_Cmd(_LCD_CURSOR_OFF). //Conversión de entero a texto. //Configuración e inicialización del PIC. sbit LCD_RS_Direction at TRISB4_bit. sbit LCD_D6 at RB2_bit. leerá el teclado y mostrará en el LCD.El programa diseñado en MikroC PRO. sbit LCD_D4_Direction at TRISB0_bit. sbit LCD_D7 at RB3_bit. sbit LCD_D4 at RB0_bit. sbit LCD_EN_Direction at TRISB5_bit. while(1)//Bucle infinito. Text). sbit LCD_D5_Direction at TRISB1_bit. //Se apaga el cursor. Lcd_Init()."Tecla:").Text).1. { Tecla=Keypad_Key_Press(). Lcd_Out(2. sbit LCD_EN at RB5_bit. //Se lee el teclado y se guarda el resultado en Tecla. En la siguiente figura se puede apreciar un a vista de la simulación cuando se pulsa la tecla rotulada con el número 8: 80 .

Esto significa que los retornos de las funciones de lectura para el teclado son los siguientes datos con respecto a cada una de las teclas: Retorno de las funciones 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Equivalencia Ninguna tecla pulsada Tecla 1 Tecla 2 Tecla 3 Tecla A Tecla 4 Tecla 5 Tecla 6 Tecla B Tecla 7 Tecla 8 Tecla 9 Tecla C Tecla * Tecla 0 Tecla # Tecla D Tabla 6-1 81 . empezando con la tecla 1. y terminando con la tecla D.Circuito 6-6 Para el caso puntual de este ejemplo el teclado hace un barrido de izquierda a derecha y de arriba hacia abajo.

case 4: return „A‟. sbit LCD_EN_Direction at TRISB5_bit. //Tecla no pulsada. case 5: return „4‟. sbit LCD_D6 at RB2_bit. } } El programa final de está aplicación es el siguiente: //Declaración del puerto para el teclado 4x4 char keypadPort at PORTC. char LeerTeclado( void ) { //Estructura switch case para evaluar los valores retornados //por la librería del teclado. Para este fin observe la siguiente función que se hace por medio de una estructura switch case para decodificar el teclado. case 15: return „#‟. case 11: return „9‟. sbit LCD_RS at RB4_bit. case 2: return „2‟. //Definicion de registros TRIS para el LCD. case 10: return „8‟. case 8: return „B‟. sbit LCD_D7 at RB3_bit. switch(Keypad_Key_Press() ) { case 1: return „1‟. default: return 0. case 12: return „C‟. case 6: return „5‟. case 9: return „7‟. case 7: return „6‟. sbit LCD_D5 at RB1_bit. case 3: return „3‟. //Función para decodificar el teclado. 82 . case 14: return „0‟. case 16: return „D‟. sbit LCD_RS_Direction at TRISB4_bit. sbit LCD_D4 at RB0_bit. para corregir este aspecto se debe implementar una función que decodifique los valores entregados por el teclado y los convierta en el verdadero carácter que cada una de las teclas representa.Hasta este punto se pueden leer del teclado una serie de números pero estos no son equivalentes a la tecla pulsada. sbit LCD_EN at RB5_bit. case 13: return „*‟. //Definicion pines para el LCD.

} } void main( void ) { //Declaración de variables. 83 . //Función para decodificar el teclado. case 15: return „#‟. case 2: return „2‟."Tecla:"). Lcd_Out(1. char Tecla. case 4: return „A‟. //Configuración e inicialización del PIC. sbit LCD_D5_Direction at TRISB1_bit. } } Una vez editado. case 16: return „D‟. switch( Keypad_Key_Press() ) { case 1: return „1‟. case 6: return „5‟. sbit LCD_D6_Direction at TRISB2_bit. sbit LCD_D4_Direction at TRISB0_bit. case 9: return „7‟. Lcd_Cmd(_LCD_CURSOR_OFF).sbit LCD_D7_Direction at TRISB3_bit. //Se lee el teclado y su resultado se guarda en Tecla. case 7: return „6‟. el carácter correspondiente a la tecla que se está pulsando sobre el teclado.Tecla). Lcd_Init(). case 10: return „8‟. case 5: return „4‟. //Se imprime texto.1. case 3: return „3‟. default: return 0. compilado y simulado este programa. //Tecla no pulsada. //Visualización el valor retornado por el teclado. case 13: return „*‟. case 8: return „B‟. char LeerTeclado( void ) { //Estructura switch case para evaluar los valores retornados //por la librería del teclado. while(1)//Bucle infinito. Lcd_Chr(2. case 11: return „9‟. se debe ver en la simulación del display LCD. case 14: return „0‟. { Tecla=LeerTeclado(). 1. //Inicializa el LCD. //Se apaga el cursor. case 12: return „C‟.

e incluso existen algunos que son flexibles. el Vcc o poder. +5V Reloj Tabla 6-2 Pin DIN 2 4 5 1 La implementación de este tipo de teclados se hace por medio de dos funciones contenidas en la librería PS2.. El compilador MikroC PRO. un pin de datos seriales. lamentablemente estos teclados no son simuladles con el programa ISIS. la función configura e inicializa el teclado para poder ser leído en función de los pines que sean definidos con anterioridad como pin de reloj y pin de datos. lo que hace necesario la señal de reloj. unsigned short *pressed). y retorna 0. La otra función usada es: unsigned short Ps2_Key_Read(unsigned short *value. Está función debe ser invocada en la configuración del PIC. posee una librería especializada en el uso de estos teclados. dentro de la función main.. cuando una tecla es leída con éxito. La comunicación de estos dispositivos es de forma síncrona. Los teclados cuentan con un terminal PS2 o DIN. y de fácil transporte.4 Uso de teclados PS2 o Din Los teclados PS2 o Din son de uso común en los ordenadores de escritorio. está función cuenta con un parámetro de salida que retorna 1. el Gnd o referencia. La primera función es: Ps2_Config(). La distribución de pines de estos terminales es la que se puede apreciar en la siguiente gráfica: Figura 6-5 En la siguiente tabla se puede apreciar la distribución de pines de la figura anterior: Pin PS2 1 3 4 5 Finalidad Datos Referencia GND Vcc. Por está razón el ejemplo que se expone en este capítulo debe ser probado con los elementos de forma real. unsigned short *special.6. y un pin de reloj. La apariencia física de estos teclados es la siguiente: Figura 6-4 La lectura de estos teclados es hace por medio de un protocolo serial que la librería ya tiene implementada. cuando no se 84 . de MikroC PRO. en los cuales se pueden identificar 4 pines fundamentales.

es un apuntador a una variable de tipo unsigned short que especifica si la tecla que se está pulsando es de origen especial como: F1. El parámetro special. ESC. ENTER. F2. significa que el valor codificado en value. La función posee tres parámetros de entrada que permiten identificar las siguientes características: value. si no un código asignado a la tecla especial que está regido por la siguiente tabla: Tecla pulsada F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 Enter Page Up Page Down Backspace Insert Delete Windows Ctrl Shift Alt Print Screen Pause Caps Lock End Home Scroll Lock Num Lock Left Arrow Right Arrow Up Arrow Down Arrow Escape Tab Retorno en value 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Tabla 6-3 85 . este es un apuntador a una variable de tipo unsigned short en la cual se guarda el valor de la tecla pulsada en código ASCII. no es un código ASCII. etc.detecta ninguna tecla pulsada. Si está variable retorna un 1.

sbit LCD_D4 at RB0_bit. sbit LCD_EN at RB5_bit. sbit LCD_D6 at RB2_bit. Por último el parámetro: pressed. si el valor retornado en pressed.Si el valor retornado es 0 el código consignado en value es un carácter ASCII. observe y analice: //Definición de pines para el teclado PS2. sbit LCD_D7 at RB3_bit. 86 . sbit PS2_Clock_Direction at TRISC3_bit. sbit LCD_D5 at RB1_bit. sbit LCD_RS at RB4_bit. Para el próximo ejemplo se debe tener presente el siguiente circuito electrónico de prueba: Circuito 6-7 El código de programa para este ejercicio es el siguiente. sbit PS2_Clock at RC3_bit. //Definición pines para el LCD. es 1 la tecla fue pulsada. si retorna 0 la tecla fue soltada. determina si la tecla denotado por los anteriores parámetros es pulsada o soltada. sbit PS2_Data_Direction at TRISC2_bit. sbit PS2_Data at RC2_bit.

"Soltada"). &Pulsada_soltada)==0 ). mostrar los datos enviados por el teclado. Lcd_Cmd(_LCD_CURSOR_OFF). //La sentencia if evalúa el estádo de Especial y muestra //su resultado en el LCD."Especial"). 3. else Lcd_Out(1. 87 . 7. Lcd_Chr(2."Tecla:"). //Se apaga el cursor. sbit LCD_EN_Direction at TRISB5_bit. //La sentencia if evalúa el estádo de Pulsada y muestra en el LCD //el resultado. sbit LCD_D7_Direction at TRISB3_bit. unsigned short Tecla = 0.Tecla). //Se imprime texto. sbit LCD_RS_Direction at TRISB4_bit. y el estádo de las teclas especiales. sbit LCD_D5_Direction at TRISB1_bit. 1. while( Ps2_Key_Read( &Tecla. Lcd_Init(). } } Después de realizar la prueba de este ejemplo en display LCD. sbit LCD_D6_Direction at TRISB2_bit. while(1)//Bucle infinito."Pulsada"). else Lcd_Out(2. Pulsada_soltada = 0. void main( void ) { //Declaración de variables. //Visualización el valor retornado por el teclado. sbit LCD_D4_Direction at TRISB0_bit. Lcd_Out(1. //Inicializa el LCD. Especial = 0. &Especial. 3. if( Pulsada_soltada==1 ) Lcd_Out(1.1."ASCII "). 7. if( Especial==1 ) Lcd_Out(2.//Definición de registros TRIS para el LCD. //Configuración e inicialización del PIC. { //Este ciclo while espera a que una tecla sea activada.

está función genera la repetición de inicio. que está incorporado en unos pocos micros. La librería se reconoce como I2C. y otros micros. como el 18F2550. definido como HID o dispositivo de interfaz humana. La librería de MikroC PRO. y ajusta la velocidad de comunicación a la rata que denote el parámetro clock. algunos microcontroladores cuentan con módulos seriales como: I²C. y retorna 1. SPI. ordenadores personales. El módulo I²C.7 Comunicaciones seriales Los desarrollos con microcontroladores requieren en algunos casos la implementación de comunicaciones seriales para establecer transporte de datos con otros dispositivos tales como: memorias.. El protocolo I²C se puede usar para conectar diferentes dispositivos con direcciones diferentes en una topología en forma de bus. y el 18F4550. se caracteriza por tener una línea de datos bidireccional y una línea de reloj con drenador abierto. El protocolo SPI. Está librería cuenta con siete funciones que lideran el funcionamiento del módulo. e incluso otros microcontroladores. es ideal para la comunicación con memorias seriales como la 24LC64.1 Modulo serial I²C Algunos microcontroladores poseen este módulo y de la misma forma el compilador MikroC PRO cuenta con una librería especializada para el uso fácil de este protocolo. Con el fin de realizar las comunicaciones seriales. ordenadores. un bloque de dirección para los dispositivos en la red. permite establecer comunicaciones con unidades de almacenamiento masivo como las memorias SD. si el bus está disponible o retorna 0. Si ack vale 1 la confirmación es positiva y si es 0 la confirmación es negativa o NO ACK. 24LC512. La función: void I2C1_Repeated_Start(void). permiten establecer comunicaciones con módulos diferentes. cuenta con las siguientes funciones que hacen posible el dominio del protocolo I²C: La función: I2C1_Init(const unsigned long clock). si está ocupado. está función genera la condición de inicio y retorna 0 si no hay ningún error en la transmisión.. Cada uno de estos formatos de comunicación. USART. La función: unsigned short I2C1_Is_Idle(void). El módulo USART es uno de los más utilizados. inicializa los pines de comunicación en el PIC. El protocolo I²C. 24LC128. La función: unsigned short I2C1_Rd(unsigned short ack). y USB. de otro forma retorna un valor diferente de 0. La función: unsigned short I2C1_Start(void). un bloque de datos y una condición de fin. Por último el módulo USB. 88 . Por este motivo en estos dos pines se debe usar una resistencia de pull-up.. módulos de transmisión y recepción XBee. Está función lee un dato del bus de datos y envía la confirmación ack por el bus. entre otras.. De igual manera usa las condiciones de repetición de inicio y un bit de reconocimiento o acknowledge denotado como ACK. está función se usa para identificar si el bus de datos está ocupado. este módulo permite hacer comunicaciones con dispositivos como sensores.. módulos GPS. Este usa una condición de inicio. sensores. Por último este protocolo debe tener una velocidad de comunicación que está dada en bits por segundo. generalmente de 10KΩ. permite hacer la comunicación con un ordenador personal por medio de un puerto USB. 7.

y el instrumento de análisis del protocolo I2CDEBUGGER. 24LC00. DIPSWC_4. LED-RED. Genera la condición de final en el bus de comunicación. Para entender y estudiar el funcionamiento de este módulo.La función: unsigned short I2C1_Wr(unsigned short data ). está función envía el dato data por el bus y retorna 0 si la transmisión fue exitosa. en el icono de instrumentos: . En las siguientes gráficas se puede apreciar el protocolo de comunicación con la memoria 24LC00 para ser leída y escrita: Protocolo de escritura Figura 7-1 Protocolo de lectura Figura 7-2 Para probar el ejemplo se debe implementar en ISIS.. La función: void I2C1_Stop(void). El circuito para armar es el siguiente: 89 . el siguiente ejemplo se concentra en la lectura y escritura de una memoria serial 24LC00. DIPSWC_8. que tiene una capacidad de 16 direcciones. RES. BUTTON. este último se puede buscar en la paleta de herramientas ubicada en la parte izquierda del programa MikroC PRO.. o algo diferente de 0 si suceden errores. un circuito con los dispositivos: 16F877A.

{ DIRECCION = (~PORTA)&0x0F. //Protocolo de lectura de la dirección DIRECCION. I2C1_Start(). TRISE = 7. ADCON1 = 6. TRISB = 0. I2C1_Wr(DIRECCION). //Captura de la dirección. PORTB = 0. I2C1_Init(100000). 90 . //Configuración de puertos. unsigned short DIRECCION. I2C1_Repeated_Start(). while(1) //Bucle infinito.Circuito 7-1 El código de programa correspondiente para está simulación es el siguiente: void main( void ) { //Declaración de variables. unsigned short DATO. I2C1_Wr(0b10100001). I2C1_Wr(0b10100000). DATO=I2C1_Rd(0). //Inicio del módulo I2C a 100K bps.

está por defecto en casi toda comunicación es de 9600 bps. delay_ms(100). I2C1_Wr(DATO). Otra ventaja de este módulo es que cuenta con comunicación full-duplex. I2C1_Wr(DIRECCION). o sincronismo lo deben asumir. La comunicación síncrona cuenta con las siguientes características: un bit de inicio o de start. sobre los LEDs. Para este propósito se usan dos medios de transmisión dedicados.2 Módulo USART La USART.I2C1_Stop(). } } } Terminada la edición y la simulación en ISIS. //Sentencia if para evaluar si se pulsa el botón de grabar. //Visualización del DATO en el puerto B. y no requiere un medio para el reloj. PORTB = DATO. Cuando el botón GRABAR es pulsado. 4. es decir que puede transmitir y recibir información al mismo tiempo. if( Button( &PORTA. 0) ) { DATO = ~PORTD. y Datos para este PIC. y 1. I2C1_Start(). Los pines de Reloj.5 o 2 bits de fin o stop. //Retardo de 100 ms para graficar. I2C1_Wr(0b10100000). está característica lo hace muy apetecido dado que requiere un solo medio de transmisión para enviar información. que siempre es un 0 lógico. independiente mente cada uno de los elementos. se puede apreciar los valores consignados en la dirección que indica el DIP-SWITCH DIRECCION. Por último la velocidad de transmisión que debe estár definida con el mismo valor en los dos dispositivos que se comunican. de forma asíncrona. //Protocolo de grabación. y RC4 respectivamente. 50. delay_ms(50). 1. el transmisor y el receptor. uno solo para transmitir y uno solo para recibir. I2C1_Stop(). 8 o 9 bits de datos para el caso puntual de los PIC. DATOS es grabado en la dirección actual. el valor del DIP-SWITCH. //Retardo para esperar que la memoria grabe el dato. //Captura del valor del dato. sin embargo esto no es una regla puede ser mayor o menor. ya están predefinidos por el fabricante por los pines RC3. 7. En la siguiente gráfica se puede apreciar el comportamiento de la transmisión de un dato con este protocolo: 91 . es un módulo de comunicación serial estándar. La señal de reloj.

Figura 7-3 Este protocolo es utilizado por los ordenadores personales y otros dispositivos. usan un conector DB9. y el valor de un 1 lógico con -12 voltios. representa el valor de un 0 lógico con una tensión de +12 voltios. para el caso puntual de este los niveles de tensión eléctrica son diferentes al microcontrolador. que tiene 9 pines. y se conoce como RS232. Los ordenadores personales y los demás dispositivos que implementan el puerto RS232. los cuales se identifican en la siguiente figura: Figura 7-4 Pin 1 2 3 4 5 6 7 8 9 Uso DCD RXD TXD DTR GND DSR RTS CTS No usado Tabla 7-1 Para fines de comunicación asíncrona se debe configurar las conexiones de la siguiente forma: Figura 7-5 92 . El protocolo RS232.

está función estáblece si el búfer de transmisión se encuentra ocupado enviando un dato. está disponible en la paleta de librerías y cuenta con las siguientes funciones para su uso: UART1_Init(const unsigned long baud_rate). está función determina si hay un dato listo para ser leído. o 0 si el módulo está disponible para enviar un nuevo dato. char *Delimiter. define la longitud de la cadena de caracteres del delimitador de fin de cadena Delimiter. es decir que sirve para leer un dato de entrada. para esto retorna 1 si el búfer está ocupado. Está función inicializa el módulo USART. en el búfer de llegada del módulo.. de lo contrario no hay datos nuevos en el búfer. el parámetro Attempts. La función: UART1_Write(char _data). La implementación de este.. La función: char UART1_Read(). el apuntador Delimiter. La función: char UART1_Data_Ready(). La función: char UART1_Tx_Idle(). char Attempts). está función retorna el valor del búfer de entrada. y estáblece la velocidad de comunicación definida en el parámetro: baud_rate... si la función retorna 1 el dato puede ser leído. está función lee una cadena de caracteres y la guarda en el apuntador Output.. La función: UART1_Read_Text(char *Output. transmite el dato _data ingresado en el parámetro de entrada. por la USART. 93 .Para estáblecer y acoplar la comunicación de un PIC. con un dispositivo RS232. se debe hacer como se aprecia en la siguiente figura: Circuito 7-2 Para el uso de este protocolo de comunicación el compilador MikroC PRO. se debe usar un convertidor conocido como MAX232. es un apuntador a una cadena de caracteres que contiene el texto definido como fin de la cadena de texto de entrada. cuenta con la librería: UART..

Con estos dos elementos se construye el siguiente circuito: Circuito 7-3 El instrumento VIRTUAL TERMINAL. //Se transmite el ASCII del retroceso del carro. 94 . //Inicio del módulo USART. Para realizar el siguiente ejemplo se debe implementar en ISIS. Transmite una cadena de texto finalizada con el carácter nulo o 0. el número de bits de parada. Sin embargo el instrumento está configurado por defecto con una velocidad de 9600 bps. UART1_Write_Text("Pulse una tecla!. //Se transmite el ASCII del ENTER. //Se trasmite el texto de pulsar tecla. char DATO. UART1_Write_Text("Bienvenido al simulador:"). la velocidad de transmisión. Este instrumento permite editar las características de la comunicación como: el número de bits de datos. UART1_Write(10). entre otros. y el instrumento virtual: VIRTUAL TERMINAL. Está cadena de texto es entregada por medio del parámetro UART_text.La función: UART1_Write_Text(char * UART_text). Para comprobar el funcionamiento de este módulo se puede compilar y simular el siguiente ejemplo: void main( void ) { //Declaración de variables. UART1_Init(9600).."). //Se transmite el texto: de bienvenida. el circuito con los siguientes dispositivos: PIC 16F877A. y características listas para usar con la USART del PIC. UART1_Write(13)... es un simulador de comunicación serial y tiene un comportamiento similar a la herramienta HiperTerminal de Windows.

//Se transmite el ASCII del ENTER. DATO = UART1_Read(). UART1_Write(DATO). En las siguientes gráficas se puede apreciar las terminales de los conectores USB: Figura 7-6 Pin 1 2 3 4 Uso Vcc +5 Voltios Dato Dato + Gnd Tabla 7-2 95 . Sin embargo estos microcontroladores tienen características poderosas que los hacen ideales para la mayoría de las aplicaciones. UART1_Write(10). { //La sentencia if evalúa si un dato está listo para leer. 7. //Se imprime el texto de realimentación. UART1_Write(13). //Se transmite el ASCII del ENTER. } } } Durante la simulación el usuario puede enviar datos por el terminal virtual y ver la realimentación que el PIC. UART1_Write_Text("Usted pulso la tecla: ").UART1_Write(13). es de gran utilidad para realizar aplicaciones que impliquen la transmisión de datos con un ordenador personal. while(1)//Bucle infinito. UART1_Write(10). //Se transmite el ASCII del retroceso del carro. //Se transmite el DATO recibido. if(UART1_Data_Ready()==1) { //Se lee el DATO del bufer. emite. La desventaja más notable de este módulo es que solo está disponible en pocos microcontroladores como los PIC 18F2550 y 18F4550.3 Módulo USB La comunicación USB. //Se transmite el ASCII del retroceso del carro.

para este fin se requiere este dispositivo. del simulador ISIS. La opción de edición para los fusibles del PIC 18F2550. Y se debe usar un cristal de cuarzo adecuado a las características de diseño. es de suma complejidad en comparación con los micros de baja gama. es que puede entregar una alimentación de 5 voltios con una corriente máxima de 500mA. que permita correr la simulación del ejemplo. no es necesario implementar una fuente de poder externa. La ubicación por defecto de estos controladores es la siguiente: C:/labcenter Electronics/Proteus 7 Professional/USB Drivers/installer. Para usar este tipo de simulación se debe asegurar la instalación de los controladores USB. Edit Proyect… está acción despliega una ventana de edición la cual debe quedar de la siguiente forma: 96 .Una ventaja de la conexión USB. un LED LED-RED. y dentro de este menú se pica el submenú. y RES. El paso siguiente es realizar un circuito en ISIS. el puerto virtual USBCONN. esto significa que si el desarrollo no supera este consumo. se debe instalar un capacitor de 33uF conectado a referencia.exe Posteriormente se debe hacer el siguiente circuito: Circuito 7-4 Para fines prácticos en el terminal VUSB. Para hacer está instalación se busca el directorio de instalación de Proteus. y correr la instalación de los controladores. Para este caso se usa un PIC 18F2550. La configuración se edita picando el menú: proyect en el compilador MikroC PRO.

Figura 7-7 97 .

Hid_Enable(&Br. PORTB = 0. //Configuración de puertos. //Función de rutina USB //dentro de las interrupciones. unsigned short n. void interrupt( void ) { HID_Inte(). TRISB = 0. char DATO. INTCON2 = 0xF5. void HID_Inte( void ) { asm CALL _Hid_InterruptProc asm nop } //Función de interrupciones. es el siguiente: //Declaración de variables globales //como búfer de entrada y salida en el USB. INTCON3 = 0xC0. while(1) //Bucle infinito. PIE1 = 0.Para fines prácticos se debe asegurar en la simulación una fuente de reloj de 48Mhz y en un circuito real se usa un cristal de 4Mhz. //Configuración del PIC.//Función de inicio para el puerto USB. n<64.&Bw). El código fuente de MikroC PRO. } void main( void ) { //Declaración de variables. n++)Bw[n]=0. PIR2 = 0. e interrupciones. esto se debe a que el microcontrolador posee un PLL interno que multiplica la frecuencia. PIE2 = 0. { 98 . RCON. for(n=0. unsigned short Br[64]. //Ciclo for para inicializar el búfer en 0. INTCON = 0. PIR1 = 0.IPEN = 0. unsigned short Bw[64].

strcpy( Bw. dado que es un dispositivo de interfaz humana. está función deshabilita el uso del puerto USB. } } } La configuración de los proyectos que usen la librería USB. Para enviar datos a la simulación se requiere correr una aplicación que viene con las herramientas de MikroC PRO. y VARs.. //Se prende todo el puerto B DATO = Br[0]. Bw[22]=10. //Se apaga el puerto B.. unsigned short len).c. //Retardo para el parpadeo del LED. el nombre del producto. Bw[20]=DATO. También contiene el tamaño de los búfer de entrada y 99 . //Sentencia if para verificar el número bytes de entrada. if( n!=0 ) { PORTB = 255. "Se recibio el dato: " ).. está función escribe en el puerto USB. La función: unsigned short Hid_Write(unsigned *writebuff. n=Hid_Read(). los identificadores numéricos de fabricante y producto: VIP y PID. Bw[21]=13. y se pica el submenú.c contiene las definiciones con las que el ordenador reconoce el dispositivo HID. HID Terminal.h. se requieren tres archivos adicionales: USBdsc. que están consignados en el apuntador writebuff.. HID_Write(Bw. está función retorna la cantidad de datos listos para ser leídos del puerto USB. unsigned *writebuff). Está herramienta se encuentra en Tools. //Se lee el dato de entrada. //Se anexa el código ASCCI del ENTER en el búfer de salida. //Se anexa el código ASCCI del retroceso del carro en el búfer de salida. la cantidad de bytes definidos en el parámetro len. compatible con Windows. PORTB = 0. es tediosa pero finalmente solo recurren a cuatro funciones para su uso. está función configura el módulo USB. La función: void Hid_Disable(void).//Función para verificar los datos de entrada. delay_ms(100). y el uso de la librería USB. Incluye los parámetros de entrada readbuff y writebuff. Para garantizar la correcta compilación del proyecto.h. //Se envían los datos por el USB. //Se copia el dato de entrada en el búfer. y estáblece la comunicación con la computadora. Tiene parámetros como el nombre del fabricante. El archivo USBdsc. //Se copia el texto. Para verificar el funcionamiento de este ejemplo se debe correr la simulación y esperar a que el dispositivo USB. se instale automáticamente. Definit. en el búfer de salida. La función: unsigned char Hid_Read(void).64). que son apuntadores a los arreglos de memoria que guardan y transmiten la información por el puerto USB. Estás funciones son las siguientes: Hid_Enable(unsigned *readbuff.

Está acción permite guardar el archivo que deben ser archivados en el directorio donde se guarda todo el proyecto. además debe ser anexado al proyecto por medio del siguiente icono: de la misma forma se deben pegar en el directorio los archivos: Definit. El HID Terminal cuenta con una pestáña descriptor. Estos archivos contienen el siguiente código: Definit. En está pestáña se editan las características del dispositivo HID. y luego se pulsa el botón Save Descriptor. estos no requieren ser anexados solo deben ser pegados. y VARs.h.h.salida.h: #defineEP0_PACKET_SIZE 8 // EP0 In & Out transfer size #defineHID_PACKET_SIZE 64 // EP1 In & Out transfer size //****************************************** // // Definitions // //******************************************** #defineUSB_BUS_ATTACHED 1 #defineUSB_BUS_DETACHED 0 #defineUSB_DEVICE_DESCRIPTOR_LEN 0x12 #defineUSB_CONFIG_DESCRIPTOR_LEN 0x09 #defineUSB_INTERF_DESCRIPTOR_LEN 0x09 #defineUSB_ENDP_DESCRIPTOR_LEN 0x07 #defineUSB_HID_DESCRIPTOR_LEN 0x09 #defineUSB_DEVICE_DESCRIPTOR_TYPE 0x01 #defineUSB_CONFIG_DESCRIPTOR_TYPE 0x02 #defineUSB_STRING_DESCRIPTOR_TYPE 0x03 #defineUSB_INTERFACE_DESCRIPTOR_TYPE 0x04 #defineUSB_ENDPOINT_DESCRIPTOR_TYPE 0x05 #defineUSB_POWER_DESCRIPTOR_TYPE 0x06 #defineUSB_HID_DESCRIPTOR_TYPE 0x21 #defineUSB_ENDPOINT_TYPE_CONTROL 0x00 #defineUSB_ENDPOINT_TYPE_ISOCHRONOUS 0x01 #defineUSB_ENDPOINT_TYPE_BULK 0x02 #defineUSB_ENDPOINT_TYPE_INTERRUPT 0x03 #defineDSC_DEV 0x01 #defineDSC_CFG 0x02 #defineDSC_STR 0x03 #defineDSC_INTF 0x04 #defineDSC_EP 0x05 //****************************************** // 100 .

2007) // //******************************************************* #define ConfigDescr_OFFS 0x24 // 0x12 #define HID_Descriptor_OFFS 0x48 // 0x24 #define HID_ReportDesc_OFFS 0x76 // 0x3B #define USB_DEVICE_DESCRIPTOR_ALL_LEN 0x6A //****************************************************** // // USBdrv.// Definitions (added 19.h // //*************************************** // * UCFG Initialization Parameters #define _PPBM0 0x00 // Pingpong Buffer Mode 0 #define _PPBM1 0x01 // Pingpong Buffer Mode 1 #define _PPBM2 0x02 // Pingpong Buffer Mode 2 #define _LS 0x00 // Use Low-Speed USB Mode #define _FS 0x04 // Use Full-Speed USB Mode #define _TRINT 0x00 // Use internal transceiver #define _TREXT 0x08 // Use external transceiver #define _PUEN 0x10 // Use internal pull-up resistor #define _OEMON 0x40 // Use SIE output indicator #define _UTEYE 0x80 // Use Eye-Pattern test // * UEPn Initialization Parameters #defineEP_CTRL 0x06 // Cfg Control pipe for this ep #defineEP_OUT 0x0C // Cfg OUT only pipe for this ep #defineEP_IN 0x0A // Cfg IN only pipe for this ep #defineEP_OUT_IN 0x0E // Cfg both OUT & IN pipes for this ep #defineHSHK_EN 0x10 // Enable handshake packet #defineOUT 0 #defineIN 1 #definePIC_EP_NUM_MASK #definePIC_EP_DIR_MASK 0x04 #defineEP00_OUT 0x00 #defineEP00_IN 0x04 #defineEP01_OUT 0x08 #defineEP01_IN 0x0C #defineEP02_OUT 0x10 #defineEP02_IN 0x14 #defineEP03_OUT 0x18 #defineEP03_IN 0x1C 0x78 // (0x00<<3) | (OUT<<2) // (0x00<<3) | ( IN<<2) // (0x01<<3) | (OUT<<2) // (0x01<<3) | ( IN<<2) // (0x02<<3) | (OUT<<2) // (0x02<<3) | ( IN<<2) // (0x03<<3) | (OUT<<2) // (0x03<<3) | ( IN<<2) 101 .06.

used in USB_DEVICE_STATUS 102 .h // //*********************************************** // * Buffer Descriptor Status Register Initialization Parameters #define_BC89 0x03 // Byte count bits 8 and 9 #define_BSTALL 0x04 // Buffer Stall enable #define_DTSEN 0x08 // Data Toggle Synch enable #define_INCDIS 0x10 // Address increment disable #define_KEN 0x20 // SIE keeps buff descriptors enable #define_DAT0 0x00 // DATA0 packet expected next #define_DAT1 0x40 // DATA1 packet expected next #define_DTSMASK 0x40 // DTS Mask #define_USIE 0x80 // SIE owns buffer #define_UCPU 0x00 // CPU owns buffer // * USB Device States .To be used with [byte usb_device_state] #defineDETACHED_STATE 0 #defineATTACHED_STATE 1 #definePOWERED_STATE 2 #defineDEFAULT_STATE 3 #defineADR_PENDING_STATE 4 #defineADDRESS_STATE 5 #defineCONFIGURED_STATE 6 // * Memory Types for Control Transfer .#defineEP04_OUT 0x20 // (0x04<<3) | (OUT<<2) #defineEP04_IN 0x24 // (0x04<<3) | ( IN<<2) #defineEP05_OUT 0x28 // (0x05<<3) | (OUT<<2) #defineEP05_IN 0x2C // (0x05<<3) | ( IN<<2) #defineEP06_OUT 0x30 // (0x06<<3) | (OUT<<2) #defineEP06_IN 0x34 // (0x06<<3) | ( IN<<2) #defineEP07_OUT 0x38 // (0x07<<3) | (OUT<<2) #defineEP07_IN 0x3C // (0x07<<3) | ( IN<<2) #defineEP08_OUT 0x40 // (0x08<<3) | (OUT<<2) #defineEP08_IN 0x44 // (0x08<<3) | ( IN<<2) #defineEP09_OUT 0x48 // (0x09<<3) | (OUT<<2) #defineEP09_IN 0x4C // (0x09<<3) | ( IN<<2) #defineEP10_OUT 0x50 // (0x0A<<3) | (OUT<<2) #defineEP10_IN 0x54 // (0x0A<<3) | ( IN<<2) #defineEP11_OUT 0x58 // (0x0B<<3) | (OUT<<2) #defineEP11_IN 0x5C // (0x0B<<3) | ( IN<<2) #defineEP12_OUT 0x60 // (0x0C<<3) | (OUT<<2) #defineEP12_IN 0x64 // (0x0C<<3) | ( IN<<2) #defineEP13_OUT 0x68 // (0x0D<<3) | (OUT<<2) #defineEP13_IN 0x6C // (0x0D<<3) | ( IN<<2) #defineEP14_OUT 0x70 // (0x0E<<3) | (OUT<<2) #defineEP14_IN 0x74 // (0x0E<<3) | ( IN<<2) #defineEP15_OUT 0x78 // (0x0F<<3) | (OUT<<2) #defineEP15_IN 0x7C // (0x0F<<3) | ( IN<<2) //**************************************** // // USBmmap.

h // //*********************************************************** // * MUID = Microchip USB Class ID // * Used to identify which of the USB classes owns // the current session of control transfer over EP0 #defineMUID_NULL 0 #defineMUID_USB9 1 #defineMUID_HID 2 //*************************************************** // // USB9.See chapter 8 in the USB specification #define SETUP_TOKEN 0x0D #define OUT_TOKEN 0x01 #define IN_TOKEN 0x09 // * bmRequestType Definitions #define HOST_TO_DEV 0 #define DEV_TO_HOST 1 #define STANDARD 0x00 #define CLASS 0x01 #define VENDOR 0x02 #define RCPT_DEV 0 #define RCPT_INTF 1 #define RCPT_EP 2 #define RCPT_OTH 3 //****************************** // // USBcfg. USB 2.h // //*************************************** // * Control Transfer States #define WAIT_SETUP 0 #define CTRL_TRF_TX 1 #define CTRL_TRF_RX 2 // * USB PID: Token Types .#define_RAM 0 #define_ROM 1 #defineRemoteWakeup 0 #definectrl_trf_mem 1 //***************************************** // // USBctrlTrf.0 Spec Ref Table 9-4 #defineGET_STATUS 0 #defineCLR_FEATURE 1 #defineSET_FEATURE 3 103 .h // //***************************************************** // * Standard Request Codes.

extern unsigned char Byte_tmp_0[2].#defineSET_ADR 5 #defineGET_DSC 6 #defineSET_DSC 7 #defineGET_CFG 8 #defineSET_CFG 9 #defineGET_INTF 10 #defineSET_INTF 11 #defineSYNCH_FRAME 12 // * Standard Feature Selectors #define DEVICE_REMOTE_WAKEUP 0x01 #define ENDPOINT_HALT 0x00 //******************************************** // // HID.h // //******************************************** #defineHID_INTF_ID 0x00 // * Class-Specific Requests #defineGET_REPORT 0x01 #defineGET_IDLE 0x02 #defineGET_PROTOCOL 0x03 #defineSET_REPORT 0x09 #defineSET_IDLE 0x0A #defineSET_PROTOCOL 0x0B // * Class Descriptor Types #defineDSC_HID 0x21 #defineDSC_RPT 0x22 #defineDSC_PHY 0x23 //******************************************** VARs.0x4FF // //********************************************** extern unsigned char BDT[16]. extern unsigned char Byte_tmp_1[2].h: //********************************************* // // Bank 4 GPR Variables in region 0x400 . extern unsigned char param_Len. 104 . extern unsigned char usb_active_cfg. extern unsigned char SetupPkt[8]. extern unsigned char CtrlTrfData[8]. extern unsigned char usb_device_state. extern unsigned char USTAT_latch. extern unsigned char hid_report_feature[8]. extern unsigned char param_buffer[2].

extern unsigned int SetupPkt_W_Length. extern unsigned char byte_to_send[2]. extern unsigned char usb_stat. extern unsigned char USB_CD_Ptr[4]. extern unsigned char pSrc[2]. extern unsigned int SetupPkt_wValue. extern unsigned int HID_WriteBuff_Ptr. extern unsigned char SetupPkt_bDscIndex. extern unsigned char SetupPkt_bDevADRH. extern unsigned int HID_ReadBuff_Ptr. extern unsigned char active_protocol. extern unsigned int SetupPkt_W_Value. extern unsigned char SetupPkt_bFeature. extern unsigned char SetupPkt_bCfgRSD. extern unsigned int SetupPkt_W_Index. extern unsigned char hid_rpt_rx_len. extern unsigned char number_of_bytes_read. extern unsigned char USB_SD_Ptr[8]. extern unsigned char ctrl_trf_state. extern unsigned int SetupPkt_wLangID. extern unsigned char SetupPkt_bRequest. extern char FSR1reg[2]. extern unsigned char hid_report_in[64]. extern unsigned char pDst[2]. extern unsigned char SetupPkt_Recipient. extern unsigned char SetupPkt_bDevADR. extern unsigned char SetupPkt_bCfgValue. extern unsigned char wCount[2]. extern unsigned int SetupPkt_wIndex. extern unsigned int SetupPkt_wLength. 105 . extern unsigned char SetupPkt_bDscType. extern unsigned char SetupPkt_DataDir.extern unsigned char usb_alt_intf[2]. extern unsigned char idle_rate. extern unsigned char hid_report_out[64]. extern unsigned char byte_to_read[2]. extern char FSR2reg[2]. extern char FSR0reg[2]. extern unsigned char SetupPkt_RequestType. extern unsigned char ctrl_trf_session_owner. //******************************************** // Setup packet structures in EP0 Out Buffer //******************************************** extern unsigned char SetupPkt_bmRequestType.

extern unsigned char BD5ADRL. extern unsigned char BD6ADRH. extern unsigned char BD4ADRL. extern unsigned char BD7STAT. extern unsigned char BD6ADRL. extern unsigned char SetupPkt_bAltID_H. extern unsigned char SetupPkt_bIntfID. //*********************************** //*********************************** // Initialization Function //*********************************** void InitVARs(). extern unsigned char BD3ADRH. extern unsigned char BD0ADRH. extern unsigned char BD0ADRL. extern unsigned char BD1ADRL. extern unsigned char BD1ADRH. extern unsigned char BD2CNT. extern unsigned char BD5STAT. extern unsigned char BD1CNT. extern unsigned char BD7ADRL. extern unsigned char BD4ADRH. extern unsigned char BD7ADRH. extern unsigned char BD3STAT.extern unsigned char SetupPkt_bAltID. extern unsigned char BD1STAT. extern unsigned char SetupPkt_bIntfID_H. extern unsigned char BD4CNT. //************************************** // Buffer Descriptors Table //************************************** extern unsigned char BD0STAT. extern unsigned char BD4STAT. extern unsigned char BD3ADRL. extern unsigned char BD0CNT. extern unsigned char BD2ADRH. extern unsigned char BD2ADRL. extern unsigned char BD3CNT. extern unsigned char BD6CNT. extern unsigned char SetupPkt_EPNum. extern unsigned char SetupPkt_bEPID. extern unsigned char SetupPkt_EPDir. extern unsigned char BD6STAT. extern unsigned char BD5ADRH. extern unsigned char BD2STAT. //*********************************** 106 . extern unsigned char SetupPkt_bEPID_H. extern unsigned char BD5CNT. extern unsigned char BD7CNT.

Para este propósito. para este caso el nombre del dispositivo es: Prueba USB. Está aplicación permite enviar y recibir datos por medio del puerto USB. y seleccionar la pestáña: Terminal. se debe seleccionar el dispositivo. Posteriormente se escribe un carácter en la casilla de envió de datos y se pulsa el botón Send. Una vista de está acción en el HID Terminal tiene la siguiente apariencia: Figura 7-9 107 .c: Figura 7-8 Después de correr la simulación se debe correr el HID Terminal. Picando el nombre del dispositivo en la lista HID Devices. y el LED del circuito hace un parpadeo. Seguidamente el PIC retransmite un texto con el carácter enviado.Para la creación del proyecto el editor HID Terminal debe tener una vista como la siguiente antes de realizar la creación del archivo USBdsc.

el HID Terminal muestra en una ventana las características con las que está configurado en dispositivo HID. una vista de está acción se puede apreciar en la siguiente figura: Figura 7-10 108 .De igual manera si se pulsa el botón: info.

PORTC = (Dato>>8)&0x03. Sin embargo cabe denotar que el módulo de conversión interna de los microcontroladores es sólo uno. es posible realizar esta conversión con los módulos PWM incorporados en algunos microcontroladores. que por defecto son 0 voltios y 5 voltios. Para realizar este tipo de conversiones el compilador MikroC PRO cuenta con una librería definida: ADC para hacer la conversión. El proceso de conversión digital análogo es posible con elementos externos de fácil implementación o. //Se muestran los 2 bits de mayor peso por el puerto C.8 Conversión AD y DA La conversión análogo digital y digital análogo es un proceso por el cual se puede tomar o entregar muestras de una señal continua de voltaje. Esta función retorna el resultado de la conversión del canal especificado por el parámetro channel. y si la tensión es de 5 voltios el resultado de la conversión es 1023 de igual manera si la tensión es de 2.1 Conversión AD. incluso. Está librería cuenta con una sola función denominada unsigned int ADC_Read(unsigned short channel).5 voltios. while(1) //Bucle infinito. PORTB = Dato&0xFF. unsigned int Dato. //Inicialización de puertos. PORTC = 0. Esto significa que si una entrada análoga. o ADC. TRISC = 0. La conversión implementada por los PICMicro cuenta con una resolución de 10 bits. //Se muestran los 8 bits de menor peso por el puerto B. Para contextualizar el uso de está librería se puede observar y analizar el siguiente ejemplo: void main( void ) { //Declaración de variables. lo que permite obtener un número con un rango de 0 a 1023. 8. El uso de estás conversiones es de gran utilidad para realizar procesamiento digital de señales. o ADC Este proceso se realiza con el convertidor interno de los microcontroladores. Este módulo está incorporado en la mayoría de los microcontroladores de gama media y alta. { Dato = ADC_Read(0). } } 109 . tiene una tensión de 0 voltios su resultado es 0. La conversión análogo digital. PORTB = 0. que es proporcional a los valores de referencia. el resultado será 512. //Se hace conversión sobre el canal 0. Dependiendo de la complejidad del microcontrolador un PIC puede tener hasta 8 entradas de señal análoga. y los múltiples canales se pueden leer pero no al mismo tiempo. se puede realizar con algunos microcontroladores que tienen implícito un convertidor de este estilo. TRISB = 0.

8. de la diferencia de potencial que entrega el potenciómetro. Este filtro debe tener como frecuencia de corte un valor mucho menor a la frecuencia de muestreo en una proporción cercana a 10 veces. El cálculo de la frecuencia de corte de este filtro está regido por la siguiente fórmula: Fc  1 2RC Ecuación 8-1 El circuito a implementa de un filtro pasa bajas de primer orden es el siguiente: 110 . consiste en tomar la señal modulada por ancho de pulso y realizar la demodulación por medio de un filtro pasa bajas.Para la simulación de este ejemplo se deben implementar los dispositivos: 16F877A. y la segunda es la implementación de un arreglo externo de resistencias para obtener la diferencia de potencial. POT-HG.1 Conversión DA con PWM La conversión digital análogo. en el siguiente circuito de ISIS: Circuito 8-1 Después de correr la simulación los LEDs muestran en código binario del valor de la conversión análogo digital. 8.2. con el módulo PWM. dado que su objetivo es eliminar la frecuencia de muestreo.2 Conversión DA o DAC Este tipo de conversión es posible por medio de dos estrategias. la primera es usar el módulo PWM. LEDRED. del microcontrolador. RES.

Circuito 8-2 Implementación del filtro pasa bajas de segundo orden: Circuito 8-3 La ecuación para el cálculo en este filtro es: Fc  1 Ecuación 8-2 2RC 2 La frecuencia que se asigne a la portadora de modulación PWM. debe ser mucho mayor a la frecuencia de muestreo de la señal moduladora. Las muestras y la forma de onda se pueden apreciar en la siguiente figura: Figura 8-1 111 . Para demostrar el funcionamiento en está conversión se designarán 20 muestras que corresponden a un ciclo de una señal seno.

La implementación de la librería PWM. 146. se hace por medio de cuatro funciones que son: PWM1_Init(const long freq). con el siguiente código fuente: //Declaración de constantes //para la señal seno. 185. 127. este parámetro puede tomar valores de 0 a 255. 65. { //Bucle para recorres las 20 muestras //de un ciclo para la onda seno. const unsigned short Seno[20] = { 127.. La siguiente imagen ilustra el comportamiento de una señal PWM. 163. //Inicio de señal PWM. Para realizar la simulación se debe hacer un proyecto en MikroC PRO.. 76. está función inicializa el módulo PWM. está función estáblece el ciclo útil por medio del parámetro duty_ratio. 68. 90. Las cuales activan y desactivan respectivamente la señal PWM. La función: PWM1_Set_Duty(unsigned short duty_ratio). a la frecuencia de la portadora freq. 163. 189. 76. y 255 el 100% del ciclo útil. n<20. 177. donde 0 representa el 0%. la relación Fpwm y Tpwm: Figura 8-2 Por último se cuenta con las funciones: PWM1_Start().625K Hz. y PWM1_Stop(). //Configuración del módulo PWM a Fpwm=15. PWM1_Start(). 107 }. PWM1_Init(15625). for( n=0. n++ ) 112 . 107. 146.. while(1) //Bucle infinito. void main( void ) { //Declaración de variables. 177. unsigned short n=0. 68. 185. 90. en función del ciclo útil.

y el filtro pasa bajas. Con los anteriores elementos se construye el siguiente circuito: Circuito 8-4 Sobre la marcha de la simulación se debe apreciar como emerge el osciloscopio virtual mostrando la señal análoga de forma seno producto de la reconstrucción digital del PIC. delay_us(50). OP1P. el filtro cuenta con una frecuencia de corte de 1. } } } La ejecución de la simulación requiere de los dispositivos: 16F877A. Este circuito implementa un filtro pasa bajas para eliminar la frecuencia Fpwm portadora que en este caso es de 15. En futuras aplicaciones se recomienda para los cálculos de los filtros. y el instrumento virtual: OSCILLOSCOPE. por medio de las ecuaciones antes mencionadas. RES.6KHz. por medio del módulo PWM. Este último es un osciloscopio virtual de 4 canales simultáneos.5KHz aproximadamente. definir un valor arbitrario para el condensador C. entre 100nF y 100pF y calcular el valor de R. PWM1_Set_Duty( Seno[n] ). CAP. //Retardo de 50u seg. ideal para visualizar señales análogas e incluso digitales.{ //Cambio del ciclo útil del PWM. la apariencia visual del osciloscopio es la que se puede ver en la siguiente figura: 113 .

se pueden hacer conversiones de 8. Esto quiere decir que con el arreglo R-2R. de ahí su nombre R-2R. De la misma forma que se hace la conversión por PWM. aumentando el número de entadas D. solo basta con colocar el valor numérico a convertir en un puerto.Figura 8-3 8. Cabe resaltar que cuantos más bits. donde una es el doble de la otra. y el valor análogo hará presencia en la salida. que se limita a 8 bits. Este arreglo permite implementar un número indeterminado de bits.2.2 Conversión DA con arreglo R-2R El arreglo de resistencias R-2R. Otra característica de este tipo de conversión es que no requiere de librerías especializadas. La implementación del convertidor R2R. Las desventajas notables de este arreglo son. en la conversión a diferencia del módulo PWM. el incremento del hardware requerido y el uso de una cantidad mayor de pines de los puertos. 32. 64. es importante suprimir la frecuencia de muestreo para evitar componentes de señal impuras. En la siguiente gráfica se puede apreciar la configuración de las resistencias para una conversión de 8 bits: Circuito 8-5 Este arreglo puede ser expandido con la misma arquitectura para conseguir hacer un convertidor de mayor resolución. o lo que es igual el número de bits. 114 . permite hacer la conversión de un número binario a un valor proporcional de voltaje. posea la conversión mayor será su resolución y por consiguiente mayor la calidad de la señal reconstruida. 16. es de fácil desarrollo dado que consiste en un arreglo de conexiones de resistencias. o un número n de bits en función de la cantidad de pines disponibles en un microcontrolador.

146. n<20. 177. es el siguiente: Circuito 8-6 Para demostrar la aplicación de está técnica de conversión se modificará el ejemplo de conversión con PWM. para este fin observe y analice el siguiente código fuente: //Declaración de constantes //para la señal seno. 90.Una forma completa del convertidor con un acople de impedancia y su respectivo filtro pasa bajas de 1. 177. 76. n++ ) { //Cambio de muestra en el puerto B. PORTB = 127. 185. const unsigned short Seno[20] = { 127. TRISB = 0. 68. 127. 185. 107 }. while(1) //Bucle infinito. 189. unsigned short n=0.5K Hz. void main( void ) { //Declaración de variables. for( n=0. 65. 107. 146. 163. 115 . 90. 163. 76. 68. //Configuración de puertos. { //Bucle para recorres las 20 muestras //de un ciclo para la onda seno.

la salida del osciloscopio debe mostrar una gráfica similar. con el circuito que se puede apreciar en el siguiente circuito: Circuito 8-7 El resultado esperado en está simulación es igual a la simulación del convertidor con PWM. //Retardo de 50u seg.PORTB = Seno[n]. delay_us(50). } } } Editado y compilado este programa se procede a simular en ISIS. 116 .

1 Memoria EEPROM La memoria EEPROM es un campo relativamente pequeño que tienen los microcontroladores para que el desarrollador guarde información como configuraciones y datos variables que no deben perderse. PORTB = 0. está función graba la dirección definida en el parámetro address. while(1) //Bucle infinito. if( !PORTC. estás dos memorias son campos de información no volátil. La manipulación de esta memoria se puede hacer con MikroC PRO. //Se guarda el nuevo valor del puerto //en la dirección 0 de la EERPOM. La capacidad de memoria varía de un microcontrolador a otro dependiendo de su gama y tamaño..F0 ) { //Se incrementa el valor del puerto B. Para entender de forma concreta el funcionamiento de está librería se puede observar y analizar el siguiente código fuente: void main( void ) { //Configuración de puertos. //La sentencia if evalúa si se pulsa el botón.9 Memorias EEPROM y FLASH Las memorias EEPROM y FLASH de los microcontroladores son espacios de memoria que se pueden programar y borrar eléctricamente. { //Se muestra en el puerto B el valor //de la dirección 0 de la memoria EEPROM. 117 . es decir. La función: EEPROM_Write(unsigned int address. está cuenta con dos funciones que permiten leer y escribir una dirección de memoria. que si la energía de alimentación del microcontrolador se desconecta la información no se pierde. con el valor entregado en el parámetro data. Está información puede ser reprogramada alrededor de 100000 veces para el caso de la memoria FLASH y algo cercano a 1000000 de veces para el caso de las memorias EEPROM. PORTB = EEPROM_Read(0). De igual manera su información puede ser garantizada hasta por 40 años sin energía eléctrica. PORTB++. Está función lee y retorna el valor guardado en la dirección especificada en el parámetro address. Las funciones son: unsigned short EEPROM_Read(unsigned int address). EEPROM_Write(0.. por medio de una librería llamada: EEPROM. unsigned short data). 9. TRISB = 0.PORTB).

el cual contiene la dirección de la primera 118 . Durante la simulación se puede detener la misma.. dado que a medida que el tamaño del programa aumenta.F0 ). la posibilidad de usar la memoria FLASH. estás funciones son: FLASH_Write(unsigned address. en está función se ingresa el parámetro address.2 Memoria FLASH La memoria FLASH es el campo de información de mayor capacidad de los microcontroladores es en está memoria donde se guarda las instrucciones de todo el programa del PIC. y BUTTON. La implementación de está librería se fundamenta en dos funciones similares a las funciones de la memoria EEPROM. En está memoria se puede guardar pequeños archivos del orden de 1 o más kilo bytes dependiendo de la capacidad de memoria de cada uno de los microcontroladores. este valor es incrementado cada vez que se pulsa el botón. LED-RED. while( !PORTC. y del tamaño total del programa. 9. } } } La simulación de este ejemplo se realiza con los dispositivos: 16F877A. se reduce. de tal forma que al correr de nuevo la simulación se puede ver como el valor de la información se conserva.//Se espera a que se suelte el botón. RES. mostrará en los LEDs el valor de la dirección 0 de la memoria EEPROM. Esto significa que es de sumo cuidado usar está memoria ya que es posible dañar el programa principal si se altera alguna de sus instrucciones. Para correr la simulación se debe construir el siguiente circuito en ISIS: Circuito 9-1 El circuito al ser simulado. La programación de la memoria FLASH es de utilidad para guardar segmentos de información de tamaño relativamente grande. para ver el efecto de la suspensión de energía. unsigned int* data). y este es actualizado en la memoria EEPROM.

//Se configura la dirección inicial de la FLASH. Dir=0x0200. que contiene los datos que serán grabados en la memoria FLASH. UART1_Write_Text("Digite su archivo FLASH"). unsigned int buf[10]. //Se configura la dirección inicial de la FLASH. UART1_Write_Text("INIDIO DEL ARCHIVO:"). { //Sentencia if para evaluar si hay datos para leer //en el puerto serial. y el apuntador data. do { 119 . Para contextualizar el uso de está librería se puede crear y analizar el siguiente código de programa: void main( void ) { //Declaración de variables. PORTB = 1. UART1_Init(9600). //Configuración del puerto serial. char Dato. UART1_Write(10). está función retorna el valor guardado en la dirección definida por el parámetro address. Dato = UART1_Read(). unsigned int Dir. La siguiente función es: unsigned FLASH_Read(unsigned address). UART1_Write(10). //Bucle do while para leer el archivo //y enviar la información. //Configuración de puertos.dirección a grabar. Dir=0x0200. if( UART1_Data_Ready() ) { //Se lee el puerto serial.. //Bienvenida al programa. TRISB = 0. switch( Dato ) { //Caso de solicitud de envió del archivo //con el carácter # case '#': UART1_Write(13). while(1) //Bucle infinito. UART1_Write(13). UART1_Write(13). //Estructura Switch case para evaluar los datos de entrada. UART1_Write(10). PORTB = 0.

//Lectura de la dirección en FLASH. Buf ). //Se configura la dirección inicial de la FLASH. UART1_Write(13). Buf[0]=Dato. UART1_Write_Text("FIN DEL ARCHIVO. } } } } Para la simulación en ISIS. Dir=0x0200. default: //Caso por defecto en donde se guardan //los datos en la FLASH. se debe implementar los dispositivos 16F877A. Dato = buf[0]. //Incremento de la dirección. FLASH_Write( Dir. break. Dir++. UART1_Write(Dato). y el VIRTUAL TERMINAL. //Envió del Dato por el puerto serial. //Se guarda en FLASH el dato. //Carácter fin de cadena NULL."). UART1_Write(10). //Envió de eco por el puerto serial. UART1_Write(Dato). UART1_Write(10). Buf[1]=0. UART1_Write(13). buf[0]=FLASH_Read(Dir++). }while( Dato!=0 ). en un circuito como el que se puede apreciar en la siguiente figura: Circuito 9-2 120 .

8. Para el caso del PIC 16F877A. Las subdivisiones posibles para este timer son las siguientes: 1. En función de estás características el tiempo de desborde o disparo se puede calcular con la siguiente relación: P = 256x(Subdivisión)x4xTosc. 121 . 32. y 256. el mayor tiempo puede ser: P=256x256x4x1u. para crear tiempo de reloj menor. 128. por medio del pin RA4. y cuando el timer pasa de 255 a 0. algunos pueden tener hasta 4 Timers. donde Text. P=65. depende de la arquitectura y configuración que se asigne en los registros de control del Timer. De igual manera la señal de reloj puede usar una subdivisión. la cantidad de módulos Timer en cada microcontrolador depende de la gama del PIC. es el periodo de oscilación del reloj externo. este conteo puede ser de 8 o 16 bits. la arquitectura del Timer 0 es como se puede apreciar en la siguiente figura: Figura 10-1 Este módulo Timer. Para el caso de el Timer 0 su registro es de 8 bits. si el Timer 0 se configura con la fuente externa la relación es: P = 256x(Subdivisión)xText.536m Seg. 64. 2. se dice que el Timer se desborda o se dispara. donde Tosc es el periodo de oscilación del reloj del procesador. 16. Para un reloj de 4MHz. 4. o una fuente externa de reloj. El intervalo de tiempo con el cual se incrementa el Timer. esto quiere decir que puede hacer un conteo de 0 a 255. uno es el reloj propio del procesador. sin embargo la mayoría de los microcontroladores posee al menos uno que generalmente se denomina Timer 0. El uso y configuración de cada módulo Timer depende del desarrollador y del tamaño del conteo.10 Módulos Timer Los módulos Timer son unidades que permiten hacer el conteo de tiempos precisos. puede tener dos fuentes de pulsos de reloj.

PSA. y a sume la subdivisión de la tabla anterior. PS1. while(1) //Bucle infinito. el cual asumen la subdivisión 1. PS0. } } } 122 . está configuración obedece a la siguiente relación: Subdivisión Timer 0 PS2:PS1:PS0 Subdivisión 000 2 001 4 010 8 011 16 100 32 101 64 110 128 111 256 Tabla 10-1 Para seleccionar la subdivisión 1. PS0.//Configuración de puertos. { if(PORTB==0)//Se conmuta el valor del puerto B. cuando este bit vale 0. en los tres bits de menor peso. La calibración de la subdivisión del Timer 0 se hace con los bits. { if(TMR0==0) //Se evalúa si el Timer 0 vale 0. el bit PSA. La configuración de los bits PS0 al PS2.//Se espera a que el Timer 0 cambie de valor. se realiza sobre el registro OPTION_REG. y PS2. OPTION_REG=0b11000111. while(TMR0==0). PORTB = 0. PS1. cuando este bit vale 1. se debe configura el bit PSA. y el bit T0SC corresponde al sexto bit del mismo registro. se puede observar y analizar el siguiente código fuente: void main( void ) { TRISB = 0. //Configuración del Timer 0.La configuración del Timer 0 se logra con los bits: T0CS. es el siguiente: OPTION_REG Bit 5 Bit 4 Bit 3 T0CS T0SE PSA Bit 7 RBPU Bit 6 INTEDG Bit 2 PS2 Bit 1 PS1 Bit 0 PS0 Para demostrar el uso del Timer 0. y PS2. El mapa de bits del registro OPTION_REG. PORTB=1. else PORTB=0. corresponde al cuarto bit del registro OPTION_REG.

Para la simulación de este circuito en ISIS. y el osciloscopio virtual. cada 65. en el siguiente arreglo: Circuito 10-1 Al correr la simulación se puede apreciar en el osciloscopio. se implementan los dispositivos 16F877A. OSCILLOSCOPE.536m Seg. La vista del osciloscopio debe ser la siguiente: Figura 10-2 123 . que hay un cambio de estado en el pin RB0.

Los mapas de bits de estos registros en el PIC 16F877A son los siguientes: Bit 7 GIE Bit 6 PEIE Bit 5 TMR0IE INTCON Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 INTE RBIE TMR0IF INTF RBIF Bit 7 PSPIF Bit 6 ADIF Bit 5 RCIF PIR1 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 TXIF SSPIF CCP1IF TMR2IF TMR1IF Bit 7 - Bit 6 CMIF Bit 5 - PIR2 Bit 4 Bit 3 Bit 2 EEIF BCLIF - Bit 1 Bit 0 CCP2IF Bit 7 PSPIE Bit 6 ADIE Bit 5 RCIE PIE1 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 TXIE SSPIE CCP1IE TMR2IE TMR1IE Bit 7 - Bit 6 CMIE Bit 5 - PIE2 Bit 4 Bit 3 Bit 2 EEIE BCLIE 124 Bit 1 Bit 0 CCP2IE . cuando el timer se desborda. dentro de está función se debe evaluar cual de las interrupciones se dispara por medio de las banderas de cada interrupción. entre otras. cuenta con una función predefinida llamada: void interrupt(void). Las interrupciones pueden tener fuentes variadas que dependen de la gama y complejidad del microcontrolador.11 Interrupciones Las interrupciones en los microcontroladores son eventos programados que hacen que el PIC suspenda su rutina y ejecute un fragmento de programa asociado al evento. serial cuando un dato llega por la USART. Para la configuración de las interrupciones se deben activar los bits correspondientes en los registros de interrupción En el caso particular del PIC 16F877A se usan los registros: INTCON. Para la ejecución de las interrupciones el compilador MikroC PRO. Por ejemplo el PIC 16F877A cuenta con fuentes de interrupción como el Timer0. externa cuando se hace un cambio de flanco sobre el pin RB0. PIE1. está función debe ser declarada antes de la función main. cuando una conversión termina. PIR2. ADC. y PIE2. Cuando una interrupción se dispara la función interrupt es invocada automáticamente por el programa. PIR1. Cuando la rutina de la interrupción se termina el programa del PIC continúa ejecutando el flujo del programa en el mismo punto en donde se suspendió la ejecución.

La implementación de las interrupciones implica seguir un protocolo para la correcta configuración de las mismas. Primero se deben activar las interrupciones que se desean usar, por medio de los bit IE, como: TMR0IE, INTE, RCIE, entre otros. Las banderas de disparo de las interrupciones debe ser desactivadas, como: TMR0IF, INTF, RCIF, entre otras. Se deben activar las interrupciones periféricas, cuando está se requieran, por ejemplo las que están citadas en el registro: PIE1, y PIE2. Por último se debe activar el bit de interrupciones en general, que es: GIE. Para el siguiente ejemplo se usarán las interrupciones por: recepción serial, Timer 0, y externa, para este fin se puede observar y analizar el siguiente código fuente:

//Declaración de la función de interrupciones. void interrupt ( void ) { //Declaración de variables usadas en //la función de interrupciones. char Dato; //Se evalúa si la interrupción disparada es //por Timer 0, TMR0IF if( INTCON.F2==1 ) { //Se complementa el valor del bit RB1. if(PORTB.F1==1) PORTB.F1=0; else PORTB.F1=1; //Se apaga la bandera de Timer 0. INTCON.F2=0; }

//Se evalúa si la interrupción disparada es externa. // INTF if( INTCON.F1==1 ) { //Se complementa el valor del bit RB2. if(PORTB.F2==1) PORTB.F2=0; else PORTB.F2=1; //Se apaga la bandera de interrupción externa. INTCON.F1=0; }

//Se evalúa si la interrupción disparada por //recepción serial. if( PIR1.F5 ) { //Se lee el dato de entrada. 125

Dato = UART1_Read(); //Se envía información de confirmación. UART1_Write_Text("Dato de entrada: "); //Se envía el dato recibido. UART1_Write(Dato); UART1_Write(13); //Se envía código ASCII del enter. UART1_Write(10); //Se envía código de retroceso del carro. //Se apara la bandera por recepción serial. PIR1.F5 = 0; } }

void main( void ) { //Se configuran los puertos. TRISB = 0b00000001; PORTB = 0; //Se activa la interrupción por //recepción serial. PIE1 = 0b00100000; //Se desactivan las demás fuentes de interrupción. PIE2 = 0; //Se apagan las banderas de interrupción. PIR2 = 0; PIR1 = 0; //Configuración del Timer 0 a 65,535m Seg. OPTION_REG=0b11000111; //Configuración del puerto serial a 9600 bps. UART1_Init(9600); //Se activan las interrupciones globales, //por Timer 0, y externa. INTCON = 0b11110000; while(1) //Bucle infinito. { } }

Para la simulación de este ejemplo en ISIS, se implementarán los siguientes dispositivos: 16F877A, RES, BUTTON, LED-RED, y los instrumentos virtuales: VIRTUAL TERMINAL y OSCILLOSCOPE. Después de correr la simulación se debe poder interactuar con el virtual terminal, al enviar datos, con el pulsador se debe conmutar el estádo del LED, y la salida del osciloscopio debe mostrar una señal cuadrada con cambios cada 65,535m Segundos, de acuerdo con el Timer 0. Para correr la simulación se debe hacer el siguiente circuito:

126

Circuito 11-1

127

12 Sensores
La implementación de sensores en sistemas microcontrolados es de gran utilidad en muchas de sus aplicaciones, estos permiten obtener lecturas de variables como temperatura, aceleración, presión, humedad, velocidad, luminosidad, contraste, entre otras. Su aplicación es importante en sistemas de control, robótica, e instrumentación.

12.1

Sensor de temperatura LM35

El sensor LM35, es un dispositivo activo de 3 terminales que permite adquirir la temperatura ambiente en rangos de -55 a 150 grados Celsius o centígrados. Este dispositivo es de fácil implementación dado que solo cuenta con dos terminales de polarización, y una salida de voltaje directamente proporcional a la temperatura. Este sensor puede ser polarizado de 4 a 30 voltios y tiene una salida de 10m voltios por cada grado Celsius. La apariencia física del sensor y su distribución de pines, así como la vista en ISIS, son las que se pueden ver en las siguientes figuras:

Figura 12-1

Para realizar la lectura del voltaje de salida del sensor se implementa en el microcontrolador el módulo ADC. La máxima salida del sensor es 1,5 voltios, cuando la temperatura es 150 grados Celsius. Por esto es importante cambiar el valor de referencia positiva del convertidor análogo digital, con el fin de mejorar la resolución de la medida de voltaje. Para el ejemplo de este capítulo se configurará el voltaje de referencia positivo del ADC, en 2,5 voltios. Cambiando la referencia positiva a 2,5 voltios el convertidor entregará un resultado binario de 1023 cuando el voltaje a convertir es de 2,5 voltios. Para el caso de este sensor, se verá definido por las siguientes relaciones: 1023 Radc  Ecuación 12-1 2,5V Vadc Donde Radc es el resultado binario de la conversión AD. De está ecuación se puede deducir que el voltaje Vadc, leído por el convertidor AD, es:

Vadc 

(2,5V )( Radc ) Ecuación 12-2 1023

Trabajando con la relación del sensor que es: 10m voltios por cada grado Celsius, se puede plantear la siguiente ecuación:

128

10mV Vadc  o Ecuación 12-3 1o C nC
Donde n es la temperatura en grados Celsius, que está registrando el sensor, de está ecuación se puede deducir que la temperatura n es:

(1o C )(Vadc ) nC Ecuación 12-4 10mV
o

Remplazando la ecuación (12.2), en (12.4), se obtiene la siguiente relación:

n oC 

(2,5)( Radc )  0,244 Radc Ecuación 12-5 10,23

Está relación debe ser implementada en la conversión AD, en el programa del PIC. Para contextualizar el uso de este sensor se puede observar y analizar el siguiente código fuente para un PIC 16F877A: //Definición de pines del LCD sbit LCD_RS at RB4_bit; sbit LCD_EN at RB5_bit; sbit LCD_D7 at RB3_bit; sbit LCD_D6 at RB2_bit; sbit LCD_D5 at RB1_bit; sbit LCD_D4 at RB0_bit; //Definición de los TRIS del LCD sbit LCD_RS_Direction at TRISB4_bit; sbit LCD_EN_Direction at TRISB5_bit; sbit LCD_D7_Direction at TRISB3_bit; sbit LCD_D6_Direction at TRISB2_bit; sbit LCD_D5_Direction at TRISB1_bit; sbit LCD_D4_Direction at TRISB0_bit; void main( void ) { //Declaración de variables. unsigned int Radc, TemI; float Tem; char Text[16]; //Configura el módulo ADC con el pin AN3 //como voltaje de referencia positiva. ADCON1 = 0b11000001; //Inicio del LCD. Lcd_Init(); //Borrado del cursor. Lcd_Cmd(_LCD_CURSOR_OFF); //Impresión de texto. Lcd_Out( 1, 1, "Temperatura:"); 129

delay_ms(100). //Se imprime el resultado.244*Radc. Tem = 0. LM35. //Se convierte el número entero a una cadena de caracteres. Text). 1. } } Terminada la edición y compilación del programa se debe construir un circuito en ISIS con los siguientes dispositivos: 16F877A. y LM016L. TemI = Tem. IntToStr( TemI. Lcd_Out( 2. Radc = ADC_Read(0). este se puede apreciar en la siguiente figura: Circuito 12-1 130 . //Retardo de 100m segundos.while(1) //Bucle infinito.5). { //Lectura del canal 0 del ADC. //Se convierte el resultado a un número entero. //Uso de la ecuación (13. RES. Text ).

Pascal. 12. se obtiene: P 111. de este dispositivo es la siguiente: Figura 12-2 Pin 1 2 3 4. y permite medir presiones entre 15 y 115 KPa. La presión se denota en diferentes unidades dependiendo del sistema. Atmósferas. la distribución de pines y la vista en ISIS.095) Ecuación 12-6 Donde Vout es el voltaje de salida del sensor. Despejando de la ecuación (13.7 psi.2 Sensores de presión La presión es una variable física. 5. Referencia Vs. cada uno debe contar con una relación de salida o función de transferencia. o entre 2. Durante la marcha de la simulación se puede cambiar el valor de la temperatura en el sensor LM35.El arreglo de resistencias de 330Ω permite hacer un divisor de voltaje para crear la referencia de 2. para ver su funcionamiento.555 Ecuación 12-7 Vs 131 .18 y 16. 6 Función Vout GND. Para los fines prácticos de este capítulo se trabajará con el sensor de presión MPX4115. la presión P.009P  0. La apariencia física. Poder No implementado Tabla 12-1 Independiente del sensor que se implemente.5 voltios. la presión se puede dar en: psi. centímetros de mercurio.111Vout  10. este sensor está caracterizado en Kilo Pascal. representada por un vector que está definido como la fuerza aplicada en una superficie específica. P es la presión en Kilo Pascal. y Vs el voltaje de alimentación del sensor. etc. en el caso particular del sensor MPX4115 la función de transferencia especificada por el fabricante es: Vout  V s(0.6).

//Borrado del cursor. //Impresión de texto. unsigned int Radc. PreI.8) y remplazando en la ecuación (12. sbit LCD_D5_Direction at TRISB1_bit.En función del convertidor AD. sbit LCD_D6 at RB2_bit. 1. sbit LCD_D6_Direction at TRISB2_bit. sbit LCD_EN at RB5_bit. y la relación final queda de la siguiente forma: P  0. //Definición de los TRIS del LCD sbit LCD_RS_Direction at TRISB4_bit. del PIC configurado con 10 bits de resolución se puede concluir que: 5V nV Ecuación 12-8  1023 Radc Por lo tanto despejando el voltaje n de la ecuación (12.7). está relación es la siguiente: P 0. por lo tanto Vs es igual a 5 voltios. sbit LCD_D7 at RB3_bit. sbit LCD_EN_Direction at TRISB5_bit. //Inicio del LCD. se puede obtener la relación o ecuación que se debe usar en el microcontrolador para hacer la lectura del sensor. float Pre. Lcd_Out( 1.555 Ecuación 12-9 Vs Para el caso particular de este ejemplo se usará una fuente de poder de 5 voltios la cual alimenta el sensor y el micro. void main( void ) { //Declaración de variables.10861Radc  10. sbit LCD_D4 at RB0_bit. sbit LCD_D4_Direction at TRISB0_bit. "Presion en KPa:"). Lcd_Init().54306 Radc  10. sbit LCD_D5 at RB1_bit. Lcd_Cmd(_LCD_CURSOR_OFF). sbit LCD_D7_Direction at TRISB3_bit. char Text[16]. 132 .555 Ecuación 12-10 Para demostrar el funcionamiento práctico de este tipo de sensores se puede observar y analizar el siguiente código fuente: //Definición de pines del LCD sbit LCD_RS at RB4_bit.

//Se imprime el resultado. 1. delay_ms(100). Text ). //Retardo de 100m segundos. Lcd_Out( 2. Text).9).5555. y LM016L. el circuito debe tener la siguiente apariencia: Circuito 12-2 133 . Radc = ADC_Read(0). //Uso de la ecuación (13. { //Lectura del canal 0 del ADC. } } Terminada la edición y compilación del programa se puede implementar en el simulador ISIS los dispositivos: 16F877A. PreI = Pre. IntToStr( PreI. //Se forza el resultado a la parte entera. //Se convierte el número entero a una cadena de caracteres.10861*Radc+10. para realizar la simulación del sistema. Pre = 0.while(1) //Bucle infinito. MPX4115.

y en proyectos de robótica. La apariencia física la distribución de pines y la vista en el simulador ISIS del sensor son los siguientes: Figura 12-3 Pin 1 2 3 Función Vout GND. Referencia Vcc. Para comprender de forma clara este concepto se puede observar y analizar la siguiente gráfica que muestra el comportamiento de la salida en función de la distancia: Figura 12-4 134 . este dispositivo usa como estrategia de medida un rayo de luz infrarroja y su reflejo para determinar la longitud. La implementación de este tipo de dispositivos es de utilidad en sistemas de control. esto quiere decir que la salida no obedece a una función de transferencia lineal. Para el caso puntual de este capítulo se trabajará con el sensor GP2D12.3 Sensores de distancia Los sensores de distancia permiten medir una longitud desde el punto de ubicación del sensor hasta un obstáculo puesto en un punto dentro del rango de trabajo del sensor. Poder Tabla 12-2 Este tipo de sensores cuentan con un comportamiento no lineal.12.

Salida sensor(x) 2. el voltaje de salida del sensor. Se asume como variable independiente x los datos de entrada que en este caso son el voltaje de salida del sensor.35 Ecuación 12-12 A 80  B 0. en este caso serán ecuaciones de primer orden. Para iniciar este proceso se escogen 2 puntos. estos puntos se toman de las medidas experimentales.35V 0. la distancia en centímetros. como variable dependiente y.425531  B 80  A2. dado que en algunos casos los fabricantes no entregan una función de transferencia útil de tal forma que pueda ser usada en una sola ecuación. Por ejemplo el punto a 10cm. Cuantos más puntos se evalúen más precisa será la función de transferencia.41 Vo. se usan los datos de dos puntos y se remplazan en las ecuaciones de la siguiente forma: Distancia(y) 10cm 80cm Sistema de ecuaciones: 10  A B 2. Para comprender este concepto observe la siguiente ecuación que representa el comportamiento exponencial del sensor: y A  B Ecuación 12-11 x Recordando que y. Para cada uno de los puntos se realiza una ecuación de orden igual al número de puntos menos uno. Como se puede apreciar en la hoja técnica el rango de acción es de 10 a 80 centímetros tal como se puede apreciar en la gráfica.43902  B Ecuación 12-13 Solucionando el sistema de ecuaciones se puede determinar que las constantes son: A=34. y 80cm.793814433 De está forma se puede formalizar la función de transferencia del sensor con la ecuación: 135 . Para poder caracterizar el comportamiento del sensor y realizar una función de transferencia es indispensable realizar un proceso matemático conocido como linealización. Sin embargo no es un obstáculo para el uso del sensor.Este comportamiento en un sensor es de difícil aplicación. La gráfica mostrada está contenida en la hoja técnica del dispositivo y está es realizada experimentalmente.76546392 B=-4. Para este fin se hace una interpolación con una cantidad finita de puntos conocidos de la función. que estén equitativamente distribuidos.41V Tabla 12-3 Sistema de ecuaciones con dos incógnitas: 10  A0. representa la distancia y que x.

sbit LCD_D6 at RB2_bit.15) en la ecuación (12.793814433 Ecuación 12-16 Radc El paso siguiente es implementar un código fuente que use la función de transferencia y que posteriormente visualice el valor capturado. sbit LCD_D7 at RB3_bit. //Inicio del LCD. void main( void ) { //Declaración de variables. sbit LCD_D5_Direction at TRISB1_bit. sbit LCD_D6_Direction at TRISB2_bit. Lcd_Init(). 136 . char Text[16].5 voltios.5Radc Ecuación 12-15 1023 Remplazando la ecuación (12.793814433 Ecuación 12-14 x Como la variable x representa el voltaje de salida del sensor y teniendo presente que el valor del sensor no supera 2. la referencia del convertidor AD. sbit LCD_D5 at RB1_bit. unsigned int Radc.76546392  4. //Definición de los TRIS del LCD sbit LCD_RS_Direction at TRISB4_bit.02784  4. sbit LCD_D7_Direction at TRISB3_bit. //Borrado del cursor.y 34. ADCON1 = 0b11000001. sbit LCD_D4_Direction at TRISB0_bit. //Configura el módulo ADC con el pin AN3 //como voltaje de referencia positiva. se puede ajustar a 2.14). sbit LCD_EN_Direction at TRISB5_bit.5 voltios. sbit LCD_D4 at RB0_bit. para este fin se puede observar y analizar el siguiente código fuente: //Definición de pines del LCD sbit LCD_RS at RB4_bit. Para estáblecer la conversión se puede usar la ecuación: (12. sbit LCD_EN at RB5_bit. obtenemos la siguiente relación o función de transferencia: y 14226.15) Vadc  2. float Dis. DisI.

793814433. Lcd_Out( 1. while(1) //Bucle infinito. 1. RES. delay_ms(100). y el sensor GP2D12.15). LM016L. //Se convierte el valor entero en cadena de texto. Text ). en el siguiente circuito: Circuito 12-3 137 . 1. //Retardo de 100m segundos. //Impresión de texto. Text ). Lcd_Out( 2. { //Lectura del canal análogo.02784/Radc)-4. } } Por último para verificar el comportamiento del sistema se implementa en ISIS. "Distancia:"). IntToStr( DisI. //Se imprime la lectura del sensor. los dispositivos: 16F877A. Radc=ADC_Read(0). DisI=Dis. //Implementación de la función de transferencia (13. //Se para el resultado a un valor entero. Dis = (14226.Lcd_Cmd(_LCD_CURSOR_OFF).

del mismo tamaño. o fotoresistencias. para entender este concepto se puede observar el siguiente arreglo o circuito: Circuito 12-4 Asumiendo la teoría básica de circuitos eléctricos para un divisor de voltaje se puede implementar la siguiente ecuación: Vadc  LDR(Vcc ) Ecuación 12-17 LDR  R 138 . Para usar una LDR. Estos dispositivos son útiles para determinar. esto implica que el rango de resistencia cambia en función del tamaño. o el voltaje que se desempeña en sus terminales. son las siguientes: Figura 12-5 Las características eléctricas de estos dispositivos no son fácil de conseguir dado su simple funcionamiento y gran variedad de fabricantes.12. Incluso los cambios de resistencia no son exactamente iguales en dos LDR. la presencia o ausencia de luz en el ambiente. de tamaño grande tiene rangos de cambio menor que una LDR pequeña. se adquieren comercialmente en tamaños diversos. cuanto mayor sea la intensidad de la luz. es posible concebir con estos dispositivos controles de luminosidad. una LDR. las LDR.4 Sensores LDR Los sensores LDR. Estás razones hacen que la forma de mayor simplicidad para su uso sea la lectura de su resistencia. La apariencia física y las vistas posibles en el simulador ISIS. menor es la resistencia que ofrece la LDR. son dispositivos que cambian su resistencia en función de la intensidad de luz. la forma más simple es realizar un divisor de voltaje con una resistencia fija.

004887585) Para la compilación y edición del programa se puede observar y analizar el siguiente código fuente: //Definición de pines del LCD sbit LCD_RS at RB4_bit. sbit LCD_D6_Direction at TRISB2_bit.04887585)( R) Ecuación 12-20 5  ( Radc )(0.18). unsigned long Radc.19) en la ecuación (13. sbit LCD_D4_Direction at TRISB0_bit. char Text[16]. 139 .87585) Ecuación 12-21 5  ( Radc )(0.17). sbit LCD_EN at RB5_bit.Despejando de la ecuación (12. obtenemos la siguiente relación: LDR  ( Radc )(0. sbit LCD_D4 at RB0_bit. sbit LCD_EN_Direction at TRISB5_bit. LDR se obtiene la siguiente ecuación: (Vout )( R) Ecuación 12-18 LDR  Vcc  Vout Dado que el valor de salida Vout se puede ingresar a una entrada AD. es: LDR  ( Radc )(48. sbit LCD_D6 at RB2_bit. con la siguiente relación y un voltaje de referencia positivo de 5 voltios: ( Radc )(5) Vadc   ( Radc )0. //Inicio del LCD. sbit LCD_D7 at RB3_bit.004887585 Ecuación 12-19 1023 Remplazando la ecuación (13. sbit LCD_D5_Direction at TRISB1_bit. DisI. void main( void ) { //Declaración de variables. sbit LCD_D7_Direction at TRISB3_bit. //Definición de los TRIS del LCD sbit LCD_RS_Direction at TRISB4_bit. y asumiendo que Vcc es de 5 voltios. el valor de la foto resistencia.004887585) Para el caso particular de la simulación de este ejercicio la resistencia del divisor de voltaje será de 10kΩ de está manera la ecuación para calcular la resistencia de la LDR. del microcontrolador se puede asumir como Vadc. sbit LCD_D5 at RB1_bit.

Text ).87585533)/(5. Lcd_Cmd(_LCD_CURSOR_OFF). TORCH_LDR. DisI = (Radc*48. LongToStr( DisI. //Retardo de 100m segundos. 1. { //Lectura del canal análogo. //Se convierte el valor entero largo. ecuación (13. RES.Lcd_Init(). Lcd_Out( 1. Radc=ADC_Read(0). Text ).004887585). Lcd_Out( 2. //Borrado del cursor. 1.20). "Resistencia:"). en cadena de texto. //Implementación del cálculo de la LDR. //Impresión de texto. delay_ms(100). en el siguiente circuito electrónico: Circuito 12-5 140 . } } Para realizar la simulación en ISIS. se implementan los siguiente dispositivos: 16F877A.0-Radc*0. LM016L. while(1) //Bucle infinito. //Se imprime la lectura del sensor.

Poder Gnd. sin embargo no es exactamente igual. bloque de datos tanto de lectura como de escritura. y un bit de conformación o ACK.12. Para realizar la comunicación con el sensor. sbit SHT_Datos_Dire at TRISC1_bit. 141 . //Declaración de constantes. en un solo encapsulado. así como su distribución de pines. el protocolo de comunicación especificado en la hoja técnica del dispositivo. con una calibración de fábrica que garantiza la fidelidad de las lecturas. //Declaración de TRIS de los pines de comunicación. La apariencia física y la vista de estos dispositivos en ISIS. El protocolo incluye una condición de inicio.5 Sensores de humedad y temperatura La humedad y la temperatura son dos variables físicas de amplio uso en la industria y para su medida se pueden usar dispositivos que integran las dos lecturas simultáneamente. es la siguiente: Figura 12-6 Pin 1 2 3 4 Función Reloj Vdd. sbit SHT_Reloj at RC0_bit. y por está razón no es posible usarlo. La familia de sensores SHT7x permiten hacer la lectura de ambos parámetros. Esto implica realizar por medio del código de programa. sbit SHT_Reloj_Dire at TRISC0_bit. const unsigned short RH_8bits_TEM_12bits = 1. el desarrollador debe implementar su propia librería para el enlace de datos. el cual es de drenador abierto. sbit SHT_Datos at RC1_bit. La comunicación se basa en dos pines uno de reloj que siempre debe ser de salida y un pin de datos bidireccional. lo que hace necesario implementar una resistencia pull-up en está línea a Vcc de 10kΩ. Referencia Datos Tabla 12-4 Estos sensores entregan la información por medio de un puerto serial similar al protocolo I²C. a continuación se mostrará paso a paso un ejemplo que permite cumplir con este cometido: //Declaración de pines de comunicación.

delay_ms(1). para frecuencia de 1MHz. if( e ) SHT_Reloj = 1.32.128}. const unsigned short Peso_bits[8] = {1. } } //Función para estáblecer el estádo del reloj. unsigned short RH_TEM. SHT_Bus(1). void SHT_Reloj_Salida( unsigned short e ) { //Se estáblece el estádo lógico en el reloj. SHT_Reloj_Salida(0). SHT_Datos = 0. } 142 . void SHT_Inicio( void ) { //Secuencia de inicio. } //Función para crear la condición de inicio. delay_us(1). SHT_Reloj_Salida(0).2. else { //Si el estádo es un 0 lógico //se estáblece la salida en 0.64. SHT_Reloj_Salida(1). SHT_Reloj_Salida(1). else SHT_Reloj = 0. SHT_Datos_Dire = 0.4. //Periodo del reloj. //Función para estáblecer un estádo //lógico en el pin de datos void SHT_Bus( unsigned short e ) { //Si el estádo es 1 se impone en alta //impedancia el pin de datos.8.16.const unsigned short RH_12bits_TEM_14bits = 0. if( e ) SHT_Datos_Dire = 1. //Bandera de resolución. SHT_Bus(0).

n!=255. SHT_Reloj_Salida( 1 ). return Dato_SHT. } //Se transmite la condición de ACK. SHT_Reloj_Salida( 1 ). Dato_SHT=0. //Generación de pulsos de reloj. SHT_Bus( 0 ). //Bucle para el envió serial. SHT_Reloj_Salida( 0 ). SHT_Reloj_Salida( 1 ). } //Generación de bit ACK. unsigned short SHT_Leer_Bus( void ) { unsigned short n. SHT_Reloj_Salida( 0 ). } //Función para grabar el registro Status. //Se deja el bus de entrada. SHT_Bus( 1 ). 143 .) { //Se asigna el bit según su peso. if( SHT_Datos ) Dato_SHT += Peso_bits[n]. n-. n!=255. } //Función para leer un dato en el bus. if( (d>>n)&1 ) SHT_Bus( 1 ).) { //Pulso de reloj. SHT_Reloj_Salida( 1 ).//Función para escribir un dato en el bus. SHT_Reloj_Salida( 0 ). //Se reciben los bits según su peso. //Se retorna el valor de la lectura. void SHT_Escribe_Bus( unsigned short d ) { unsigned short n. for( n=7. SHT_Bus( 1 ). SHT_Reloj_Salida( 0 ). //Bucle para recibir los bits de forma serial. for( n=7. else SHT_Bus( 0 ). n-. //Se configura el bus de entrada. SHT_Bus( 1 ).

SHT_Inicio(). DL=SHT_Leer_Bus(). } float SHT_Leer_Hum( void ) { float hum. //Se aplica el protocolo de lectura. unsigned short DH. SHT_Leer_Bus(). float SHT_Leer_Tem( void ) { float tem. } //Función para leer el valor del Status. //Se retorna la temperatura. SHT_Inicio().1 + 0.01*LEC. SHT_Escribe_Bus( 0b00000110 ). } //Función para leer la temperatura. unsigned short SHT_Status_Leer( void ) { unsigned short st. return tem. unsigned short DH. delay_ms(310). 144 . DH=SHT_Leer_Bus(). st=SHT_Leer_Bus(). return st. else tem = -40. //Espera para hacer la lectura. unsigned int LEC=0. //Se implementan las ecuaciones para //la temperatura. SHT_Inicio(). DL. SHT_Leer_Bus(). DL. if( RH_TEM ) tem = -40.1 + 0. SHT_Escribe_Bus( s ). SHT_Escribe_Bus( 0b00000111 ).void SHT_Status_Graba( unsigned short s ) { //Se transmite el valor del status. LEC = DL + DH*256.04*LEC. SHT_Escribe_Bus( 0b00000011 ). unsigned int LEC=0.

for(n=0. DL=SHT_Leer_Bus(). SHT_Bus(1).5872*LEC .0. } //Función para inicializar el sensor.0. LEC = DL + DH*256. //Se generan 9 ciclos de reloj.0367*LEC . SHT_Status_Graba( St ). SHT_Reloj_Salida(0). delay_ms(100).3468 + 0. delay_ms(1). //Se retorna la humedad. } //Se configura el registro Status. DH=SHT_Leer_Bus(). n++) { SHT_Reloj_Salida(1). //y estádo inicial del bus de datos. void Inicio_SHT7x( unsigned short St ) { unsigned short n. SHT_Leer_Bus(). RH_TEM = St.0000015955*LEC*LEC. 145 . SHT_Reloj_Salida(0). delay_ms(310). return hum. //Se estáble el pin de datos en 1. if( RH_TEM ) hum = -4. SHT_Escribe_Bus( 0b00000101 ). SHT_Reloj_Dire = 0. SHT_Inicio(). } Para el caso puntual de este ejemplo se complementa el código con la siguiente función main: void main( void ) { //Declaración de variables. para reiniciar el sensor. SHT_Inicio().00040845*LEC*LEC. //Configuración de pines. //Se inicia el reloj en 0. //Espera para hacer la lectura. n<9.3468 + 0.//Se aplica el protocolo de lectura. //Se implemetan las ecuacion para //la humedad. else hum = -4.

Inicio_SHT7x(RH_12bits_TEM_14bits). Lcd_Out( 1. { //Se lee la temperatura y se imprime en el LCD. LM016L. Lcd_Out( 1. while(1) //Bucle infinito. Text ). //Impresión de texto. Text ). "Hum:"). 1. //Borrado del cursor. //Inicio del LCD. RES. y SHT71. en el siguiente circuito electrónico: Circuito 12-6 146 . FloatToStr( Sen. Sen = SHT_Leer_Tem(). char Text[16]. Text ). Sen = SHT_Leer_hum(). } } Finalizada la edición y compilación del código fuente. FloatToStr( Sen. //Se lee la humedad y se imprime en el LCD. //Se inicializa el sensor SHT7x. Lcd_Init(). "Tem:"). 1. Text ). con los dispositivos: 16F877A. se procede a realizar la simulación en ISIS. Lcd_Out( 2. Lcd_Out( 2. 6. 6.float Sen. Lcd_Cmd(_LCD_CURSOR_OFF).

el margen de error es casi del 0%. lo que implica que con respecto al diámetro del globo terráqueo. del cual se puede descargar una versión demostrativa en la página www. estás aplicaciones se pueden desarrollar en paquetes. Uno de los aplicativos que permite crear los puertos virtuales es Virtual Serial Port Kit. etc.1 Módulos GPS Los módulos GPS son dispositivos de comunicación inalámbrica que toman tramas de información suministrada por los satélites artificiales geoestácionarios del planeta. De la misma forma el simulador ISIS permite implementar un puerto virtual con conectividad a los puertos seriales físicos del ordenador. Los módulos GPS deben contar con la lectura de un mínimo de tres satélites para realizar un cálculo conocido como triangulación. sin embargo los GPS comerciales que se consiguen tienen un margen de error aproximado de 3 metros.virtual-serial-port. C++. entre otros. y lectura de módulos como: relojes en tiempo real. este consiste en medir la diferencia en tiempo entre las lecturas para determinar la posición del GPS sobre la superficie de la tierra. también es posible crear sus propios aplicativos de software para ordenadores personales.com La apariencia visual de este paquete es la siguiente: Figura 13-1 Sin embargo este es tan solo uno de los diversos aplicativos que cumplen con la misma función. Para poder simular la conectividad con aplicativos propios e incluso con paquetes de software como Hyper Terminal. se puede recurrir a aplicativos que crean en el ordenador puertos seriales virtuales. el sistema de control. puntos inalámbricos. En función de la necesidad y de las capacidades del desarrollador. La calidad de la lectura dependerá del número de satélites simultáneos conectados al GPS. Los módulos GPS se pueden adquirir 147 . 13. la lectura será de mayor precisión cuantos más satélites estén conectados. Java. GPS.13 Comunicación con dispositivos La comunicación con diversos dispositivos se hace indispensable para realizar tareas como la lectura de sensores. como Visual Basic.

como: Número de satélites detectados. entre otras. y características de curso y velocidad. su punto de referencia es el paralelo del Ecuador y puede ser Norte. es un segundo. La lectura de un GPS. que es el ángulo φ. o simplemente un circuito impreso con el módulo. representado en la gráfica. Los Módulos GPS. Las coordenadas esféricas usan tres parámetros. representado en la gráfica y se mide a partir del meridiano de Greenwich. este ángulo puede ser Este. y su magnitud puede variar de 0 a 90 grados. es el conjunto de coordenadas esféricas sobre el globo terráqueo. El segundo parámetro es la latitud. o Sur. u Oeste. el primero es la longitud. El último parámetro es conocido como altitud. y su magnitud está definida en metros. La apariencia física de ellos se puede apreciar en las siguientes figuras: Figura 13-2 Los módulos GPS cuentan con puertos seriales que transmiten periódicamente la lectura de las coordenadas geográficas leídas por el GPS. que es el ángulo λ. y el paralelo del Ecuador. Latitud ángulo φ. de la misma forma su magnitud varia de 0 a 90 grados. este determina la altura del punto coordenado con respecto al nivel del mar. Para contextualizar este concepto se puede observar el siguiente gráfico: Figura 13-3 Longitud ángulo λ. especificaciones mínimas del GPS. usando como referencia de medida el meridiano de Greenwich. El periodo por defecto de los GPS. Sin embargo para realizar la lectura de las coordenadas solo se 148 . suministran una variedad de datos a demás de las coordenadas esféricas.comercialmente en varias presentaciones con encapsulado.

*7B Donde este campo 8..3047.000.3.S.0.N.1. representa el número de satélites conectados al GPS.0.539667.03957.17.1899.3354.5. Información de Longitud: $GPGGA.0.724500.0.S.0.W.3047.0.0.000000.M.1. conectados: $GPGGA.00. El siguiente ejemplo muestra la lectura de las coordenadas: Latitud: 30° 47‟ 43.A.724500.000.6.9. Información de altitud: $GPGGA.3047.*0B Las tramas de información entregadas por un GPS.0..0.24.539667.2600.4.539667.5.556000.A*30 $GPGSA.3.1.*7B Información de Latitud: $GPGGA.1.556000.000000.24. que es equivalente a 57 minutos y 33.03957.S.301299..M.1.0.47 segundos. El siguiente ejemplo muestra una trama completa de datos entregados por un GPS: $GPZDA.S.M.000000.3047.8.86.000000.36 segundos. 47.A.S.1. Es indispensable usar el módulo USART del PIC.000..3354.M.0.000.0*2D $GPGSV.M.0.0.03957.4.24. enter: 13.0.M.A*5F $GPVTG.47” Sur.04125.4.6.0.8.000000.2.673167.000.M.556000. 57. $GPGGA.22.30.1.000000.S.3354. Es decir 39 grados.556000.K.M.36” Oeste...0*46 $GPGSV.8.M.W.24.A. Para iniciar el proceso de diseño con el microcontrolador es importante crear una rutina que identifique las tramas y los encabezados para poder filtrar los datos de interés.0.0.2.000000.0.2.S representa: 30° 47‟ 43.M.8.0.24.03957.04125.A*2A $GPRMC.2600.4.requiere la trama de información con el encabezado: $GPGGA..36” Oeste..9.0.3.8. y retroceso del carro 10.000000.W. Altura: 2600 metros..04125.W.0.*7B Donde: 03957.24.1.0.M.0*44 $GPRTE.0.00*5F $GPGGA.673167.7245 minutos.C.0.2600.2600.00500277932185.W representa: 39° 57‟ 33.0. Para fines de diseño es importante identificar la velocidad de transmisión del módulo GPS y ajustar la misma velocidad en el módulo USART del PIC.S.8.556000.E.724500. que es equivalente a 47 minutos y 43.12.17.724500.000. son de forma texto.000.2.0. en este caso 8.24. Es decir 30 grados.556 minutos.3047.0.724500.) entre si.1.8.0.M representa la magnitud en metros y la altitud de 2600 metros.2600.47” Sur. Información del número de satélites.S.86.0. Longitud: 39° 57‟ 33.*78 $GPGLL.W.W.8.T.W.1.673167.000.0.724500. en 149 . por medio de caracteres ASCII.*7B Donde 3047..9.2. y finalizadas con los caracteres ASCII.2600.*7B Donde 2600.0.22.556000. Los campos de información siempre son fijos y estás separados por una coma (.000000.03957.1. que para este caso son las coordenadas esféricas o geográficas.000.24.M.M.W.

//Se activan las interrupciones globales. char Bufer[50]. //Configuración del puerto serial a 4800 bps. Para evitar la pérdida de datos se implementará la lectura de los datos seriales por medio de las interrupciones. que este en capacidad de recibir y almacenar las tramas en un búfer de datos: char Dato. Bufer[Pos]=0. } } } void main( void ) { //Se configuran los puertos. //Declaración de la función de interrupciones. //Se estáblece el fin de cadena. //Se recibe el carácter Enter. default:Bufer[Pos++]=Dato. INTCON = 0b11000000.este caso. UART1_Init(4800). unsigned short Pos=0. Dato = UART1_Read(). //Se desactivan las demás fuentes de interrupción. 150 . switch( Dato ) { case 13: Pos=0. PIR1 = 0. //Se guardan los datos en el búfer. // //Se activa la interrupción por //recepción serial. //y periféricas. void interrupt ( void ) { //Se evalúa si la interrupción disparada por //recepción serial. break. Bandera = 1. break. PIR2 = 0. PIE2 = 0. PIE1 = 0b00100000. //Se recibe el retroceso del carro. y se establecerá una velocidad de transmisión de 4800 bits por segundo. Bandera=0. En primera instancia se hace un programa para el PIC 18F452.F5 ) { //Se lee el dato de entrada. if( PIR1. //Se apagan las banderas de interrupción. case 10:break.

short Grados_latitud. if( trama[0]=='$' && trama[1]=='G' && trama[2]=='P' && trama[3]=='G' && trama[4]=='G' && trama[5]=='A' ) return 1. Bandera=0.while(1) //Bucle infinito. char Longitud. { //Se evalúa si una trama ha llegado. else return 0. float Altitud. char Latitud. } } } El procedimiento siguiente es implementar una función que identifique la trama de información y los datos contenidos en ella. A continuación se mostrará una función capaz de validar una trama para la lectura de las coordenadas: //Función para detectar la trama de coordenadas. es importante recordar que los campos de información son fijos pero no necesariamente el contenido de cada campo. float Minutos_latitud. float Minutos_longitud. 151 . short Satelites. short EsGPGGA( char *trama ) { //Está función retorna 1 si la trama es válida y 0 de lo contrario. if( Bandera ) { //Aquí se debe analizar el contenido de la información del búfer. Para estáblecer la información de la lectura se usa una variable o estructura diseñada por el desarrollador: typedef struct { short Grados_longitud. }Coordenada. } A continuación se muestra un ejemplo de una función diseñada para leer los datos de las coordenadas: //Función para adquirir los datos del campo. //Se apaga la bandera de llegada. Coordenada LecturaGPS( char *trama ) { //Declaración de variables.

//Se convierte el texto en el valor numérico. 9 ). //Se verifica que el encabezado contenga el texto $GPGGA. Texto.Altitud = atof(Texto). //Se convierte el texto en el valor numérico de los grados de la longitud. BuscarCampo( Trama.Grados_longitud=0. 3 ).Latitud = Texto[0]. //Se convierte el texto en el valor numérico los minutos de la longitud. //Se truncan los minutos de la latitud.Latitud=0. C.Coordenada C.Grados_latitud = atoi(Texto). BuscarCampo( Trama. C. //Se filtra el campo 2 que contiene los minutos de la latitud. //Se filtra el campo 2 que contiene. //Valor inicial de las variables. C.k. //Se convierte el texto en el valor numérico. Texto. BuscarCampo( Trama.Longitud = Texto[0]. C. BuscarCampo( Trama. C. Texto. char Texto[50]. C. //Se convierte el texto en el valor numérico de los grados de la latitud. 5 ). Texto. C. C. } La siguiente función permite buscar dentro de la trama enviada por el GPS.Satelites = atoi(Texto). Texto[3]=0. //Se convierte el texto en el valor numérico. //Se filtra el campo 3 que contiene la orientación de la latitud.Satelites=0. //Se filtra el campo 4 que contiene los minutos de la longitud. C.Minutos_longitud = atof( Texto + 3 ). Texto. 7 ). 2 ). un campo de información determinado: 152 . C.Grados_latitud=0.Longitud=0. Texto. C.Minutos_longitud=0. C. //Se filtra el campo 9 que contiene la altitud.Minutos_latitud=0. BuscarCampo( Trama. return C. 4 ). unsigned short j. C.Altitud=0. C.Minutos_latitud = atof( Texto + 2 ). Texto[2]=0. //Se filtra el campo 7 que contiene el número de satélites detectados. C. if( !EsGPGGA( trama ) )return C. C.Grados_longitud = atoi( Texto ). BuscarCampo( Trama.

Text ). IntToStr( grados. segundos. //Se separan los segundos de los minutos. short campo ) { unsigned short j=0. g = (short)minutos. j++. char ll ) { //Declaración de variables de la función. char Text[20]. int grados. Destino[j]=0.' )j++. j++.k=0. short j=0. while( j<campo )if( Fuente[k++]=='. while( !(Fuente[k]=='. while( Text[j]!=0 ) { if( Text[j]!=' ' ) coor[k++]=Text[j]. } } Está última función permite convertir la información de una lectura en una cadena de coordenadas entendible en grados. y orientación: //Función para encadenar los valores de una coordenada. } coor[k++]=':'. minutos. //Bucle para buscar el campo. el valor de los grados. Text ). while( Text[j]!=0 ) { if( Text[j]!=' ' ) coor[k++]=Text[j].(float)g. char *Destino. float s. void BuscarCampo( char *Fuente.' || Fuente[k]=='*') ) { Destino[j++]=Fuente[k++]. j=0. s = minutos . //Se pega en la cadena de texto. IntToStr( g. float minutos. //Se pega en la cadena de texto. el valor de los minutos. //Bucle para copiar el campo.//Función para buscar y filtrar un campo en la trama. j=0.k=0. short g. void Convertir_coordenada( char *coor. } 153 .

//Se calculan los segundos. //Declaración de variables. la orientación. sbit LCD_D4_Direction at TRISD0_bit. float Altitud. sbit LCD_D4 at RD0_bit. sbit LCD_EN_Direction at TRISD5_bit. Text ). sbit LCD_D5_Direction at TRISD1_bit. while( Text[j]!=0 ) { if( Text[j]!=' ' ) coor[k++]=Text[j]. char Longitud. coor[k++]=ll. //Definición de los registros TRIS del LCD. float Minutos_longitud. sbit LCD_D6_Direction at TRISD2_bit. sbit LCD_RS_Direction at TRISD4_bit. } //Se pega en la cadena de texto. sbit LCD_EN at RD5_bit. coor[k++]=' '. short Grados_latitud. sbit LCD_RS at RD4_bit. coor[k]=0. sbit LCD_D6 at RD2_bit.coor[k++]=':'. typedef struct { short Grados_longitud. j=0. //Se pega en la cadena de texto.0. short Satelites. } Complementando las funciones antes citadas y la estructura final del programa con un display LCD de caracteres y la comunicación del puerto serial se tiene el siguiente código fuente final: // Definición de las conexiones del LCD. float Minutos_latitud. sbit LCD_D5 at RD1_bit. 154 . j++. s *= 60. char Latitud. }Coordenada. el valor de los segundos. sbit LCD_D7_Direction at TRISD3_bit. FloatToStr( s. //Estructura para la lectura de una coordenada esférica. sbit LCD_D7 at RD3_bit. de la fracción de minutos.

' )j++. //Se verifica que el encabezado contenga el texto $GPGGA. if( !EsGPGGA( trama ) )return C.Minutos_latitud=0. short EsGPGGA( char *trama ) { //Está función retorna 1 si la trama es valida y 0 de lo contrario. j=0. char *Destino. if( trama[0]=='$' && trama[1]=='G' && trama[2]=='P' && trama[3]=='G' && trama[4]=='G' && trama[5]=='A' ) return 1. char Texto[50]. Destino[j]=0.Latitud=0. Coordenada C. C.k. 155 .Altitud=0. } //Función para buscar y filtrar un campo en la trama. unsigned short j. C.Grados_latitud=0.Longitud=0. short campo ) { unsigned short j=0. C. unsigned short Bandera=0.Grados_longitud=0. void BuscarCampo( char *Fuente. } } //Función para adquirir los datos del campo. //Bucle para buscar el campo. C. char Bufer[100]. C. //Bucle para copiar el campo. unsigned short Pos=0. C. else return 0. //Valor inicial de las variables.k=0.Minutos_longitud=0.' || Fuente[k]=='*') ) { Destino[j++]=Fuente[k++].char Dato. Coordenada LecturaGPS( char *trama ) { //Declaración de variables. C. while( j<campo )if( Fuente[k++]=='. C.Satelites=0. //Función para detectar la trama de coordenadas. while( !(Fuente[k]=='.

} 156 . //Se convierte el texto en el valor numérico. short g.Longitud = Texto[0].k=0. Texto. Texto. el valor de los grados. IntToStr( grados.Minutos_longitud = atof( Texto + 3 ). char ll ) { //Declaración de variables de la función. //Se truncan los minutos de la latitud. Texto[3]=0. C.Minutos_latitud = atof( Texto + 2 ). C. BuscarCampo( trama. Texto. //Se filtra el campo 3 que contiene la orientación de la latitud. //Se filtra el campo 2 que contiene. Texto. C.Grados_latitud = atoi(Texto). } //Función para encadenar los valores de una coordenada. //Se pega en la cadena de texto.Satelites = atoi(Texto). C. C. 2 ). //Se convierte el texto en el valor numérico de los grados de la longitud. Text ). //Se convierte el texto en el valor numérico. C. 3 ). BuscarCampo( trama.Grados_longitud = atoi( Texto ). float minutos. //Se filtra el campo 4 que contiene los minutos de la longitud. //Se convierte el texto en el valor numérico los minutos de la longitud. 4 ). return C.Altitud = atof(Texto). BuscarCampo( trama. BuscarCampo( trama. C. C. 9 ). //Se filtra el campo 2 que contiene los minutos de la latitud. //Se convierte el texto en el valor numérico. void Convertir_coordenada( char *coor. Texto. float s. 5 ). //Se convierte el texto en el valor numérico de los grados de la latitud. short j=0. BuscarCampo( trama.//Se filtra el campo 9 que contiene la altitud. BuscarCampo( trama. int grados. char Text[20]. j++. Texto[2]=0. Texto. 7 ). while( Text[j]!=0 ) { if( Text[j]!=' ' ) coor[k++]=Text[j]. //Se filtra el campo 7 que contiene el número de satélites detectados.Latitud = Texto[0].

default:Bufer[Pos++]=Dato. //Se pega en la cadena de texto. s *= 60. j++. //Se recibe el carácter Enter. Bandera = 1. Text ). } //Se pega en la cadena de texto. //Se estáblece el fin de cadena. //Se separan los segundos de los minutos. if( PIR1. g = (short)minutos. la orientación. //Se recibe el retroceso del carro. } coor[k++]=':'. coor[k++]=' '. } } 157 . Dato = UART1_Read(). de la fracción de minutos.F5 ) { //Se lee el dato de entrada. while( Text[j]!=0 ) { if( Text[j]!=' ' ) coor[k++]=Text[j]. case 10:break. el valor de los segundos. break.coor[k++]=':'. break. while( Text[j]!=0 ) { if( Text[j]!=' ' ) coor[k++]=Text[j]. void interrupt ( void ) { //Se evalúa si la interrupción disparada por //recepción serial. el valor de los minutos. j=0. IntToStr( g. FloatToStr( s. //Se guardan los datos en el búfer. j=0. s = minutos . Bufer[Pos]=0. //Se pega en la cadena de texto. //Se calculan los segundos.(float)g. coor[k++]=ll.0. } //Declaración de la función de interrupciones. Text ). coor[k]=0. switch( Dato ) { case 13: Pos=0. j++.

Texto). //Se activan las interrupciones globales."Lon "). C. Convertir_coordenada( Texto. while(1) //Bucle infinito. INTCON = 0b11000000.5. FloatToStr(C. Lcd_Out(1.Longitud). Lcd_Init(). Convertir_coordenada(Texto.} void main( void ) { Coordenada C. Lcd_Cmd(_LCD_CURSOR_OFF). if( EsGPGGA( Bufer ) ) { //Se leen los datos de las coordenadas y se guardan en la variable C.Grados_longitud. Lcd_Out(2. UART1_Init(4800). C=LecturaGPS( Bufer ). //Se activa la interrupción por //recepción serial. PIR1 = 0. //Se configuran los puertos.5. Lcd_Out(2.C.1. TRISB = 0.Altitud.C. //Se transcriben los datos de la longitud. //Inicialización del display LCD.Texto). //Configuración del puerto serial a 4800 bps.1.Texto). //Se transcriben el valor de la altitud. Lcd_Out(3.Grados_latitud. C. if( Bandera ) { //Se verifica que la trama sea valida para las coordenadas.Minutos_longitud.Latitud ). C. Lcd_Out(1. PIR2 = 0.C. 158 . //y periféricas. PORTB = 0. PIE2 = 0. //Se imprimen los datos de la latitud en el display LCD."Altitud: "). //Se apagan las banderas de interrupción."Lat "). //Se imprimen los datos de la longitud en el display LCD. { //Se evalúa si una trama ha llegado. char Texto[20]. PIE1 = 0b00100000. //Se transcriben los datos de la latitud. //Se imprimen el valor de la altitud en el display LCD.1.Minutos_latitud. //Se desactivan las demás fuentes de interrupción.

159 . } Bandera=0.Texto).1.11. Este último se debe configurar a 4800 bps. IntToStr(C.Texto). la vista de configuración del puerto virtual COMPIM debe ser la siguiente: Figura 13-4 La configuración del puerto físico que en este caso está determinado como COM1.Texto). //Se transcriben el valor del número de satélites conectados. y el puerto virtual. //Se apaga la bandera de llegada. un circuito con los siguientes dispositivos: PIC 18F452. Lcd_Out(4. puede variar en función de los puertos disponibles en el ordenador.Lcd_Out(3. tanto en el puerto virtual como el puerto real. LM044L. COMPIM. Lcd_Out(4. } } } Después de editar y compilar el programa con fuente de reloj de 20 Mhz.Satelites. //Se imprimen el número de satélites en el display LCD.9. se procede a crear en ISIS."Satelites: ").

en este caso se puede hacer por medio de un simulador de GPS.La construcción del circuito en ISIS debe ser como se aprecia en la siguiente gráfica: Circuito 13-1 La conexión del GPS. es la siguiente: Figura 13-5 160 . o cualquier otro que funcione de la misma forma. la vista del simulador de GPS. o por medio de un GPS real conectado al puerto físico. se puede usar el paquete de software: Virtual GPS. Para la simulación del GPS.

Para fines de este ejemplo se implementarán los radios Laipac. De igual manera las frecuencias portadoras de estos dispositivos son variadas. De la misma forma se pueden cambiar los datos enviados por el simulador de GPS. La apariencia física de estos dispositivos es la siguiente: Figura 13-6 Estos dispositivos solo son un medio de transmisión inalámbrico por lo cual no tienen ningún sistema de corrección ni detección de errores de transmisión. 13.2 Módulos inalámbricos unidireccionales Las comunicaciones inalámbricas entre dos puntos hacen parte fundamental de muchas aplicaciones en el ámbito del control y el monitoreo de variables o estádos. o Linx Technologies. La lectura de un sensor. si se desea hacer la simulación de todo el sistema con un simulador de GPS. o el control de actuadores son posibles por medio de enlaces inalámbricos.En este caso se pueden comparar los datos emitidos por el simulador y su verificación en el circuito hecho en ISIS. y tienen velocidades de transmisión hasta 9600 bits por segundo. Se debe recordar hacer la creación de dos puertos virtuales cruzados. La comunicación usada por estos dispositivos es unidireccional o simplex. entre otros. Este tipo de dispositivos se pueden conseguir de fabricantes como Laipac Tech. y funcionan dentro del espectro de bandas libres para enlaces de comunicación. o FSK. Los módulos inalámbricos útiles para este fin funcionan con modulación ASK. por está razón es importante hacer dentro de la programación alguna rutina de detección de errores. La distribución de pines de estos dispositivos es la siguiente: Figura 13-7 161 .

y por último se transmiten 4 bits con un número de 0 a 8 que contiene el número de unos que contiene el byte que se está transmitiendo como estrategia de verificación. Cuando un dato es recibido correctamente se entrega un byte que puede valer de 0 a 255. //Declaración de constantes de peso binario. const unsigned int Peso[12] = {1.1024.16. sbit RXPIN_TRIS at TRISB7_bit.2.32. 162 .4. en otras palabras el sistema entrega un dato que es recibido correctamente y si el dato se detecta con errores se descarta.8. sbit TXPIN at RB6_bit. Referencia GND. de igual manera los microcontroladores implementados en está librería son los PIC 16F628A con frecuencia de reloj de 4M Hz. sin embargo puede ser usada en cualquier otro microcontrolador. y si el dato está dañado se entrega un valor igual a -1. El programa representa un 1 lógico con un pulso en alto de 2m segundos de duración seguido por 1m segundo en bajo. y 500u segundos en bajo. sbit RXPIN at RB7_bit. //Función para iniciar los pines de comunicación.256.512. posteriormente se envían 8 bits con el byte que se desea transmitir. Para este ejemplo se envían 16 pulsos de sincronismo. la librería básica es la siguiente: //Declaración de pines de comunicación. Referencia Antena Tabla 13-1 Para el envió de bits este ejemplo usa la modulación por ancho de pulso o PWM.2048}. y como estrategia de sincronismo se implementa un pulso en alto de 500u segundos.128. pero si permite verificar el dato recibido. Referencia Entrada serial Vcc. Referencia Salida Serial Salida de prueba Vcc Vcc 5 V GND.64. Para la demostración de está comunicación se implementará una librería que transmite bytes con las reglas antes mencionadas. Este sistema no está en capacidad de corregir errores de transmisión. sbit TXPIN_TRIS at TRISB6_bit.PIN 1 2 3 4 PIN 1 2 3 4 5 6 7 8 TLP434A Función GND. de 5 a 12 V Antena RLP434A Función GND. el 0 lógico se transmite con un pulso en alto de 1m segundo y 1m segundo en bajo.

void Inicio_Radio_Tx( void ) { TXPIN_TRIS=0. while( (D=Entra_Bit())=='S' ). //Se mide el tiempo en alto de la modulación. case '0': BIT++. while( RXPIN ){ delay_us(50). 500u Seg. break. } //Función para iniciar los pines de comunicación. //Aprox. } //Función para enviar un byte. //Se espera un bit de sincronismo. // Tiempo errado. BIT. while( Entra_Bit()!='S' ). //Aprox. //Aprox. } //Función para determinar que bit se está recibiendo. break. int DATO. switch( D ) { case '1': DATO += Peso[BIT++]. while( 1 ) { //Se evalúa el BIT que llego. char Entra_Bit( void ) { unsigned short T=0. 163 . while( !RXPIN ). return 'E'. 1m Seg. //Se espera el último BIT de sincronismo. N_Bits. int Resibir_Dato( void ) { unsigned short D. //Bucle para archivar el byte de entrada. BIT=0. if( T > 18 && T < 22 )return '0'. void Inicio_Radio_Rx( void ) { RXPIN_TRIS=1. case 'E': case 'S': return -1. if( T > 38 && T < 42 )return '1'. TXPIN=0. DATO=0. T++. //Se espera el estádo 1 lógico. } //Se clasifica el bit recibido en función de su tiempo. 2m Seg. if( T > 8 && T < 12 )return 'S'.

N_Bits=0. TXPIN = 0. BITS++ ) { TXPIN=1. else delay_ms(1). } } //Función para transmitir un byte. BITS++ ) { TXPIN = 1. delay_ms(1). BITS<16. se //evalúan los errores. TXPIN=0. delay_us(500). //y se retorna el byte de entrada. BITS++ ) if( (d>>BITS)&1 )N_Bits++. if( (d>>BITS)&1 ) delay_ms(2). return -1. for( BITS=0. D=Entra_Bit(). void Enviar_Dato( unsigned short d ) { unsigned short BITS. } //Se envían 8 bits de datos. for( BITS=0. delay_us(500). el byte.} //Si el número bits recibidos es 12. for( BIT=0. if( N_Bits == (DATO>>8)&0x0F )return DATO&0x00FF. if( BIT==12 ) { //Se cuentan los bits del dato de información. BIT<8. BIT++ ) if( (DATO>>BIT)&1 )N_Bits++. //Se envían 16 bits de sincronismo. //Se verifica que el número de unos sea igual. BITS<8. //Se cuentan el número de bits a enviar. 164 . } //Se envían 4 bits de verificación //con el número de unos en el byte. N_Bits=0. BITS<8. //Ocurre un error y se espera una nueva trama. } //Se lee el siguiente bit. for( BITS=0.

} //Retardo final.for( BITS=0. //Transmisión del byte. BITS++ ) { TXPIN=1. delay_ms(1). } } 165 . //Inicio del puerto de transmisión. y otro para la recepción. } Para concretar el ejemplo se realizan dos programas. y anexar las siguientes funciones main. //Configuración de puertos. en cada uno de los códigos fuente: Función main para el transmisor: void main( void ) { //Declaración de variables. if( (N_Bits>>BITS)&1 ) delay_ms(2). uno para la transmisión. BITS<4. TXPIN=0. //Activación de las resistencias PULL-UP del puerto B. OPTION_REG = 0. De igual manera la simulación puede soportar los dos microcontroladores al mismo tiempo. else delay_ms(1). delay_ms(3). Inicio_Radio_Tx(). while(1) //Bucle infinito. Cada uno de los programas debe tener anexa la librería anteriormente citada. Para fines de simulación se implementa una conexión directa. Enviar_Dato( DATO ). pero no puede simular los radios. sin embargo el desarrollador puede posteriormente hacer una prueba real con los radios. DATO = (~PORTB)&0x0F. TRISB = 0xFF. unsigned char DATO. este sistema transmite el estádo de 4 pulsadores y se puede visualizar en el receptor por medio de 4 LEDs. { //Adquisición de estádos de los pulsadores.

TRISB = 0. //Verificación de errores. { //Lectura del byte de entrada. //Configuración de puertos. DATO = Resibir_Dato(). LEDRED. } } Para realizar la simulación se deben usar los siguientes dispositivos: PIC 16F628A. int DATO. Inicio_Radio_Rx(). if( DATO!=-1 ) PORTB =DATO. y visualización del byte. RES. //Inicio del puerto de recepción.Función main para el receptor: void main( void ) { //Declaración de variables. while(1) //Bucle infinito. en el circuito que se puede ver a continuación: Circuito 13-2 166 . y BUTTON. PORTB = 0.

La simulación de estos radios no es posible con ISIS. son de fácil instalación y programación por medio de un paquete de software. como el caso de los controles realimentados y los sistemas de comunicación full duplex. para este proposito se debe tener presente el siguiente circuito electrónico: 167 . El alcance de los radios depende de la antena y de la referencia de los mismos. este programa estáblece con el radio la comunicación para su programación por medio de un puerto serial COM. suministrado por el fabricante. Para cumplir con el propósito de realizar la comunicación full duplex se pueden implementar módulos de radio prediseñados como los XBee. pero dado el óptimo desempeño de los mismos es equivalente en ISIS a realizar una conexión cruzada entre Tx y Rx. De la misma forma es posible estáblecer una conexión inalámbrica entre un microcontrolador con un ordenador personal por medio de un puerto serial COM.4GHz. este se denomina: X-CTU. este aplicativo se puede descargar gratuitamente en la página: www. que trabajan sobre la banda de 2. Los módulos XBee tienen un alcance de 100 metros con una línea de vista directa y 1000 metros en los módulos XBee PRO. Los módulos XBee incorporan dentro de su hardware y software un procedimiento de detección y corrección de errores en la transmisión. e implementan el estándar IEEE 801.4.15. estos dispositivos son módulos MODEM.Para fines prácticos con los radios Laipac. y velocidades de transmisión de 1200 a 115200 bits por segundo.digi.3 Módulos inalámbricos bidireccionales En algunos casos las comunicaciones deben ser imperativamente bidireccionales. Para trabajar y programar los radios XBee es necesario crear o implementar un acople de hardware para hacer la conexión con un puerto serial COM.com. o XBee PRO. Estos son dispositivos que trabajan con tecnología de 3 voltios de bajo consumo. Los módulos XBee. se bebe hacer un par de circuitos como los que se pueden ver en la siguiente gráfica: Circuito 13-3 13. de los microcontroladores.

sin embargo se puede construir un circuito impreso como el que se puede apreciar en las siguientes gráficas: Figura 13-8 168 .Módulo de acople con un ordenador personal: Circuito 13-4 El anterior circuito electrónico puede ser construido en una protoborad.

unsigned short N=0. sbit LCD_D7 at RB3_bit. sbit LCD_RS_Direction at TRISB4_bit. sbit LCD_RS at RB4_bit. sbit LCD_D4 at RB0_bit. se puede editar y analizar el siguiente código fuente: //Declaración de pines de control para LCD. void main( void ) { //Declaración de variables. se puede asignar un identificador del radio y asignar dirección de trabajo en una red Zigbee. char Dato. y LCD.La vista de este circuito impreso finalizado y del módulo XBee. permite cambiar los parámetros de velocidad. es la siguiente: Figura 13-9 Los módulos XBee por defecto vienen programados para establecer una comunicación a 9600 bits por segundo. sbit LCD_EN_Direction at TRISB5_bit. //Inicio de los módulos USART. 169 . sbit LCD_D7_Direction at TRISB3_bit. Lcd_Init(). sbit LCD_EN at RB5_bit. UART1_Init(9600). sbit LCD_D5_Direction at TRISB1_bit. sbit LCD_D6_Direction at TRISB2_bit. canal de comunicación entre 16 posibles. sbit LCD_D4_Direction at TRISB0_bit. La reprogramación del radio XBee. sbit LCD_D5 at RB1_bit. Para hacer una demostración del funcionamiento con los módulos XBee. char Texto[30]. sbit LCD_D6 at RB2_bit. Sin embargo se puede cambiar por medio del programa X-CTU.

como se puede apreciar en el siguiente esquema: 170 .1. switch( Dato ) { //Se evalúa la llegada de un enter. Lcd_Out(1. break." "). case 13: N=0. Lcd_Out(2. } } } } Para fines de simulación se puede implementar un circuito con los dispositivos: 16F877A.1.1.1. //Se transmite un enter y un retroceso del carro. Texto[N++]=Dato. break. while(1) //Bucle infinito. Texto[N]=0."").Texto)." "). y el puerto virtual VIRTUAL TERMINAL. UART1_Write(10). LM044L. { //Se evalúa si un dato llega por el puerto serial. Lcd_Out(2. Dato = UART1_Read(). UART1_Write(10). UART1_Write(13). UART1_Write_Text(Texto). Lcd_Out(2. //Se transmite el texto: Texto Xbee.1. //Se imprimen 20 espacios vacíos. //Se posiciona el cursor en la segunda línea. //Se retransmite la cadena recibida. UART1_Write(Dato).1.//Se imprimen los textos de inicio." "). UART1_Write_Text("Texto Xbee: "). //Se transmite un enter y un retroceso del carro.""). Lcd_Out(4. default: //Se guardan los datos entrantes en la cadena de caracteres Texto. //Se imprimen 20 espacios vacíos. UART1_Write(13). if( UART1_Data_Ready() ) { //Se lee el dato de entrada. Lcd_Out(2. //Se refresca la información en el LCD.1. //Se limpian las líneas del LCD."Datos de entrada:"). Lcd_Out(2. //Se evalúa que Dato llega por el puerto serial. //Se imprimen 20 espacios vacíos. //Se retransmite eco del dato de entrada.

antes citado con una sesión de Hyper terminal de Windows. Como parte complementaria se puede hacer un circuito real como se puede ver en la siguiente gráfica: Circuito 13-6 171 .Circuito 13-5 Para fines prácticos se puede usar el módulo de acople con un ordenador personal.

el circuito integrado más popular para estás aplicaciones es el MAX485. unsigned short DATO. Como medida adicional se deben implementar un par de resistencias de 120Ω en paralelo a los pines A. Los pines DI. con un alcance máximo de 1000 metros sin la necesidad de implementar dispositivos de repetición. son los habilitadores de los búfer de transmisión y recepción respectivamente. y un control adicional con los pines de habilitación. para este fin se usarán los módulos USART. implementa un par trenzado de cobre para la transmisión de datos. esto quiere decir que la comunicación es bidireccional pero no al mismo tiempo. PORTB = 0. La distribución de pines y la apariencia física de este dispositivo es la siguiente: Figura 13-10 Los pines A. a RS 485. Cuando la diferencia de potencial es positiva el protocolo representa un 1 lógico y cuando la diferencia es negativa representa un 0 lógico. 172 . los pines DE. son el bus de datos bidireccional. y RE. Para la implementación de este tipo de comunicación se usan convertidores integrados que permiten hacer un enlace de niveles TTL. //Se Configura la MAX485 como receptor. De igual forma es posible conectar al bus de datos un máximo de 32 nodos. con una conectividad half duplex.13. y B. La comunicación RS 485. Para contextualizar el uso de la comunicación RS 485. y RO. y B. y B. El protocolo RS 485 permite estáblecer comunicaciones con una velocidad de hasta 2. que es un integrado de 8 pines. son respectivamente los terminales de transmisión y recepción.5M bits por segundo. es un formato de comunicación serial que usa como estrategia de transmisión. se puede observar y analizar el siguiente código de programa que permite enviar el estádo de un potenciómetro para controlar la intensidad de luz de una lámpara en el terminal opuesto: void main( void ) { //Declaración de variables. Para demostrar una comunicación con este tipo de redes se realizará en este capítulo una comunicación de half duplex entre dos microcontroladores. la diferencia de potencial entre dos terminales denominadas A. en el primer y en el último terminal de la red RS 485. //Configuración de puertos. TRISB = 0b11111110. como características notables de este protocolo se tiene que puede estáblecer la comunicación en una topología en forma de bus.4 Comunicación RS 485 El protocolo RS 485.

PWM1_Set_Duty(0). //Se transmite el valor de la variable DATO. //Se Configura la MAX485 como transmisor. PORTB = 0. 50. 1. para sincronizar la comunicación. //Se lee el canal análogo con el voltaje del //potenciómetro. } } } El anterior programa es simétrico. while( !UART1_Data_Ready() ). la diferencia de potencial se obtiene del módulo PWM. 50. //Se Configura la MAX485 como receptor. //Se espera a que se suelte el botón. { //Se evalúa si un dato ha llegado. 173 . El circuito electrónico recurre a una lámpara incandescente con el fin de mostrar la intensidad de la luz. 0) ). PWM1_Set_Duty(DATO). if( Button(&PORTB. DATO = UART1_Read(). delay_ms(1). PWM1_Init(1000). //Se inicia el módulo PWM con 0%. DATO = (ADC_Read(0)>>2)&0xFF. //Se configura el módulo USART a 250000 bps. 0) ) { PORTB = 1.//Se activan las resistencias PULL-UP. //Se actualiza el ciclo útil del PWM. PWM1_Start(). if( DATO==170 ) { //Se espera el siguiente dato. if( UART1_Data_Ready() ) { DATO = UART1_Read(). y se guardan los 8 bits de mayor peso //en el la variable DATO. //Se evalúa si el dato de llegada vale 170. OPTION_REG = 0. 1. es decir que los dos microcontroladores de la simulación se cargan con el mismo programa. } } //Se evalúa si se ha pulsado el botón. while(1) //Bucle infinito. UART1_Init(250000). UART1_Write(170). //Se hace una pausa de 1m segundos entre datos. UART1_Write(DATO). //Se configura el módulo PWM con una frecuencia de 1K Hz. while( Button(&PORTB. //Se transmite la bandera 170.

que tiene características y funcionamiento similar. y referencia. El circuito a implementar es el siguiente: Circuito 13-7 Cuando la simulación está corriendo. y en el momento que se pulsa un botón el microcontrolador hace la conversión análoga digital. tiene un alcance promedio de 10 metros. reproductores de video. ASK. La transmisión de datos se realiza por medio de diodos emisores de luz infrarroja. Los estándares de comunicación para los controles infrarrojos implementan frecuencias portadoras de 36. Los demoduladores ASK. sin embargo para fines de simulación se usa el MAX487. La teoría vista en este capítulo cita el dispositivo MAX485. Sin embargo este estándar es utilizado en la comunicación de control de los electrodomésticos convencionales como: televisores. cada uno de los módulos MAX.5 Módulos inalámbricos infrarrojos La comunicación infrarroja es una estrategia práctica para sistemas que no requieren una gran velocidad de transmisión. Para realizar la simulación se deben implementar los siguientes dispositivos: 16F877A. El alcance de un enlace infrarrojo depende de la potencia del emisor. infrarrojos son dispositivos de carácter integrado que se pueden conseguir comercialmente. y 40 K Hz entre otras. equipos de sonido. y que admiten línea de vista directa. esto quiere decir que se requiere el uso de una resistencia PULL-UP. que puede recibir datos hasta los 2.con la conversión digital análogo. están configurados como receptores. 174 . a Vcc. BUTTON. con una modulación digital por amplitud.4K bits por segundo. La recepción de los datos se hace por medio de un demodulador ASK. RES. Vcc o poder. 13. Estos poseen tres terminales que corresponden a dos de polarización. y POT-HG. MAX487. con colector abierto. y una salida demodulada en amplitud. Para fines ideales de la simulación las características por defecto de la lámpara son reconfiguradas cambiando la impedancia de 24Ω a 100KΩ y su voltaje nominal de 12V a 5V. Algunos fabricantes incorporan está resistencia en la integración del dispositivo. y transmite está información para configurar la intensidad de luz en el módulo opuesto. LAMP. aires acondicionados entre otros. CAP. 38. en este último terminal.

y el dispositivo demodulador. el cual tiene la siguiente apariencia en el simulador: Figura 13-12 Este dispositivo virtual simula el enlace inalámbrico por medio del LED infrarrojo. como fuente portadora. con textos fijos que se pueden ver en un hyper terminal virtual: void main( void ) { //Configuración del módulo USART a 2400 bps. Vcc. PWM1_Start().Para fines de diseño es importante tener presente que la salida del demodulador está invertida. 175 . Para contextualizar el desempeño de estos enlaces se debe observar y analizar el siguiente código fuente. la información transmitida son datos ASCII. Para la simulación de este tipo de enlaces se puede usar en ISIS el módulo virtual: IRLINK. que usa el módulo PWM. sin embargo siempre serán las mismas tres. UART1_Init(2400). Referencia. y el módulo USART. y Salida. //Configuración de PWM para crear la frecuencia portadora a 38K Hz. PWM1_Init(38000). es decir que cuando el dispositivo detecta la señal portadora la salida del demodulador se activa en bajo y se activa en alto cuando se detecta la ausencia de la misma. como señal moduladora. de igual manera permite editar la frecuencia de la señal portadora ASK. para fines prácticos se hace imperativo consultar la ficha técnica del dispositivo que se use. La apariencia física de estos dispositivos es la siguiente: Figura 13-11 La distribución de pines puede variar de un fabricante a otro.

//Se transmite información en un texto fijo. UART1_Write(13). UART1_Write_Text("Con una velocidad de transmision de 2400 bps. 1N4148. UART1_Write(10). //Se transmite información en un texto fijo. //Se realiza una pausa de 200m segundos. UART1_Write(13). UART1_Write_Text("Transmision de datos infrarrojos. 74LS04. UART1_Write(10)."). UART1_Write(13). UART1_Write(13). UART1_Write_Text("Está emision está modulada a 38K Hz. delay_ms(200)."). IRLINK. PWM1_Set_Duty(127). RES. //Se envía los caracteres enter y retroceso del carro.//Se configura el ciclo útil del PWM al 50%. //Se envía los caracteres enter y retroceso del carro. while(1) //Bucle infinito. y el VIRTUAL TERMINAL. UART1_Write(10)."). El circuito electrónico debe ser ensamblado de la siguiente manera: Circuito 13-8 176 . UART1_Write(10). } } Para la simulación de este ejemplo se implementan en ISIS los dispositivos: 16F628A. //Se envía los caracteres enter y retroceso del carro. { //Se transmite información en un texto fijo.

para este fin los microcontroladores cuentan con un módulo de comunicación serial de este tipo. diseñada con diodos rectificadores de switcheo rápido. cuenta con una librería 177 . de gran capacidad como los microcontroladores de la familia 18F. requieren de un protocolo serial conocido como SPI. con la señal portadora.La modulación se realiza por medio de la multiplicación de la señal moduladora. para esto se implementa una compuerta AND. para esto se requiere hacer un acople de voltajes con el microcontrolador por medio de un divisor de voltaje con resistencias. La comunicación de las memorias SD. se requiere trabajar con segmentos de memoria de 512 bytes. La apariencia física y la distribución de pines de este tipo de memorias son las siguientes: Figura 13-14 La capacidad de las memorias SD actuales es del orden de los Giga bytes. es de 1G bytes. el hiperterminal virtual debe mostrar los datos de texto de la siguiente forma: Figura 13-13 13. Para este fin es necesario contar con espacios de memoria RAM. Para leer o escribir sobre una memoria SD. pueden ser manipuladas por los microcontroladores de alta gama como la familia 18F. sin embargo la máxima capacidad que soporta la librería para estás memorias en MikroC PRO. Para la manipulación de este tipo de memorias el compilador MikroC PRO. La alimentación de voltaje de las memorias SD es de 3 voltios.6 Comunicación con memorias SD Las memorias de almacenamiento masivo SD. En funcionamiento de la simulación.

sbit Mmc_Chip_Select_Direction at TRISC2_bit. _SPI_CLK_IDLE_HIGH. //Inicio del puerto SPI. SD = 1.denominada: Mmc. UART1_Init(9600). //Inicio de la memoria. La función: unsigned char Mmc_Write_Sector(unsigned long sector. void main( void ) { //Declaración de variables. unsigned short NN. SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV64. char *dbuff). UART1_Write(13). //"). if( Mmc_Init()==0 ) { //Confirmación exitosa de conexión. para la memoria SD. dos líneas de datos. _SPI_LOW_2_HIGH). unsigned int Cont. char Dato. UART1_Write_Text("// La memoria fue iniciada exitosamente. Para contextualizar el funcionamiento de estos dispositivos se puede observar y analizar el siguiente código fuente: // Pines de conexión para el selector de pastilla sbit Mmc_Chip_Select at RC2_bit. está función inicializa la comunicación con la memoria SD. en las dos funciones se retorna el valor 0 si la operación culmina exitosamente o retornan 1 si un fallo se presenta. unsigned short Bufer[512]. y un selector de pastilla. o retorna 1 si algún error se presenta. char Texto[50]. La cual posee tres funciones para estáblecer de forma simple la comunicación y control de la memoria. e iniciando en el sector definido por el parámetro sector. char SD. char *dbuff). está permite leer un sector de 512 bytes. entrada y salida. UART1_Write(10). Para la comunicación física se requieren 4 conexiones que son: una línea de reloj. La función: unsigned char Mmc_Init( void ). UART1_Write(13). long SEC=0. UART1_Write(10). guardando la lectura en el apuntador dbuff. está función trabaja de forma similar a la función de lectura. _SPI_DATA_SAMPLE_MIDDLE. y retorna 0 si la comunicación se estábleció exitosamente. //Inicio del puerto serial a 9600 bps. La función: unsigned char Mmc_Read_Sector(unsigned long sector. 178 . unsigned short Menu=0.

UART1_Write_Text( "2. { if( SD ) //Se ejecuta el Menú si la conexión fue exitosa." ). 179 . UART1_Write_Text("ERROR no se puede iniciar la memoria !!!")." ). break. { //Se despliega el Menú. case '2': Menu=2. switch( Dato ) { case '1': Menu=1. default: break. UART1_Write(10). break. //Captura del sector de lectura o escritura. UART1_Write(10). UART1_Write(13). UART1_Write(10). UART1_Write_Text( "1. while( Menu==0 ) { if( UART1_Data_Ready() ) { Dato = UART1_Read(). UART1_Write(13). SD = 0. UART1_Write_Text( "Digite una opcion: " ). } } } //Envió del enter y retroceso del carro.} else { //Confirmación fallida de conexión. UART1_Write(10). UART1_Write(Dato). UART1_Write(13). UART1_Write(10). //Bucle para esperar la opción. Menu = 0. Leer un Sector. UART1_Write(13). } while(1) //Bucle infinito. UART1_Write(13). Grabar un Sector. UART1_Write(' ').

y pulse ENTER: " ). case 13: SEC=atol(Texto). Texto[0]=0. UART1_Write(10). UART1_Write(13). Texto). switch( Dato ) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': Texto[NN++]=Dato. UART1_Write_Text( Texto ). //Se graban o se leen los sectores. 180 . //Se borra el Búfer. LongToStr( SEC. Texto[NN]=0. if( Menu==1 ) { UART1_Write_Text( "Se grabara el sector: " ). Cont++ ) Bufer[Cont]=0. while( SEC==-1 ) { if( UART1_Data_Ready() ) { Dato = UART1_Read(). default : break. break. //Bucle para digital el sector. for( Cont=0. } } } //Envió del enter y retroceso del carro. UART1_Write(13). //Envió del enter y retroceso del carro. Cont<512. //Bucle para capturar los datos a grabar. UART1_Write(Dato). NN=0. UART1_Write(10). SEC=-1.UART1_Write_Text( "Digite el numero del sector de memoria.

while( Menu ) { if( UART1_Data_Ready() ) { Dato = UART1_Read(); UART1_Write(Dato); switch( Dato ) { case 13: Menu=0; break; default : Bufer[NN++]=Dato; break; } } } //Se graban los 512 datos del sector en la memoria. Mmc_Write_Sector( SEC, Bufer); //Envió del enter y retroceso del carro. UART1_Write(13); UART1_Write(10); UART1_Write_Text( "Fin de la grabacion." ); } else { UART1_Write_Text( "Se leera el sector: " ); LongToStr( SEC, Texto); UART1_Write_Text( Texto ); //Envió del enter y retroceso del carro. UART1_Write(13); UART1_Write(10); //Se leen 512 datos del sector. Mmc_Read_Sector( SEC, Bufer); //Bucle para imprimir los datos leídos de la memoria. for( Cont=0; Cont<512; Cont++ ) UART1_Write( Bufer[Cont] ); } //Envió del enter y retroceso del carro. UART1_Write(13); UART1_Write(10); //Envió del enter y retroceso del carro. UART1_Write(13); UART1_Write(10); } } } Como medida adicional se debe inicializar el puerto SPI, por medio de la función: SPI1_Init_Advanced. Para comprobar el funcionamiento de de este programa se debe construir en ISIS, un circuito con los dispositivos: 18F452, MMC, y VIRTUAL TERMINAL, como se puede apreciar en la siguiente gráfica: 181

Circuito 13-9

Para fines de simulación las conexiones con la memoria se hacen directamente como se puede ver en el anterior circuito. Sin embargo se debe tener presente que para fines prácticos la memoria trabaja con 3 voltios, y se debe acoplar con un arreglo de resistencias como el siguiente:

Circuito 13-10

182

13.7

Relojes en tiempo real

Algunas aplicaciones requieren de un reloj en tiempo real, para tener dominio del tiempo de eventos, acciones, y demás. Este tipo de relojes son circuitos integrados con baterías propias, y cristales de cuarzo para definir las unidades de tiempo. Los relojes en tiempo real se comunican por medio del protocolo I²C. Una de las referencias comerciales más populares es el DS1307, que puede contar años, meses, días, hora, minutos, y segundos. La apariencia física y la vista en ISIS de estos dispositivos es la siguiente:

Figura 13-15

Este circuito integrado cuenta con dos pines de comunicación un pin de salida oscilante, y una entrada para la batería, además para fines prácticos cuenta con dos terminales para instalar un cristal de cuarzo de 32,768KHz. El siguiente código fuente en lenguaje C, muestra una serie de funciones que forman una librería que permiten manipular este tipo de relojes: //Declaración de estructuras typedef struct { unsigned short Hor, Min, Seg; }Hora; typedef struct { unsigned short Dia, Fec, Mes, Ano; }Fecha; //Función para definir dirección de memoria. void DS1307SetDir( unsigned short dir ) { I2C1_Start(); I2C1_Wr(0xD0); I2C1_Wr(dir); } //Función para convertir de código Bcd a Entero. unsigned short BcdToShort( unsigned short bcd ) { unsigned short LV, HV; LV = bcd&0x0F; HV = (bcd>>4)&0x0F; 183

return LV + HV*10; } //Función para convertir de Entero a Bcd. unsigned short ShortToBcd( unsigned short valor ) { unsigned short HV, LV; HV = valor/10; LV = valor - HV*10; return LV + HV*16; } //Función para inicializar el DS1307. void DS1307Inicio( void ) { unsigned short VAL[7], HV, LV, DATO; I2C1_Init(100000); //Inicio del bus I2C. delay_ms(50); //Retardo. //Lectura de las primeras 7 direcciones. DS1307SetDir(0); I2C1_Repeated_Start(); I2C1_Wr(0xD1); VAL[0] = I2C1_Rd(1); VAL[1] = I2C1_Rd(1); VAL[2] = I2C1_Rd(1); VAL[3] = I2C1_Rd(1); VAL[4] = I2C1_Rd(1); VAL[5] = I2C1_Rd(1); VAL[6] = I2C1_Rd(0); I2C1_Stop(); delay_ms(50); //Retardo. //Validación y corrección de información, //como hora y fecha. DATO = BcdToShort( VAL[0] ); if( DATO > 59 )VAL[0]=0; DATO = BcdToShort( VAL[1] ); if( DATO>59 )VAL[1]=0; DATO = BcdToShort( VAL[2] ); if( DATO>23 )VAL[2]=0; DATO = BcdToShort( VAL[3] ); if( DATO>7 || DATO==0 )VAL[3]=1; DATO = BcdToShort( VAL[4] ); if( DATO>31 || DATO==0 )VAL[4]=1; DATO = BcdToShort( VAL[5] ); if( DATO>12 || DATO==0 )VAL[5]=1; DATO = BcdToShort( VAL[6] ); if( DATO>99 )VAL[6]=0; //Grabación de las primeras 7 direcciones. DS1307SetDir(0); 184

I2C1_Wr(VAL[0]); I2C1_Wr(VAL[1]); I2C1_Wr(VAL[2]); I2C1_Wr(VAL[3]); I2C1_Wr(VAL[4]); I2C1_Wr(VAL[5]); I2C1_Wr(VAL[6]); I2C1_Wr(0x10); //Se activa la salida oscilante 1Hz. I2C1_Stop(); delay_ms(50); //Retardo. } //Función para grabar la hora minutos y segundos. void DS1307SetHora( Hora h ) { DS1307SetDir(0); I2C1_Wr( ShortToBcd(h.Seg) ); I2C1_Wr( ShortToBcd(h.Min) ); I2C1_Wr( ShortToBcd(h.Hor) ); I2C1_Stop(); } //Función para grabar el día, fecha, mes, y año. void DS1307SetFecha( Fecha f ) { DS1307SetDir(3); I2C1_Wr( ShortToBcd(f.Dia) ); I2C1_Wr( ShortToBcd(f.Fec) ); I2C1_Wr( ShortToBcd(f.Mes) ); I2C1_Wr( ShortToBcd(f.Ano) ); I2C1_Stop(); } //Función para leer la hora minutos y segundos. Hora DS1307GetHora( void ) { Hora H; unsigned short VAL[3]; DS1307SetDir(0); I2C1_Repeated_Start(); I2C1_Wr(0xD1); VAL[0] = I2C1_Rd(1); VAL[1] = I2C1_Rd(1); VAL[2] = I2C1_Rd(0); I2C1_Stop(); H.Seg = BcdToShort( VAL[0] ); H.Min = BcdToShort( VAL[1] ); H.Hor = BcdToShort( VAL[2] ); return H; 185

Fec = BcdToShort( VAL[1] ).Seg. unsigned short VAL[4]. return h. h=DS1307GetHora(). h=DS1307GetHora(). VAL[0] = I2C1_Rd(1). void DS1307SetHoras( unsigned short ho ) { Hora h.} //Función para leer el día. mes.Mes = BcdToShort( VAL[2] ). F. h=DS1307GetHora(). unsigned short DS1307GetSegundos( void ) { Hora h. DS1307SetDir(3). unsigned short DS1307GetHoras( void ) { Hora h. y año. Fecha DS1307GetFecha( void ) { Fecha F. } //Función para leer los segundos. h=DS1307GetHora(). return F.Hor. F. I2C1_Stop(). } //Función para leer los minutos. I2C1_Wr(0xD1). } //Función para grabar las horas. VAL[3] = I2C1_Rd(0). 186 . VAL[1] = I2C1_Rd(1). return h. } // Funciones para leer y grabar datos individuales // //Función para leer las horas. VAL[2] = I2C1_Rd(1). F. F.Dia = BcdToShort( VAL[0] ). unsigned short DS1307GetMinutos( void ) { Hora h.Min. I2C1_Repeated_Start().Ano = BcdToShort( VAL[3] ). fecha. return h.

f=DS1307GetFecha(). h. h=DS1307GetHora(). unsigned short DS1307GetFechas( void ) { Fecha f. } //Función para leer la fecha del mes. unsigned short DS1307GetAnos( void ) { Fecha f.Seg = se. return f.h.Fec. } //Función para leer el mes del año. DS1307SetHora( h ).Mes. return f. DS1307SetHora( h ). void DS1307SetSegundos( unsigned short se ) { Hora h. return f. } //Función para leer el día de la semana. 187 .Hor = ho. DS1307SetHora( h ).Dia. } //Función para grabar los segundos. h=DS1307GetHora(). unsigned short DS1307GetDias( void ) { Fecha f. } //Función para grabar los minutos. h. f=DS1307GetFecha(). void DS1307SetMinutos( unsigned short mi ) { Hora h.Min = mi. f=DS1307GetFecha(). } //Función para leer el año. unsigned short DS1307GetMeses( void ) { Fecha f.

} //Función para grabar el año. return f. f=DS1307GetFecha(). 188 . f. //Inicio y configuración del reloj. se puede implementar la anterior librería y el siguiente código fuente correspondiente a la función main: void main( void ) { //Declaración de variables.Mes = me. } //Función para grabar el dia de la semana. void DS1307SetFechas( unsigned short fe ) { Fecha f. DS1307SetFecha(f). unsigned short DATO. f=DS1307GetFecha(). f. } //Función para grabar la fecha del mes. f=DS1307GetFecha(). void DS1307SetAnos( unsigned short an ) { Fecha f. f. f=DS1307GetFecha().f=DS1307GetFecha(). char Text[50].Ano. DS1307SetFecha(f). } //Función para grabar el mes del año.Ano = an. void DS1307SetDias( unsigned short di ) { Fecha f. DS1307SetFecha(f). DS1307SetFecha(f). void DS1307SetMeses( unsigned short me ) { Fecha f. f.Fec = fe.Dia = di. } Para realizar la simulación de un ejemplo con este tipo de reloj.

CRYSTAL. UART1_Write_Text(":"). UART1_Write_Text( Text ). //Se leen la hora y se transmiten por la USART. se implementan los siguientes dispositivos: 16F877A. I2C DEBUGGER. //Se leen los segundos y se transmiten por la USART. while(1) //Bucle infinito. } } Para realizar la simulación pertinente en ISIS. ByteToStr( DATO. UART1_Write_Text( Text ). while( DATO == DS1307GetSegundos() )delay_ms(200). CELL. Text ). UART1_Init(9600). UART1_Write_Text(":"). //Inicio y configuración de la USART a 9600 bps. UART1_Write_Text( Text ). y los instrumentos virtuales. DATO = DS1307GetSegundos(). ByteToStr( DATO. VIRTUAL TERMINAL. DS1307. Text ). DATO = DS1307GetHoras(). Text ). DATO = DS1307GetMinutos(). UART1_Write(10).DS1307Inicio(). { //Se espera el cambio en los segundos. RES. UART1_Write(13). LED-RED. //Se leen los minutos y se transmiten por la USART. ByteToStr( DATO. //Se leen los segundos del reloj. El circuito electrónico correspondiente para simular es el siguiente: Circuito 13-11 189 . DATO = DS1307GetSegundos().

donde T es el periodo de muestreo. esto se logra haciendo una conversión análoga digital. es el tiempo en segundos que existe entre muestra y muestra. Para definir el periodo de muestreo T. sin embargo. realizando tratamientos de mayor complejidad será necesario trabajar con unidades de procesamiento más robustas como la familia de microcontroladores PICMicro ds. para estos se debe utilizar el compilador MikroC PRO para dsPIC. con una frecuencia constante denominada frecuencia de muestreo Fs. la frecuencia de muestreo debe ser como mínimo el doble. de hecho cuanto mayor sea la frecuencia de muestreo mayor será la fidelidad de la señal digitalizada. Sin embargo la frecuencia de muestreo puede ser mayor al doble del máximo en frecuencia de la señal x(t). se debe tener la siguiente consideración: La frecuencia de muestreo debe ser mayor que la máxima componente espectral contenida en la señal x(t). y a su vez es el inverso de la frecuencia de muestreo T = 1/Fs. Este concepto es conocido como teorema de muestreo de Nyquist. Estos microcontroladores no pueden ser programados con el compilador MikroC PRO. Cabe denotar que el alcance de los microcontroladores PICMicro de baja gama como los 16F y 18F sólo permiten realizar tratamientos sobre señales de baja frecuencia. muchas aplicaciones son de utilidad bajo estás características tales como sistemas de comunicación. este capítulo se centra en las nociones de adquisición tratamiento y reconstrucción de señales. es decir Fs = 2x500Hz = 1000Hz. y n es el número de la muestra digitalizada. pero a su vez mayor serán los recursos requeridos en velocidad. De todos modos. o lo que es igual la frecuencia de muestreo Fs. generación de tonos DTMF y adquisición de señales bioeléctricas.1 Muestreo Como primera medida se requiere adquirir muestras periódicamente de la señal análoga. Si el desarrollador desea trabajar y tratar señales de mayor espectro. Un diagrama en bloques del procesamiento digital de una señal se puede apreciar en la siguiente figura: Figura 14-1 14. y memoria de procesamiento. Fs  2FMAX 190 Ecuación 14-1 .14 Tratamiento digital de señales El tratamiento digital de señales es una línea de la tecnología demasiado extensa para ser tratada en un solo capítulo. El muestreo permite convertir una señal continua en una señal discreta. entre otras. es decir que pasa de x(t) a x(Tn). Por ejemplo. El periodo de muestreo T. si la máxima frecuencia contenida en la señal x(t) es 500Hz.

de un sistema discreto es igual a la entrada X. este a su vez determina el orden de la función de transferencia. en su implementación y requieren de campos de memoria pequeños. Sin embargo la operación de convolución puede ser de dos formas. los sistemas FIR. Para el caso del tratamiento de señales. en este caso al realizar la transformación su equivalencia es la convolución.14. para las funciones en términos de la variable z. una en la cual la convolución se hace con las muestras pasadas y actuales de la señal x(n). por está razón siempre se deberá realizar la transformada inversa de z.2 Funciones de transferencia Una función de transferencia es la relación que existe entre la entrada de una señal y su salida al pasar por el bloque de proceso. Esto se debe a que al convertir las funciones a la frecuencia compleja las operaciones que implican manipulación de números complejos y sistemas integro diferenciales que se hacen lineales. y esto simplifica su tratamiento. que representa la frecuencia compleja para el tiempo discreto. A continuación se puede ver un ejemplo: H ( z)  Y ( z) A  X ( z) B Ecuación 14-2 De la ecuación anterior se puede deducir que: H ( z) X ( z)  Y ( z) Ecuación 14-3 Como se puede ver en la anterior ecuación la salida Y. La transformación inversa de la ecuación anterior da como resultado la siguiente ecuación en función del tiempo discreto Tn: h(n) * x(n)  y(n) Ecuación 14-4 De la anterior ecuación se puede observar que se omite la escritura del periodo T. Las funciones de transferencia se denotan por excelencia con la letra H. igual que la salida y(n). esto se conoce como sistema sin memoria o sistemas FIR (Respuestá Finita al Impulso). es finita. por otra parte realizar su diseño y lograr su estábilidad es más complicado y requiere de tediosos procesos matemáticos. y con h. De lo anterior se puede concluir que para realizar un tratamiento digital sobre una señal se debe realizar la operación de convolución continua entre los coeficiente de la función h(n) y la señal x(n). y de y(n). y no tiene relevancia escribirlo siempre. Las funciones de transferencia para el caso de los sistemas de tiempo discreto se hacen en términos de la variable compleja z. Los sistemas FIR. es una serie de muestras de longitud indeterminada. dado que este es un valor constante. para el tratamiento de estos sistemas se recurre a la transformación z. Por otra parte la señal de entrada x(n). ofrecen características contrarias a los sistemas IIR. y el número máximo que asume n se denomina M. la longitud de h(n). Por otro lado se puede observar que la función h y la señal x. multiplicada por la función de transferencia H. son de fácil implementación y diseño. para las funciones en términos del tiempo discreto Tn. Las funciones de transferencia se estudian y se manipulan en términos de la frecuencia compleja y no en el dominio del tiempo continuo. estos sistemas se denominan sistemas con memoria o IIR (Respuestá Infinita al Impulso). pero consumen recursos de 191 . no se multiplican como en la frecuencia compleja. Los sistemas IIR son sistemáticamente más simples. Por otra parte cuando la convolución requiere información de las muestras pasadas de x(n). Se debe recordar que el tratamiento real y la programación se realizan en términos del tiempo discreto.

4 Filtros FIR El diseño de los filtros FIR. Los análisis y ejemplos de este capítulo se concentrarán en los sistemas FIR e IIR. Los filtros pueden ser de cinco naturalezas: Pasa bajas. Para el tratamiento de los filtros se debe tener presente las siguientes consideraciones: Los cálculos de los filtros se hacen en radianes y no en hercios. por lo tanto la ecuación se puede reescribir de la siguiente forma: y (n)   h(k ) x(n  k ) k 0 M Ecuación 14-6 Cada vez que una muestra de la salida y(n).memoria y procesamiento mayor. se requieren M.)x[n]=x[n-1].3 Convolución y ( n)  Para iniciar es importante conocer la estructura de una convolución continua en forma matemática y por medio de un segmento de código en lenguaje C: k    h(k ) x(n  k ) Ecuación 14-5  La ecuación anterior describe la forma general de la convolución. k-. M=17. k>=1. float h[M]. incluida la muestra actual. 192 . 14. es relativamente simple y utiliza estructuras ya definidas para cada tipo de filtro. A continuación se puede observar un ejemplo de cómo se implementar una convolución. pasa bandas. es finita y su máximo es M. rechaza banda. esto quiere decir que para hacer la convolución se debe tener presente la necesidad de un campo de memoria igual a M para guardar las últimas muestras durante el proceso. float yn=0. k<M. en lenguaje C: //Orden del sistema FIR de orden 17. k++ )yn += h[k]*x[k]. una de las virtudes de estos sistemas es que siempre son estábles. for( k=M-1. short k. 14. muestras de la señal x(n). for( k=0. pasa altas. es calculada por medio de la convolución. #define M 17 float x[M]. //Rutina para hacer la convolución. sin embargo se debe recordar que la longitud de la función h(n). x[0]=x0.0. y multi banda.

n  0  Ecuación 14-7  M M n 2 2 La respuestá espectral de este filtro de orden 7.4. La máxima frecuencia tratada por los filtros es Fs/2. para este filtro es:  Sen ( wc n) . La función de transferencia h(n). 14. y F = W/2π.n  0  n  h( n)    wc . es equivalente a una frecuencia en radianes/segundo de 2π. en escala lineal es la siguiente: Figura 14-2 193 . El orden de los sistemas FIR se denota como M. Se recomienda usar un número impar para M. La frecuencia de corte se calcula como Wc = 2 π Fc / Fs. Las frecuencias de corte de los filtros se denotan como Wc. La banda de transición del filtro se denota como dW en radianes/segundo y dF en hercios. La relación que existe entre radianes y hercios es: W = 2 πF. y Fc. o π Radianes.1 Filtro Pasa bajas Los filtros pasa bajas se caracterizan por tener una frecuencia de corte a partir de la cual las frecuencias inferiores son permitidas.La frecuencia de muestreo en hercios Fs. y las frecuencias superiores son atenuadas. A continuación se estudiará la forma de diseño para cada uno de estos filtros.

2 Filtros Pasa Altas Los filtros pasa altos permiten el paso de las frecuencias superiores a la frecuencia de corte. y suprimen las inferiores a la misma.4. n  0    M M n 2 2 194 Ecuación 14-9 .n  0   n h( n)   1  wc . El cálculo de la función de transferencia h(n). La función de transferencia para este tipo de filtros se calcula por medio de la siguiente ecuación:  Sen ( wc 2 n)  Sen ( wc1n) . y el resto de frecuencias son eliminadas.n  0   n h( n)    wc 2  wc1 .3 Filtro Pasa Banda Los filtros pasa banda permiten el paso de una porción de frecuencias entre Wc1 y Wc2. es la siguiente: Figura 14-3 14.14.4. para estos filtros está definida por la siguiente ecuación:   Sen ( wc n) . n  0    M M n 2 2 Ecuación 14-8 La respuestá espectral en escala lineal de este filtro en orden 17.

4. la función que caracteriza los coeficientes de la función de transferencia es la siguiente:  Sen ( wc1n)  Sen ( wc 2 n) .n  0   n h( n)   1  wc 2  wc1 . es la siguiente: Figura 14-5 195 . es la siguiente: Figura 14-4 14.La respuestá espectral en escala lineal de este filtro en orden 17. n  0    M M n 2 2 Ecuación 14-10 La respuestá espectral en escala lineal de este filtro en orden 17.4 Filtro Rechaza Banda Los filtros rechaza banda tienen un comportamiento similar a los filtros pasa banda. y su proceder es inverso a los filtros pasa banda.

y las frecuencias de corte w: L  sen( w n)   ( Al  Al 1 ) n l . estos pueden dar ganancias arbitrarias en un rango de frecuencias.4. n  0   l 1   h( n)   L  ( A  A ) wl . 14. el objetivo de las ventanas es mejorar y suavizar la respuestá espectral de los filtros FIR.6 Ventanas fijas Las ventanas se aplican a las funciones de transferencia h(n). con cuatro bandas diferentes: Figura 14-6 Para fines de diseño se debe tener presente que la ultima frecuencia calculada debe ser igual a π. Este tipo de filtros son ideales para el diseño de sistemas que requieran efectuar ecualización. La siguiente ecuación integra las ganancias A del filtro. n  0 l 1   l    l 1  Ecuación 14-11 M M n 2 2 La siguiente gráfica muestra la respuestá.5 Filtro Multi Band Los filtros multi banda son arreglos que integran diferentes filtros en uno solo. Las ventanas de mayor uso son las siguientes:  Rectangular  Hamming  Hanning  Blackman 196 .4. sobre una señal.14. de un filtro multi banda de orden 65.

sin embargo está propiedad produce en los filtros sobresaltos y oscilaciones en el espectro.4. La aplicación de las ventanas involucra la creación de una nueva función de factores w(n).7 Ventanas Rectangulares Una ventana rectangular ofrece un patrón lineal. Este efecto puede apreciarse en la siguiente gráfica: Figura 14-7 La ventana Rectangular ofrece una banda de transición igual a la siguiente relación: dW = 1. para crear la función h(n) definitiva. Este efecto es conocido como: fenómeno Gibbs. por defecto ya se encuentra con una ventana de este tipo. Se debe recordar que al realizar el cálculo de un filtro FIR.Los filtros que se demostraron anteriormente por defecto son filtros con ventana rectangular. Una ventana Rectangular se representa por medio de la siguiente función w(n): Figura 14-8 La respuestá espectral de la ventana Rectangular en escala logarítmica es la siguiente: 197 . que posteriormente debe ser multiplicada término a término con la función de transferencia h(n). lo que los hace más cercanos a los filtros ideales. Estos filtros tienen la menor transición en la frecuencia de corte. y constante.8π/M 14.

Figura 14-9 La ventana Rectangular ofrece una atenuación del fenómeno Gibbs de -13dB. en la figura (14.8 Ventanas Hamming Para el caso de la ventana Hamming. se calculan de la siguiente forma: w(n)  1  Cos(2n / M ) .2π/M Una ventana Hamming se representa por medio de la siguiente función w(n): 198 .0  n  M 2 Ecuación 14-12 La misma respuestá espectral aplicando una ventana Hamming.7) del fenómeno Gibbs se ve de la siguiente forma: Figura 14-10 La ventana Hamming ofrece una banda de transición igual a la siguiente relación: dW = 6. los factores w(n). 14.4.

9 Ventanas Hanning Para el cálculo de la ventana Hanning. se deducen de la siguiente forma: w(n)  27  23Cos(2n / M ) . los factores w(n). 14.7) del fenómeno Gibbs se ve de la siguiente forma: 199 .0  n  M 50 Ecuación 14-13 La misma respuestá espectral aplicando una ventana Hanning. en la figura (14.4.Figura 14-11 La respuestá espectral de la ventana Hamming en escala logarítmica es la siguiente: Figura 14-12 En la ventana Hamming se ofrece una atenuación del fenómeno Gibbs de -31dB.

6π/M Una ventana Hanning se representa por medio de la siguiente función w(n): Figura 14-14 La respuestá espectral de la ventana Hanning en escala logarítmica es la siguiente: Figura 14-15 En la ventana Hanning se ofrece una atenuación del fenómeno Gibbs de -41dB.Figura 14-13 La ventana Hanning ofrece una banda de transición igual a la siguiente relación: dW = 6. 200 .

en la figura (14.14.0  n  M Ecuación 14-14 50 Para el diseño de una ventana Blackman.4. los factores w(n).10 Ventanas Blackman w(n)  21  25Cos(2n / M )  4Cos(4n / M ) . se calculan de la siguiente forma: La misma respuestá espectral aplicando una ventana Blackman.7) del fenómeno Gibbs se ve de la siguiente forma: Figura 14-16 La ventana Blackman ofrece una banda de transición igual a la siguiente relación: dW = 11π/M Una ventana Blackman se representa por medio de la siguiente función w(n): Figura 14-17 La respuestá espectral de la ventana Blackman en escala logarítmica es la siguiente: Figura 14-18 201 .

RES. un PLL que multiplica la frecuencia externa por un factor de cuatro.5 voltios. Para evitar las circunstancias antes nombradas. los niveles de tensión que las entradas análogas admiten. El parámetro offset se configura a 2. OSCILLOSCOPE. Para usar los generadores de señal virtual en ISIS. 14. y Amplitude. se debe manipular la configuración del generador virtual de señal. La adquisición de señales se hace en el microcontrolador por medio del módulo AD. Para fines de simulación el parámetro Frequency.La ventana Blackman ofrece una atenuación del fenómeno Gibbs de -57dB.5 Ejemplos de diseño para filtros FIR En la siguiente sección se mostrarán ejemplos de diseño para filtros FIR. y se pega dentro del área de trabajo. y una amplitud de 1 voltio. no pueden salirse de los confines de la polarización del microcontrolador. sobre el generador y se editan los parámetros: Offset. puede ser alterado al gusto del desarrollador. y el parámetro Amplitude a 2 voltios. Para todos los ejemplos FIR que se mostrarán se deben simular con el mismo arreglo en ISIS. y el resultado es usado como fuente de reloj para el procesador del microcontrolador. En otras palabras los niveles de las entradas análogas no pueden ser superiores a 5 voltios. se hace doble clic. se logra utilizando un cristal de 10MHz. para este fin se implementa un circuito con los dispositivos 18F452. Está opción implementa internamente en el PIC. dentro de este se escoge la opción SINE. Una vista de está edición se puede apreciar en la siguiente figura: Figura 14-19 202 . Este generador tiene por defecto una frecuencia de 1Hz. implementando un microcontrolador 18F452. este tiene la siguiente apariencia visual: . y activando la fuente de reloj HSPLL en el PIC. Está acción evita que las señales generadas se salgan de los parámetros de PIC. con una fuente de 40MHz como reloj. ni voltajes negativos. Para fines prácticos reales la frecuencia de 40MHz. se debe picar en la barra de herramientas de la izquierda el icono de Generator Mode. Generador virtual Seno. para este propósito.

PORTB = YY&255. PORTC = (YY>>8)&3.F2 ) { TMR0L=135. se puede implementar el siguiente circuito electrónico en ISIS: Circuito 14-1 Para la reconstrucción de la señal procesada. por medio de un arreglo R-2R. void interrupt ( void ) { if( INTCON. //Timer0 con periodo de 774. y por defecto la frecuencia de muestreo. unsigned int YY. para hacer un convertidor DA.0). se configuran 10 bits de salida.32 Hz. se usará la interrupción por TIMER 0. x0 = (float)(ADC_Read(0)-512. x[0].Indiferente al ejemplo que se simule en está sección.4u segundo. // Fs = 1291. //Adquisición de una muestra de 10 bits en. //Declaración de la función de interrupciones. Para todos los ejemplos usados en este apartado. para crear el periodo de muestreo. y0. float x0. // //Espacio para procesar la señal. El siguiente código fuente muestra un ejemplo del muestreo de la señal por medio del TIMER 0: //Declaración de varíales. YY = (unsigned int)(x0+512). 203 . // //Reconstrucción de la señal: y en 10 bits.

//Se configura el TIMER 0.INTCON.72985.0309497847516785 h(-4)=0.0171035387965417 h(-7)=-0.7).0864308262744764 h(4)=0. se designan los coeficientes de la función h(n): h(-8)=-0.32KHz.32 = 0. INTCON = 0b10100000. Usando la formula (14. Fc = 150Hz.0501329294124475 h(7)=-0.0309497847516785 h(6)=-0. Se determina la frecuencia de corte digital: Wc = 2 π Fc / Fs = 2 π 150 / 1291.158173992108178 h(3)=0. TRISB = 0.0175345019583181 h(-3)=0.212237065988464 h(2)=0.0864308262744764 h(-2)=0.0501329294124475 h(-5)=-0. PORTC = 0.158173992108178 h(-1)=0. TRISC = 0. } } void main( void ) { //Inicio del puerto B como salida.0171035387965417 204 .232320416318186 h(1)=0.5.212237065988464 h(0)=0.0419431579233366 h(-6)=-0. Ventana Rectangular. PORTB = 0. { } } 14. su interrupción. T0CON = 0b11000101. while(1)//Bucle infinito.0175345019583181 h(5)=-0.F2=0.1 Ejemplo de diseño para filtro pasa bajas Fs = 1291.0419431579233366 h(8)=-0.

0501329294124475.0501329294124475. //h(8) 0.158173992108178. //h(12) -0. //h(7) 0.0309497847516785. //h(5) 0.212237065988464.0309497847516785.158173992108178.0175345019583181.0171035387965417 //h(16) }.212237065988464. //h(6) 0. //h(2) -0. //h(1) -0.0864308262744764. //h(0) -0. //h(2) -0.0501329294124475. 205 . y para usarlo en el código en lenguaje C. //h(11) 0. //h(3) 0. //h(9) 0. //h(15) -0.Se debe tener presente que para fines de programación los valores de n no pueden ser negativos.0175345019583181.158173992108178. El programa definitivo con la función de transferencia h(n). //h(10) 0. por está razón se debe iniciar el vector en 0. //h(5) 0.0175345019583181. //h(0) -0.0501329294124475. será de la siguiente forma: #define M 17 //Función de trasferencia h[n] const float h[]= { -0. //h(8) 0. //h(13) -0. //h(10) 0.0175345019583181.0309497847516785.0171035387965417. //h(6) 0. //h(14) -0.0309497847516785. //h(9) 0. //h(14) -0.0171035387965417 //h(16) }.232320416318186.0171035387965417.158173992108178.0864308262744764.0419431579233366. //h(7) 0.232320416318186. //h(12) -0.0864308262744764.212237065988464. //h(3) 0. se implementa de la siguiente manera: const float h[]= { -0. //h(13) -0. //h(1) -0.0419431579233366.0419431579233366.212237065988464. //h(4) 0. //h(4) 0. //h(11) 0. //h(15) -0.0419431579233366.0864308262744764.

//Declaración de la función de interrupciones. i<M. //Reconstrucción de la señal: y en 10 bits. y0.)x[i]=x[i-1]. su interrupción.32 Hz. PORTB = YY&255.4u segundo. T0CON = 0b11000101. } } void main( void ) { //Inicio del puerto B como salida. float x[M]. // Fs = 1291. y teniendo presente la frecuencia de muestreo de 1291. //Convolución continúa. unsigned int YY. y0 = 0. x[0]. INTCON. PORTC = (YY>>8)&3.0. unsigned short i.32Hz no es posible usar un orden del filtro superior a 17. 13. i!=0. PORTB = 0. //Adquisición de una muestra de 10 bits en. o 3. 206 .F2 ) { TMR0L=135. //Corrimiento continuo de la señal x[n] for( i=M-1. Sin embargo es posible usar órdenes menores como. INTCON = 0b10100000. x[0] = (float)(ADC_Read(0)-512. Para fines prácticos y didácticos en los próximos ejemplos se implementarán filtros de orden 17 igual a este. PORTC. void interrupt ( void ) { if( INTCON.F2=0. //Timer0 con periodo de 774. 15. PORTC. 5. i++ ) y0 += h[i]*x[i]. while(1)//Bucle infinito. TRISC = 0. //Se configura el TIMER 0.0). 9.//Declaración de varíales. 11. float x0. YY = (unsigned int)(y0+512. for( i=0. { } } Por limitaciones de velocidad en este microcontrolador.0). PORTC = 0.F7=0.F7=1. 7. i-. TRISB = 0.

sustituyendo el arreglo h[].0246447931670207 h(-3)=-0.2 Ejemplo de diseño para filtro pasa altas Fs = 1291. Usando la formula (14.0345797805794857. //h(2) 0. //h(10) -0.0345797805794857 h(5)=0. //h(5) -0.260893089082499.0187342667800403. //h(11) 0.32KHz. //h(8) -0.12).00631989035694063 h(7)=-0.8). //h(4) -0.000774581987451374.028555061237806.00297591407324525.0174357425540551 h(-4)=0.0148886974624375 h(-2)=-0. Fc = 200Hz.028555061237806 h(6)=0. Se determina la frecuencia de corte digital: Wc = 2 π Fc / Fs = 2 π 200 / 1291.260893089082499 h(2)=-0. //h(13) 207 . //h(6) -0.0174357425540551.0148886974624375. //h(9) -0.118648252092459. //h(12) 0.243426834227261 h(0)=0. //h(7) 0. //h(0) -0.136977589378571.0187342667800403 h(4)=0.00134023946307802 La implementación del código fuente es igual al filtro pasa bajas.14. Ventana Hamming.243426834227261. //h(3) 0. por el siguiente: const float h[]= { 0.684363125797732.32 = 0. se definen los coeficientes de la función h(n) multiplicados por la ventana Hamming w(n) de la ecuación (14.000774581987451374 h(-6)=0. //h(1) 0.00297591407324525 h(-5)=0.0246447931670207. dando como resultado la siguiente función h(n): h(-8)=0 h(-7)=-0.5.684363125797732 h(1)=-0.136977589378571 h(3)=-0.118648252092459 h(-1)=-0.97314.00299371636029323 h(8)=-0.

13).218010454414228 h(-1)=0. //h(7) 208 . Wc2 = 2 π Fc / Fs = 2 π 400 / 1291.0879236273273945.107904878578323 h(4)=0. //h(2) 0.0.248392736747743 h(3)=-0. Usando la formula (14.0879236273273945 h(-2)=-0.00299371636029323. Se determinan las frecuencias de corte digital: Wc1 = 2 π Fc / Fs = 2 π 150 / 1291.72985.00631989035694063. dando como resultado la siguiente función h(n): h(-8)=0.00299868100666665 h(7)=0.0018052104538582 h(-7)=0.00250614601893693 La función h[]. //h(6) 0.0307760790492056. //h(15) -0. //h(1) 0.384167993159943 h(1)=0. Ventana Hanning.00179096961917994.0411879382357919 h(5)=0. 14.32 = 1.9).00583790841302624 h(6)=0.5.32 = 0.078115497545518. Fc1 = 150Hz. se definen los coeficientes de la función h(n) multiplicados por la ventana Hanning w(n) de la ecuación (14. //h(0) 0. //h(14) -0. //h(4) -0.94628.0018052104538582. para implementar en el código fuente en lenguaje C es: const float h[]= { 0.3 Ejemplo de diseño para filtro pasa banda Fs = 1291.0832388331308223 h(2)=-0.218010454414228. //h(5) -0.00393014193876244. //h(3) 0.00393014193876244 h(-4)=0.0307760790492056 h(-3)=-0.32KHz.00134023946307802 //h(16) }.00905810215732946.00905810215732946 h(-6)=0.0163162389068139 h(8)=0.00179096961917994 h(-5)=0.078115497545518 h(0)=0. Fc2 = 400Hz.

186587834540544 h(-1)=-0. //h(12) 0.0182611591144528 h(-3)=0.384167993159943. para implementar en el código fuente en lenguaje C es: const float h[]= { 3. dando como resultado la siguiente función h(n): h(-8)=3. Se determinan las frecuencias de corte digital: Wc1 = 2 π Fc / Fs = 2 π 150 / 1291.13154095338657E-19.0827284691705043 h(2)=0. //h(15) 0. //h(8) 0.32 = 0.0.94628.0411879382357919.00346395569934133 h(6)=-0. //h(10) -0.000518135020348656 h(-5)=-0. Wc2 = 2 π Fc / Fs = 2 π 400 / 1291.00250614601893693 //h(16) }.0738928265716166 h(0)=0. 14.00472035632958784 h(8)=-0. Usando la formula (14.0645431455464317 h(-2)=0. //h(13) 0. //h(9) -0. Ventana Blackman.107904878578323.0832388331308223. Fc2 = 400Hz.000290742652784183 La función h[].00583790841302624. se definen los coeficientes de la función h(n) multiplicados por la ventana Blackman w(n) de la ecuación (14. Fc1 = 150Hz.00174730211401576 h(-4)=-0. //h(14) 0. //h(1) 209 . //h(0) -0.14).13154095338657E-19 h(-7)=-0.32KHz.32 = 1.00133318382487159 h(7)=-0.234965429330525 h(3)=0.0163162389068139. //h(11) 0.00105084724932717.0302353209611255 h(5)=-0.4 Ejemplo de diseño para filtro rechaza banda Fs = 1291.248392736747743.604271792109402 h(1)=-0.72985.00299868100666665.5.0923521657912548 h(4)=-0.00105084724932717 h(-6)=-0.10).

000518135020348656. //h(15) -0.234965429330525.604271792109402. //h(2) -0.0827284691705043.0923521657912548. A2 = 0. Ventana Rectangular.00133318382487159.0685784954060509 h(-5)=0.32 = 0. //h(11) -0. //h(4) 0. //h(3) -0.674596536876994 h(1)=0.186587834540544. Wc2 = 2 π Fc2 / Fs = 2 π 322 / 1291. se definen los coeficientes de la función h(n): h(-8)=-0. A4 = 1.000403386658426763 h(-7)=-0. //h(12) -0.2. Fc2 = 322Hz. Usando la formula (14. //h(5) 0.0738928265716166. Wc4 = 2 π Fc4 / Fs = 2 π 645.00472035632958784.5 Ejemplo de diseño para filtro multi banda Fs = 1291. //h(10) 0.0330418473673128 h(-4)=-0. A1 = 1. //h(7) 0.-0.207286062892996 210 .000367826230314909 h(-3)=-0. //h(9) 0.66 / 1291.00174730211401576.0645431455464317.0538949660023408 h(-2)=0.32 = π.66Hz.78337.32KHz.00346395569934133. Fc3 = 484Hz.5.11).0275264614524777 h(2)=0.355. Fc1 = 161Hz.0302353209611255. Wc3 = 2 π Fc3 / Fs = 2 π 484 / 1291.0275264614524777 h(0)=0. //h(13) -0.56675.32 = 2. //h(8) -0. 14.00443133542115946 h(-6)=-0. //h(14) -0.207286062892996 h(-1)=0.32 = 1.000290742652784183 //h(16) }.0182611591144528. Fc4 = 645. //h(6) -0.5. Se determinan las frecuencias de corte digital: Wc1 = 2 π Fc1 / Fs = 2 π 161 / 1291. A3 = 0.

//h(8) 0. //h(13) -0.000403386658426763. //h(0) -0.0330418473673128 h(6)=-0.0275264614524777. //h(14) -0.0275264614524777.0538949660023408 h(4)=-0.0685784954060509 h(7)=-0. //h(15) -0.000367826230314909 h(5)=0. //h(4) -0. //h(7) 0. //h(16) }. //h(10) -0. //h(1) -0.0330418473673128. //h(11) -0. //h(12) 0.0685784954060509.0538949660023408. 211 .0685784954060509.0538949660023408. //h(6) 0.00443133542115946. //h(3) -0. para implementar en el código fuente en lenguaje C es la siguiente: const float h[]= { -0.000367826230314909. //h(2) 0.000403386658426763.000403386658426763 La función h[].207286062892996.000367826230314909.674596536876994.00443133542115946 h(8)=-0.h(3)=-0.0330418473673128. //h(9) 0. //h(5) 0.00443133542115946.207286062892996.

produce en la respuesta de la señal distorsiones mayores a las que se generan con los filtros FIR. El coeficiente  . Las frecuencias Ω B representan el ancho de banda de los filtros pasa banda y rechaza banda. 212 . cuando el coeficiente de amortiguamiento vale 0. Las frecuencias Ωc representan la frecuencia de corte en los filtros pasa bajas. y tendría un comportamiento similar a los filtros pasa banda o rechaza banda. Esto quiere decir que para realizar un filtro IIR. Las frecuencias Ωo representan la frecuencia de resonancia en los filtros pasa banda y rechaza banda. representa el amortiguamiento del filtro. son de implementación computacional más simple. existen diferentes técnicas. pero su diseño y sus cálculos son de mayor complejidad. el filtro se convierte en resonante. En síntesis para iniciar el diseño de un filtro IIR. La transformación bilineal es una relación que existe entre la variable compleja s. Cabe denotar que la implementación de filtros IIR. se requiere una función de transferencia en términos de la variable s.14. se requiere como punto de partida. y z.6 Filtros IIR Los filtros IIR. la función de transferencia de un filtro análogo. Para diseñar un filtro IIR. el valor que este coeficiente debe tomar es mayor que 0. y altas. esto generaría una función de transferencia inestable. Por otra parte el coeficiente de amortiguamiento no podrá ser negativo. sin embargo este capítulo se centrará en el diseño por medio de la transformación bilineal. Para fines de estudio en este capítulo se mostrarán a continuación las funciones de transferencia de los filtros básicos de segundo orden: Filtro Pasa Bajas: 2 c H ( s)  2 2 s  2c s  c Ecuación 14-15 Filtro Pasa Altas: s2 H ( s)  2 2 s  2c s  c Filtro Pasa Banda: Ecuación 14-16 H ( s)  s B 2 s 2   B s  0 Ecuación 14-17 Filtro Rechaza Banda: 2 s 2  0 H ( s)  2 2 s   B s  0 Ecuación 14-18 Las frecuencias Ω están definidas en radianes/segundo.

y Ts.19).1 Transformación bilineal 2(1  z 1 ) 2 Fs (1  z 1 ) s  Ts (1  z 1 ) (1  z 1 ) La transformación bilineal es una relación matemática entre las variables complejas s. 14. y Fs.15). la frecuencia análoga es: Ω. la relación entre la frecuencia digital y la frecuencia en hercios es la siguiente: 2Fx wd   x  Fs 2 tan( Fx Fs )  2 Fs tan( Fx Fs Ts ) Ecuación 14-21 Para contextualizar este proceso. De la misma forma existe una relación entre las frecuencias del filtro análogo y el filtro digital. se realiza el reemplazo de variables en la ecuación (14. Está relación se rige por las siguientes ecuaciones: Ecuación 14-19 En la ecuación (14. donde Ts. De la misma forma que en los filtros FIR. y z. se puede observar la relación entre las dos variables. son respectivamente el periodo de muestreo y la frecuencia de muestreo.6. se mostrará a continuación la transformación bilineal de los cuatro filtros básicos en segundo orden.14. para obtener la función h(z): 2 c H ( z)  2  2 Fs (1  z 1 )   2 F (1  z 1 )  2  2c  s 1   c  (1  z 1 )     (1  z )  H ( z)  2 c (1  z 1 )2 2 4 Fs2 (1  z 1 )2  4c Fs (1  z 1 )(1  z 1 )  c (1  z 1 )2 2 c (1  2 z 1  z 2 ) 2 4 Fs2 (1  2 z 1  z  2 )  4c Fs (1  z  2 )  c (1  2 z 1  z  2 ) 2 2 2 c  2c z 1  c z 2 2 2 2 [4 Fs2  4c Fs  c ]  [2c  8Fs2 ]z 1  [4 Fs2  4c Fs  c ]z  2 H ( z)  H ( z)  213 . Fs. son respectivamente el periodo de muestreo y frecuencia de muestreo implementado.2 Filtro Pasa Bajas IIR Como primera medida. está relación se puede apreciar en la siguiente ecuación:  2 tan( wd ) 2  2 F tan( wd ) s Ts 2 Ecuación 14-20 Donde la frecuencia digital es: Wd.6.

de la forma H(z) = A(z)/B(z). se tiene una doble convolución.En este punto se tiene una función de transferencia con polinomios ordenados en los polos y los ceros. Está condición aplicada en la última ecuación del filtro pasa bajas. B( z )  b1z 0  b2 z 1  b3 z 2 . es de la siguiente forma: H ( z)  a1  a1 z 0  a2 z 1  a3 z 2 . se tiene en este caso expresiones 0 1 2 de segundo orden con la forma: A( z )  a1z  a2 z  a3 z . Para lograr este objetivo todos los coeficientes a y b se dividen en b1. para deducir la ecuación en diferencias es necesario forzar el coeficiente del denominador b 1 a valer 1. Después de tener la función de transferencia de esta forma se debe implementar una ecuación en diferencias para la ejecución en la programación. Para los polinomios A. H ( z)  Y ( z ) A( z )   Y ( z ) B( z )  X ( z ) A( z )  y(n) * b(n)  x(n) * a(n) X ( z ) B( z ) Realizando la transformación inversa de z. que genera una ecuación de la siguiente forma: b1 y(n)  b2 y(n  1)  b3 y(n  2)  a1x(n)  a2 x(n  1)  a2 x(n  2) En conclusión la salida y(n) queda de la siguiente manera: y(n)  a1x(n)  a2 x(n  1)  a2 x(n  2)  b2 y(n  1)  b3 y(n  2) 214 . B. b1 z 0  b2 z 1  b3 z  2 2 c 2 4 Fs2  4c Fs  c a2  2a1 a3  a1 b1  1 2 2c  8 Fs2 b2  2 4 Fs2  4c Fs  c b3  2 4 Fs2  4c Fs  c 2 4 Fs2  4c Fs  c Para realizar la ecuación en diferencias se hace la siguiente transformación de z a n. da como resultado lo siguiente: 2 2 2 c 2c c  z 1  z 2 2 2 2 [4 Fs2  4c Fs  c ] [4 Fs2  4c Fs  c ] [4 Fs2  4c Fs  c ] H ( z)  2 2 [2c  8Fs2 ] [4 Fs2  4c Fs  c ]  2 1 1 z  z 2 2 [4 Fs2  4c Fs  c ] [4 Fs2  4c Fs  c ] La misma ecuación en términos de los coeficientes.

0436286852600961 b1  1 b2  2 2 c  8Fs2  2 4 Fs2  4 c Fs   c 2(641. y0=0. y pasa altas de segundo orden el valor de  debe ser 2 para que la frecuencia de corte no sufra desplazamientos con respecto a las condiciones establecidas para los filtros en general. Este paso se puede apreciar a continuación: H ( z)  a1  a1 z 0  a2 z 1  a3 z 2 .21).0154)(1291. 215 . x1=0.0154) 2 2 4(1291.3   2 . A continuación se puede observar el siguiente ejemplo en lenguaje C: //Declaración de varíales.32)  4( )(641.32843480532316 2 2 2 4(1291.0154) 2 2 )(641.0154)(1291. Para deducir la frecuencia de corte análoga en función de las frecuencias reales se usa la ecuación (14. 2 2 Ejemplo de diseño para filtro pasa bajas Fs = 1291.32) tan( )  641.32 El paso siguiente es remplazar las constantes en las relaciones que determinan los coeficientes a.32)  (641. requiere un búfer de datos igual a: 2(N+1). y 3 campos más para los 3 últimos valores de la salida y.32)  4( )(641.32) 2  4( 2 4 F 2  4 c Fs   c b3  s2  2 4 Fs  4 c Fs   c La implementación de un filtro IIR.0436286852600961 a2  2a1  0.0154 Fs 1291. b1 z 0  b2 z 1  b3 z 2 (641.32) 2  -1. y1=0.32)  4( )(641.154) 2 2 4(1291.502949546363549 2 2 4(1291. de orden N. dando como resultado la siguiente frecuencia de corte: c  2 Fs tan( Fc 100 )  2(1291.0154)(1291. float x0=0. Para ilustrar la implementación de este proceso se usará la misma arquitectura de software y hardware implementada en los filtros FIR.0154) 2 2 2 2 c  2 4 Fs2  4 c Fs   c  0. y b. x2=0.0154) 2  8(1291.0154)(1291.32)  (641.32Hz.14.32)  (641.0872573705201923 a3  a1  0. Fc = 100Hz. En el caso de un filtro de segundo orden se requieren 3 campos para guardar los últimos valores de la entrada x.32)  (641. y2=0. Para el caso particular de los filtros pasa bajas.0154) 2 2  0.6.

para obtener la función h(z): 216 . PORTB = YY&255. PORTB = 0.4u segundo.6. TRISC = 0.32 Hz. INTCON = 0b10100000. PORTC = 0. //Declaración de la función de interrupciones.0872573705201923 + y1*1. x0 = (float)(ADC_Read(0)-512. x1 = x0. x[0]. y2 = y1. void interrupt ( void ) { if( INTCON.0). TRISB = 0. PORTC = (YY>>8)&3. while(1)//Bucle infinito. y1 = y0. INTCON. //Adquisición de una muestra de 10 bits en. T0CON = 0b11000101. YY = (unsigned int)(y0+512.0436286852600961 + x1*0. y0 = (x0+x2)*0. //Reconstrucción de la señal: y en 10 bits. y y(n).16). x2 = x1.4 Filtro Pasa Altas IIR De la misma forma que se hizo en el filtro pasa bajas. // Fs = 1291.F2 ) { TMR0L=135.unsigned int YY.32843480532316 -y2*0. //Implementación de la ecuación en diferencias. } } void main( void ) { //Inicio del puerto B como salida.0).502949546363549.F2=0. //Corrimiento de los valores x(n). { } } 14. se realiza el reemplazo de variable en la ecuación (14. //Timer0 con periodo de 774. su interrupción. //Se configura el TIMER 0.

 2 Fs (1  z 1 )   (1  z 1 )    H ( z)  2 1  2 Fs (1  z )   2 F (1  z 1 )  2  2c  s  c  (1  z 1 )  (1  z 1 )      H ( z)  4 Fs2 (1  z 1 ) 2 2 4 Fs2 (1  z 1 ) 2  4c Fs (1  z 1 )(1  z 1 )   c (1  z 1 ) 2 4 Fs2 (1  2 z 1  z 2 ) 2 4 F (1  2 z  z )  4c Fs (1  z 2 )   c (1  2 z 1  z 2 ) 2 s 1 2 2 H ( z)  H ( z)  4 Fs2  8Fs2 z 1  4 Fs2 z 2 2 2 2 [4 Fs2  4c F  c ]  [2 c  8Fs2 ]z 1  [4 Fs2  4 c Fs  c ]z 2 Realizando la normalización de los coeficientes se obtiene lo siguiente: 4 Fs2 8Fs2 4 Fs2  z 1  z 2 2 2 2 2 2 4 F  4 c Fs   c 4 Fs  4 c Fs   c 4 Fs  4 c Fs   c H ( z)  2 2 2 c  8Fs2 4 F 2  4 c Fs   c 2 1 z 1  s2 z 2 2 4 Fs2  4 c Fs   c 4 Fs  4 c Fs   c 2 s De está forma los coeficientes a.6.5 Ejemplo de diseño para filtro pasa altas Fs = 1291. b1 z 0  b2 z 1  b3 z  2 4 Fs2 a1  2 4 Fs2  4c Fs  c a2  2a1 a3  a1 b1  1 b2  2 2c  8 Fs2 2 4 Fs2  4c Fs  c b3  2 4 Fs2  4c Fs  c 2 4 Fs2  4c Fs  c 14. y b quedan definidos así: H ( z)  a1 z 0  a2 z 1  a3 z 2 .32Hz. 217 . Fc = 200Hz.

32) 2  4( 2 4 F 2  4 c Fs   c b3  s2  2 4 Fs  4 c Fs   c La programación de este filtro pasa altas. 218 .493018837784645 b1  1 b2  2 2 c  8Fs2  2 4 Fs2  4 c Fs   c 4 Fs2  2 4 F  4 c Fs   c  0.19404 Fs 1291.19404)(1291.32) tan( )  1366.32)  (1366.493018837784645 2(1366. //Timer0 con periodo de 774.19404)(1291. y2=0.32) 2  4( )(1366.986037675569289 a3  a1  0.4u segundo. bajo las mismas condiciones del filtro pasa bajas es el siguiente: //Declaración de varíales.  2 . x0 = (float)(ADC_Read(0)-512.262123407215126. y1=0.19404) 2 2 a2  2a1  -0.709951943923452 .19404) 2 2  0. //Adquisición de una muestra de 10 bits en.32 Seguidamente se determinan los coeficientes del filtro: H ( z)  a1  a1 z 0  a2 z 1  a3 z 2 .709951943923452 2 4(1291.x1*0.y2*0. //Corrimiento de los valores x(n).32)  (1366. y0=0.0). //Declaración de la función de interrupciones.32)  (1366.19404) 2  8(1291. unsigned int YY.32) 2  4( )(1366.493018837784645 . y0 = (x0+x2)*0. x[0].19404)(1291.32) 2 2 2 s 2 4(1291.262123407215126 2 4(1291.32 Hz.32) 2  -0. b1 z 0  b2 z 1  b3 z 2 4(1291.32)  (1366.19404)(1291. // Fs = 1291. void interrupt ( void ) { if( INTCON.32)  4( )(1366. float x0=0. y y(n). x1=0.F2 ) { TMR0L=135. //Implementación de la ecuación en diferencias.19404) 2 2 2 )(1366.986037675569289 + y1*0. 2 Se determina la frecuencia de corte análoga en términos de la frecuencia digital: c  2 Fs tan( Fc 200 )  2(1291.19404) 2 2 4(1291. x2=0.

su interrupción. while(1)//Bucle infinito. //Reconstrucción de la señal: y en 10 bits. x2 = x1. TRISC = 0. { } } 14. PORTC = 0. INTCON = 0b10100000. PORTC = (YY>>8)&3.6 Filtro Pasa Banda IIR Para la función de transferencia h(z) en el filtro pasa banda se realiza el cambio de variable bilineal.F2=0. } } void main( void ) { //Inicio del puerto B como salida. PORTB = YY&255. y1 = y0. T0CON = 0b11000101. PORTB = 0. tomando como base la ecuación (14.6. YY = (unsigned int)(y0+512.17):  (1  z 1 )  2 Fs B  (1  z 1 )    H ( z)  2   (1  z 1 )  (1  z 1 )  2 2 Fs   B 2 Fs  0  (1  z 1 )  (1  z 1 )      2 Fs  B (1  z 1 )(1  z 1 ) H ( z)  2 4 Fs2 (1  z 1 )2  2 Fs  B (1  z 1 )(1  z 1 )  0 (1  z 1 )2 H ( z)  2 Fs  B (1  z 2 ) 2 4 Fs2 (1  2 z 1  z  2 )  2 Fs  B (1  z  2 )  0 (1  2 z 1  z  2 ) 2 Fs  B  2 Fs  B z 2 2 2 2 [4 Fs2  2 Fs  B  0 ]  [20  2 Fs  B ]z 1  [4 Fs2  2 Fs  B  0 ]z  2 219 H ( z)  .0).y2 = y1. x1 = x0. TRISB = 0. //Se configura el TIMER 0. INTCON.

7 Ejemplo de diseño para filtro pasa banda Fs = 1291.Normalizando la ecuación se obtiene la siguiente relación: 2 Fs  B 2 Fs  B  z 2 2 2 2 4 F  2 Fs  B  0 4 Fs  2 Fs  B  0 H ( z)  2 2 20  2 Fs  B 4 Fs2  2 Fs  B  0  2 1 1 z  z 2 2 4 Fs2  2 Fs  B  0 4 Fs2  2 Fs  B  0 2 s De la anterior ecuación. Fb = 10Hz.32 F 10  B  2 Fs tan( b )  2(1291.8442 Fs 1291. se pueden estáblecer las siguientes constantes: H ( z)  a1  a1 z 0  a2 z 1  a3 z 2 .32 F0 Posteriormente se determinan los coeficientes del filtro: 220 . b1 z 0  b2 z 1  b3 z  2 2 s 2 Fs  B 2 4 F  2 Fs  B  0 a2  0 a3  a1 b1  1 2 20  2 Fs  B b2  2 4 Fs2  2 Fs  B  0 b3  2 4 Fs2  2 Fs  B  0 2 4 Fs2  2 Fs  B  0 14.58 Fs 1291. A continuación se determinan las frecuencias análogas para el diseño del filtro pasa banda: 0  2 Fs tan( 300 )  2(1291.6. Fo = 300Hz.32Hz.32) tan( )  62.32) tan( )  2310.

0133351841372129 b1  1 b2  2 2 0  2 Fs  B 2(2310.H ( z)  a1  a1 z 0  a2 z 1  a3 z 2 . b1 z 0  b2 z 1  b3 z 2 2 Fs  B 2(1291.58) 2 14.32)(62.58) 2  2(1291.8442)  (2310.8 Filtro Rechaza Banda IIR Para la función de transferencia h(z) en el filtro pasa banda se realiza el cambio de variable bilineal.58) 2 a2  0 a3  a1  -0.17):  (1  z 1 )  2 2 Fs (1  z 1 )   0   H ( z)  2 1   (1  z )  (1  z 1 )  2 2 Fs   B 2 Fs  0  (1  z 1 )  (1  z 1 )      H ( z)  2 4 Fs2 (1  z 1 )2  0 (1  z 1 )2 2 4 Fs2 (1  z 1 )2  2 Fs  B (1  z 1 )(1  z 1 )  0 (1  z 1 )2 2 4 Fs2 (1  2 z 1  z 2 )  0 (1  2 z 1  z 2 ) 2 4 Fs2 (1  2 z 1  z  2 )  2 Fs  B (1  z  2 )  0 (1  2 z 1  z  2 ) 2 H ( z)  2 2 2 [4 Fs2  0 ]  [20  8Fs2 ]z 1  [4 Fs2  0 ]z 2 H ( z)  2 2 2 [4 Fs2  2 Fs  B  0 ]  [20  8Fs2 ]z 1  [4 Fs2  2 Fs  B  0 ]z  2 Normalizando la ecuación se obtiene la siguiente relación: 2 2 2 4 Fs2   0 2 0  8Fs2 4 Fs2   0 1  z  z 2 2 2 2 2 2 2 4 Fs  2 Fs  B   0 4 Fs  2 Fs  B   0 4 Fs  2 Fs  B   0 H ( z)  2 2 2 2 2 0  8Fs 4 F  2 Fs  B   0 2 1 z 1  s2 z 2 2 4 Fs2  2 Fs  B   0 4 Fs  2 Fs  B   0 De la anterior ecuación.32)(62.32)(62.32)(62.218755004098491 2 4 Fs2  2 Fs  B   0 4(1291.8442)  (2310.973329631725574 2 4 Fs2  2 Fs  B   0 4(1291.8442)   -0.32) 2  2(1291. se pueden estáblecer las siguientes constantes: 221 .58) 2   0.8442)  (2310.8442)  (2310.32) 2  2(1291.0133351841372129 2 4 Fs2  2 Fs  B   0 4(1291.6.32) 2  2(1291.8442)   0.32)(62.32)(62.32) 2  2(1291.58) 2 b3  2 4 Fs2  2 Fs  B   0 4(1291. tomando como base la ecuación (14.

b1 z 0  b2 z 1  b3 z 2 222 . A continuación se determinan las frecuencias análogas para el diseño del filtro rechaza banda: 0  2 Fs tan( F0 100 )  2(1291.32) tan( )  641. Fo = 100Hz. b1 z 0  b2 z 1  b3 z  2 2 4 Fs2  0 2 4 Fs2  2 Fs  B  0 2 20  8 Fs2 a2  2 4 Fs2  2 Fs  B  0 a3  a1 b1  1 b2  2 20  8 Fs2 2 4 Fs2  2 Fs  B  0 2 4 Fs2  2 Fs  B  0 b3  2 4 Fs2  2 Fs  B  0 14.6. Fb = 10Hz.015 Fs 1291.9 Ejemplo de diseño filtro rechaza banda Fs = 1291.32Hz.32  B  2 Fs tan( Fb Posteriormente se determinan los coeficientes del filtro: H ( z)  a1 z 0  a2 z 1  a3 z 2 .32 10 )  2(1291.8442 Fs 1291.H ( z)  a1  a1 z 0  a2 z 1  a3 z 2 .32) tan( )  62.

223 .015) 2   0.32) 2  (641. x2=0. y2 = y1. x[0]. y0 = (x0+x2)*0.0). INTCON.015) 2 a3  a1  0. void interrupt ( void ) { if( INTCON.4u segundo. y2=0.a1  2 4 Fs2   0 4(1291. y1=0.8442)  (641. YY = (unsigned int)(y0+512.F2=0.32) 2  2(1291.977592319507735 2 4 Fs2  2 Fs  B   0 4(1291. float x0=0.72826896958352 .0). correspondiente a este diseño es el siguiente: //Declaración de varíales. x1=0.32 Hz. y y(n).F2 ) { TMR0L=135.32) 2  2(1291.32)(62.8442)  (641. //Declaración de la función de interrupciones. TRISB = 0. x0 = (float)(ADC_Read(0)-512. //Adquisición de una muestra de 10 bits en.y2*0.32)(62.015) 2  8(1291. PORTB = YY&255.015) 2   0. unsigned int YY.32) 2  2(1291. // Fs = 1291.015) 2 El código fuente en lenguaje C.32)(62. y1 = y0.32)(62.977592319507735 + (y1-x1)*1.8442)  (641. //Timer0 con periodo de 774.32) 2   -1. y0=0.8442)  (641.015) 2 a2  2 2 0  8Fs2 2(641.72826896958352 b3  2 4 Fs2  2 Fs  B   0 4(1291. x2 = x1.977592319507735 b1  1 b2  a2  -1. x1 = x0.95518463901547. //Implementación de la ecuación en diferencias. PORTC = (YY>>8)&3.32) 2  2(1291.72826896958352 2 4 Fs2  2 Fs  B   0 4(1291. //Corrimiento de los valores x(n).95518463901547 2 2 4 Fs  2 Fs  B   0 4(1291. //Reconstrucción de la señal: y en 10 bits. } } void main( void ) { //Inicio del puerto B como salida.

así como el número de multiplicaciones requeridas por iteración. de la misma. TRISC = 0. T0CON = 0b11000101. //Se configura el TIMER 0.PORTB = 0. PORTC = 0. La forma general de está matriz se puede apreciar a continuación:  y (n) a b   y (n  1)  x(n)    c d   x(n  1)       Los cálculos de la matriz de rotación. Estas condiciones se resumen en la siguiente tabla: Propiedades de los osciladores recursivos Multiplicaciones Amplitud Salida en Factores por iteración equitativa cuadratura 2 Si No k=2Cos(θ) Oscilador Doble cuadrado De acople en cuadratura estándar Matriz de rotación k  1 1 0    4 Si Si k=sen(θ)  1 k2   k    1 k2   k Tabla 14-1 Para iniciar el diseño de un oscilador o generador digital se debe definir el valor de la constante k. el valor del ángulo θ. INTCON = 0b10100000. implica usar una matriz de rotación que incluye dos valores de salida de la señal y dos valores pasados. este se define por medio de la siguiente ecuación:  2Fg Fs Ecuación 14-22 224 . su interrupción. while(1)//Bucle infinito. y por defecto.7 Osciladores digitales Generar una señal seno de forma sintética. { } } 14. permiten crear osciladores de 2 salidas que pueden tener amplitud equitativa o no y salida en cuadratura o no.

las cuales se pueden iniciar de la siguiente forma: y(n-1) = Cos(-θ) = Cos(-0.4865707421 1291. El valor del ángulo θ. y y(n-2). que es el siguiente: k=2Cos(0.76788313  1  1 0   Las ecuaciones de implementación son las siguientes: y (n)  1.4865707421) = 1.32Hz. 225 .1 Ejemplo de diseño oscilador doble cuadrado Fs = 1291.32 Con el valor θ se puede calcular el valor de k. y y2. Para manipular la amplitud se debe multiplicar la salida y(n). 14. Por obvias razones los osciladores de mayor velocidad son los que solo requieren de una multiplicación por iteración.4865707421). que es la siguiente: y (n)  ay (n  1)  bx(n  1) x(n)  cy (n  1)  dx(n  1) Para que el oscilador arranque correctamente es importante dar valores iniciales a las salidas y1.76788313 De está manera se tiene la siguiente matriz de rotación: 1. por el factor deseado. Otra condición para el funcionamiento adecuado de este oscilador es dar valores iniciales a las salidas y(n-1). y(n-2) = Cos(-2 θ) = Cos(-2(0.76788313 y(n  1)  y(n  2) La condición para el funcionamiento de este oscilador es que la magnitud de la señal debe ser 1. La implementación de estos generadores siempre usa la misma forma de operaciones matemáticas. es el que se calcula a continuación:  2Fg Fs  2 100  0.7. y Fs es la frecuencia de muestreo en hercios. Fg = 100Hz.Donde Fg es la frecuencia en hercios de la señal que se desea generar.4865707421)).76788313 y(n  1)  x(n  1) x(n)  y (n  1) Las mismas funciones en términos de solo la salida y es igual a la siguiente ecuación: y(n)  1.

y1=1. //Timer0 con periodo de 774. PORTB = 0. T0CON = 0b11000101. 226 . su interrupción.32Hz. y1 = y0. unsigned int YY. //Implementación de la ecuación del oscilador.2 Ejemplo de diseño para oscilador de acople en cuadratura Fs = 1291. y2 = y1. void interrupt ( void ) { if( INTCON. PORTB = YY&255. { } } 14.0.F2 ) { TMR0L=135. //Se configura el TIMER 0. PORTC = 0.76788313*y1 . para simular este ejemplo: //Declaración de varíales inicializadas. //Declaración de la función de interrupciones.0). omitiendo el generador de onda seno que se conecta a la entrada análoga.F2=0.Para fines de simulación. y0 = 1. INTCON. while(1)//Bucle infinito. y2=0. Teniendo presente las anteriores observaciones se puede implementar el siguiente código fuente en lenguaje C. Fg = 150Hz. e IIR. INTCON = 0b10100000. se implementarán las mismas condiciones del hardware usado en los filtros FIR. amplificada 500 veces.4u segundo. //Reconstrucción de la señal: y en 10 bits. A = 450. YY = (unsigned int)(500*y0+512. } } void main( void ) { //Inicio del puerto B como salida. float y0. // Fs = 1291.883941565.32 Hz. TRISB = 0.y2. PORTC = (YY>>8)&3. //Se hace el corrimiento de la salida.7. TRISC = 0.

4u segundo. para este oscilador: //Declaración de varíales inicializadas. x0 = -0.7452703484 0. El patrón de la matriz de rotación es el siguiente:  0.Se calcula el ángulo de paso θ:  2Fg Fs  2 150  0.7452703484*y1 + 0. de está manera se asignan los siguientes valores iniciales: y(n-1) = x(n-1) = ASen(3π/4). unsigned int YY. x0. b  c  sen( )  0. y1=318.6667624073*x1. //Se hace el corrimiento de la salida.6667624073 y(n  1)  0. //Timer0 con periodo de 774.1980515. y0 = 0.7298561132)]2  0. para este fin se debe tener presente que este oscilador tiene las dos salidas en cuadratura.7452703484   El juego de ecuaciones que rigen las iteraciones del oscilador son las siguientes: y(n)  0. void interrupt ( void ) { if( INTCON.7298561132 1291.32 Hz.7452703484. es decir que entre las dos salidas siempre existe un desfase de 90 grados. x1=318.32 Posteriormente se calculan las constantes de la matriz: a  d  1  [ sen( )]2  1  [ sen(0. y(n-1) = x(n-1) = 450Sen(3π/4) = 318. //Declaración de la función de interrupciones. Finalmente se implementa. Bajo este concepto se puede concluir que las dos señales en cuadratura tendrán la misma magnitud a 3π/4.7452703484 x(n  1) Para dar inicio correcto al oscilador se deben dar valores iniciales a las salidas retardadas y(n-1). el código fuente en lenguaje C.6667624073x(n  1) x(n)  0.6667624073  0.F2 ) { TMR0L=135.1980515.6667624073 0. 227 .7452703484*x1.7452703484 y(n  1)  0. y x(n-1). // Fs = 1291.1980515.6667624073. float y0.6667624073*y1 + 0. //Implementación de las ecuaciones del oscilador.

son números complejos. biomédicas. por está razón los análisis de Fourier se hacen por excelencia en términos de la magnitud de estos números complejos. while(1)//Bucle infinito.F2=0. reconocimiento de patrones. PORTB = YY&255. { } } 14. está definida por la siguiente ecuación: X ( jw)   x(n)e  jwn n 0 L 1 Ecuación 14-23 Como se puede apreciar en la anterior ecuación la potencia de una frecuencia w.y1 = y0. Las aplicaciones de este procedimiento son numerosas en las que se pueden nombrar: Analizadores de espectro. PORTC = 0. y audio. x1 = x0. YY = (unsigned int)(y0+512. TRISB = 0. T0CON = 0b11000101. //Se configura el TIMER 0. INTCON = 0b10100000. PORTC = (YY>>8)&3. de igual manera se puede notar que los valores resultantes de la función X(jw). //Reconstrucción de la señal: y en 10 bits.0). en señales. es una temática sumamente densa.23) en términos de un número complejo rectangular es la siguiente: X ( jw)   x(n)[cos(wn)  jsen( wn)] n 0 L 1 Ecuación 14-24 228 . } } void main( void ) { //Inicio del puerto B como salida. PORTB = 0. La transformada de Fourier. La ecuación (15. o discreta. TRISC = 0. sin embargo este capítulo busca mostrar de forma simple la implementación de está herramienta.8 Transformada discreta de Fourier DFT La transformación de Fourier es un procedimiento matemático que permite analizar las componentes espectrales y la intensidad de potencia de una señal análoga. INTCON. entre otras. La transformada discreta de Fourier o DTF. se puede evaluar en un fragmento de señal de longitud L. de comunicaciones. su interrupción.

F2=200Hz. en la señal x_n. que se usó en los filtros FIR. F1=100Hz.0. 2 100  0. } //Se retorna el valor de la magnitud del // número complejo. con el fin de implementar la misma estructura del microcontrolador 18F452. que realice está operación se puede implementar con una función como la siguiente: //Función para determinar la intensidad de potencia //de la componente w. e IIR. float w. n++ ) { //Cálculo y sumatoria de los componentes //reales e imaginarios. o la intensidad de la componente w.32 2 300 W3   1. la frecuencia de muestreo se define de 1291. por medio de la transformada de Fourier. n<L. Como primera medida se definen las tres frecuencias análogas y sus respectivas frecuencias digitales.De está forma la relación X(jw).459712226 1291. } El siguiente ejemplo implementa un programa que permite detectar la presencia de tres frecuencias diferentes en una señal análoga. y F3=300Hz. float TDF( float *x_n. y su relación matemática es la siguiente:  L 1   L 1  | X ( jw) |  x(n) cos(wn)   x(n) sen( wn)  n 0   n 0  2 2 Ecuación 14-25 Está ecuación matemática permite evaluar la potencia. for( n=0. I=0. I += sin( w*n ). puede ser pasada a la magnitud de sí misma como |X(jw)|. Un fragmento de código en lenguaje C. dentro de la muestra x(n).32Hz. float R=0. unsigned int L ) { //Declaracion de variables.32 W1  229 . //Bucle for para realizar las sumatorias.32 2 200 W2   0.48657707421 1291. R += cos( w*n ). return sqrt( R*R + I*I ).0. unsigned int n. de longitud L.9731414842 1291.

//Bucle for para realizar las sumatorias.0. unsigned short n. { OK++. unsigned int L ) { //Declaración de variables. //Timer0 con periodo de 774. La señal es adquirida por una entrada análoga del PIC. // Fs = 1291.0). float TDF( float *x_n. return sqrt( R*R + I*I ).4u segundo. x[0] = ((float)ADC_Read(0)-512.)x[i]=x[i-1]. El programa correspondiente para este ejercicio es el siguiente: //Declaración de varíales inicializadas. for( i=63. float x[64]. I += x_n[n]*sin( w*n ). //Función para determinar la intensidad de potencia //de la componente w.0. I=0.Para realizar la transformada de Fourier. OK=1. float w. for( n=0. n++ ) { //Cálculo y sumatoria de los componentes //reales e imaginarios. if( OK ) //Se evalúa la adquisición de muestras. } INTCON.F2 ) { TMR0L=135. float R=0. //Declaración de la función de interrupciones. se usará la función antes citada. la cual es la suma de tres fuentes diferentes. en la señal x_n. } void interrupt ( void ) { if( INTCON. n<L. } } 230 . i-.32 Hz. i!=0.F2=0. //Se cuentan las muestras tomadas. R += x_n[n]*cos( w*n ). //Se guarda la última muestra. unsigned short i. } //Se retorna el valor de la magnitud del // número complejo. //Se corren las últimas 64 muestras en el bufer x. Una salida del microcontrolador se activa cuando se detecta la potencia suficiente de la componente espectral.

//Se evalua la potencia de la señal de 200Hz.9731414842. //Se evalúa la potencia de la señal de 300Hz. BUTTON. 64 ). else PORTB. 1. los dispositivos: 18F452. PORTB = 0.F0=0. { while( OK<65 ). El circuito correspondiente para está simulación es el siguiente: Circuito 14-2 231 .F2=0. RES2. if( RES2>800 )PORTB. RES3 = TDF( x. y generador virtual. CAP. //Se hace la transformada de Fourier.F2=1. 0. //Se hace la transformada de Fourier. 64 ).//Se espera a recibir 64 muestras. OK=1. RES2 = TDF( x. if( RES3>800 )PORTB.F1=0. //Se configura el TIMER 0. OP1P.F1=1. RES3. INTCON = 0b10100000.4865707421.void main( void ) { float RES1. 64 ). //Inicio del puerto B como salida. T0CON = 0b11000101. 0. if( RES1>800 )PORTB. LED-RED. else PORTB. } } Para realizar la simulación de este ejercicio se implementa en ISIS. else PORTB. RES. OK =0. su interrupción. //Se suspenden la adquisición de muestras.F0=1. TRISB = 0. de la componente de 200Hz. //Se hace la transformada de Fourier.459712226. RES1 = TDF( x. //Se activa la adquisición de muestras. de la componente de 100Hz. de la componente de 300Hz. //Se evalúa la potencia de la señal de 100Hz. while(1)//Bucle infinito.

x(3). x(1). En la siguiente matriz se puede apreciar el almacenamiento por columnas:  x(0)  x(1) x(l . se multiplica por los factores de fase Wn. está operación se define en la siguiente ecuación: e j 2mq M  cos( lq G (l . implica múltiples cálculos matemáticos. Este algoritmo se fundamenta en la división en segmentos cortos para realizar la evaluación de la transformada de Fourier. q) 0  l  ( L  1) 0  q  ( M  1) lq WN  e 2lq j N Ecuación 14-27  cos( 2lq 2lq )  jsen ( ) N N 232 . Por esta razón la ciencia de tratamiento de señales. Está operación se realiza en función de la siguiente ecuación: mq F (l . como sumas. 14. organiza en una matriz las muestras de entrada y realiza trasformadas más pequeñas en las filas y posteriormente en las columnas.8. q)  WN F (l . con M columnas. sale como resultado notable el algoritmo de transformación rápida de Fourier.1 Transformada rápida de Fourier FFT La transformada rápida de Fourier. puntos sobre cada una de las filas.q). o FFT. multiplicaciones y evaluaciones trigonométricas. se vio en la necesidad de buscar técnicas de realizar la transformada de Fourier de forma mucho más eficiente. x(2). Las muestras digitalizadas de la señal análoga se pueden representar de la siguiente forma: x(0).La transformada de Fourier. Estos procesos hacen lento el rendimiento de la máquina de proceso. de este esfuerzo. q)   x(l . es un arreglo de valores complejos correspondiente a las transformadas de M.x(N) Donde N es el número máximo de muestras que se desean analizar. Seguidamente la matriz F(l. x(5). Las muestras son organizadas en una matriz de L filas. x(4).……………. m)WM m 0 M 1   Ecuación 14-26 0  l  ( L  1) 0  q  ( M  1) W mq M 2mq 2mq )  jsen ( ) M M La matriz F(l. m)    x(2)   x(3) x(4) x(5) x(6) x (7 ) x(8) x(9) x(10) x(11) x(12) x(13)   x(14)  x(15) Seguidamente se debe realizar la transformación por filas.q). haciendo la trasformada de M muestras.

mayor será la resolución en el espectro. Se calcula la transformada de L muestras de cada una de las columnas. q )    X (8)   X (12) X (1) X (5) X (9) X (13) X (2) X (6) X (10) X (14) X (3)  X (7 )   X (11)   X (15) Cabe denotar que entre más muestras se ingresen para hacer la transformación. q)   G (l . De la misma manera la resolución de la transformación en hercios está definida por la siguiente ecuación: RHz  Fs N Ecuación 14-29 Donde R. como se aprecia en la siguiente matriz:  X (0)  X (4) X ( p. En conclusión la lectura de la matriz solo se debe hacer hasta la mitad. lo que significa que los valores superiores a π. solo se deben leer 64. o el mínimo ancho de banda que se puede analizar con la FFT. Se multiplican las anteriores transformadas por el factor de fase Wn. q)WLlp l 0 L 1   Ecuación 14-28 0  p  ( L  1) 0  q  ( M  1) WLlp  e j 2lp L  cos( 2lp 2lp )  jsen ( ) L L La matriz X(p. por ejemplo si la cantidad de muestras analizadas son 128. es la transformada final de las N muestras iniciales. Se calcula la transformada de M muestras de cada una de las filas. A continuación se resume el proceso antes citado:      Se archiva la señal de x(n). Se leen los datos de la matriz X(p. se debe leer por filas. por filas. o en otras palabras los niveles DC. Estás componentes son las más intensas.q).Por último se realiza la trasformada por columnas de L muestras. entrega un espectro evaluado de 0 a 2π. El resultado de una transformada de Fourier. y para fines prácticos está primera muestra puede ser ignorada.m). son un espejo de las componentes inferiores causadas por los armónicos de las frecuencias inferiores a π. es la resolución en hercios. También cabe denotar que la primera muestra de la FFT. 233 . y N es el número de muestras ingresadas a la FFT. hace alusión a la componente espectral más baja incluida la frecuencia 0. Está matriz contiene en cada uno de sus términos un número complejo que representa la magnitud y la fase de las componentes espectrales. por columnas en la matriz x(l. Está operación se resume en la siguiente ecuación: X ( p. pero a su vez mayor será la cantidad de recursos de procesamiento y de memoria requeridos. La matriz X(p.q). Fs es la frecuencia de muestreo.q).

} //Función para realizar una suma compleja. A continuación se presentará un ejemplo en lenguaje C. Res. unsigned short Fi=0.I*B. //Declaración de varíales inicializadas.R = A. //Declaración de la función de interrupciones. Sin embargo está capacidad de memoria solo permite hacer una FFT. float Magnitud( Complejo A ) { return sqrt( A. Complejo B ) { Complejo Res. útil para los procesos de la FFT. Complejo Producto( Complejo A.I + A.R. Res. Complejo Y[L][M]. Res.I + B.I = A.R*B.R. unsigned short i. Co=0. } 234 .R*A. OK=0. } //Función para determinar la magnitud de un número complejo. //Función para realizar una multiplicación compleja.I*B.I*A.R .I. return Res.Para realizar un ejemplo con la FFT. muestras: #define M 16 #define L 8 #define N L*M #define PI 3. dada su amplia memoria RAM de 3328 Bytes. Complejo X[L][M]. para realizar la transformación de una señal de N = ML.I = A.141592654 typedef struct { float R. Res.R + B.I ). se implementará el PIC 18F4585.R*B. }Complejo. Complejo Adicion( Complejo A.R = A.A.I. return Res. Complejo B ) { Complejo Res. con máximo 169 muestras. I.R + A.

0. Y[l][q]. q++ ) { WW. q<M. la matriz Y[][]. for( p=0.//Cálculo rápido de Fourier.I = -sin( 2*PI*m*q / M ).q).26).q.0. for( l=0. WW.R = 0. WW ). l++ ) { for( q=0. q<M.I = 0. for( m=0. y //X[][] representa la matriz G(l. for( l=0.0.q).q). Complejo WW. la matriz Y[][]. l<L. //Trío de bucles for. WW ). p<L. l<L.m.I = -sin( 2*PI*l*q / N ). para ejecutar la //ecuación (15.R=0.27). //y X[][] representa la matriz G(l. Y[][] representa la matriz F(l. q++ ) { Y[p][q]. representa X(p. representa F(l.28). WW ). l<L. l++ ) { for( q=0.I=0. para realizar la multiplicación //de factores Wn (15. para ejecutar la //ecuación (15. WW = Producto( X[l][m]. l++ ) { 235 . void FFT( void ) { unsigned short l.p. p++ ) { for( q=0. de N muestras.R = cos( 2*PI*m*q / M ).R = cos( 2*PI*l*q / N ). Y[l][q] = Adicion( Y[l][q]. q<M.q). } } //Trío de bucles for. } } } //Dupla se bucles for. Y[p][q]. q++ ) { Y[l][q].0. for( l=0. m<M.q). X[l][q] = Producto( Y[l][q]. m++ ) { WW. WW.

0. WW ).R = Magnitud( Y[l][m] ). } } void interrupt ( void ) { if( INTCON. if( Fi==L ) { Fi = 0. } } 236 . //Timer0 con periodo de 774.4u segundo. //Este resultado queda en los términos reales de X[][] for( l=0.I = -sin( 2*PI*l*p / L ).WW. // Fs = 1291.R = ((float)ADC_Read(0)-127. } } } //Doble for anidado para determinar. WW. X[Fi][Co].R = cos( 2*PI*l*p / L ).F2=0. WW = Producto( X[l][q]. X[l][m]. Y[p][q] = Adicion( Y[p][q]. //y se archivan por columnas. m<M. { //Se hace la adquisición de las muestras.F2 ) { TMR0L=135. X[Fi][Co]. if( Co==M ) { Co=0.0.0). if( OK ) //Se evalúa la adquisición de muestras. l<L.32 Hz.I = 0. Co++. //la magnitud de la transformada. OK=0. l++ ) for( m=0. } } } INTCON.I = 0. Fi++. m++ ) { X[l][m]. WW ).

T0CON = 0b11000101. X[0][0]. cc. cont. INTCON = 0b10100000. //Se configura el TIMER 0. //Se espera la adquisicion de las muestras. cc<M. while( OK ). for( ff=0.R = 0. UART1_Write(10). Text ). //Cálculo de la FFT. UART1_Write_Text("// Adquisicion y FFT de:"). FFT(). ff++ ) { for( cc=0. UART1_Write(10). IntToStr( N. UART1_Init(9600). UART1_Write(13). //Inicio del puerto B como salida. UART1_Write_Text(" Muestras //"). PORTB = 0. ff<L. } } 237 . UART1_Write_Text(Text). UART1_Write(13). UART1_Write(10). if( cont==N ) { cc = M. UART1_Write_Text(Text). cont++. } } } UART1_Write_Text("// Fin de las muestras //"). while(1)//Bucle infinito. //Se envían la magnitud de la FFT. { OK = 1. cont = 0. hasta N/2 //por medio del puerto serial. unsigned short ff. Text ). su interrupción. UART1_Write(13). //Se habilita la lectura de muestras. cc++ ) { FloatToStr( X[ff][cc]. TRISB = 0.void main( void ) { char Text[30].0. UART1_Write_Text("// Inicio de las muestras //"). //La primera componente espectral se ignora y se hace igual a cero. ff = L. UART1_Write(10).R. UART1_Write(13).

por un 18F4585.9 Control digital PID El en ámbito del control automático la forma de control más popular es el PID. que significa proporcional. Sustituyendo el PIC. El control es una rama de la física y la electrónica que permite manipular una variable física para permanecer en un valor deseado arbitrariamente. El circuito para simular es el siguiente: Circuito 14-3 14. derivativo. y anexando un VIRTUAL TERMINAL. se implementa un circuito similar al ejercicio anterior de transformada discreta de Fourier.Para simular este ejercicio en ISIS. Todo sistema de control cuenta con las siguientes características:        Punto de control Error Controlador (PID) Corrección Planta Variable a controlar Sensor o transductor Estás características se pueden apreciar en el siguiente diagrama en bloques: Figura 14-20 238 . integral.

y como estrategia para simular el comportamiento de la planta se usa un circuito RC. Fd=2. se realiza la transformación bilineal. Ki=52. es la señal de error para cada una de ellas. //Declaración de constantes y variables.0*Fs*Kd. Fs = 152.0*Fs).8 y un periodo crítico de 41. y este desarrollo da el siguiente análisis: PID ( z )  Kp  Ki 2 FsKd (1  z 1 )  2 Fs (1  z 1 ) (1  z 1 ) (1  z 1 ) Ki (1  z 1 ) 2 FsKd (1  z 1 )  2 Fs (1  z 1 ) (1  z 1 ) Ki (1  z 1 ) 2 FsKd (1  z 1 ) PID ( z )  Kp  2 Fs 1  (1  z ) (1  z 1 ) PID ( z )  Kp  Del anterior análisis se pueden deducir tres funciones de transferencia para el controlador donde la entrada. la lectura del sensor. El punto de partida de un controlador es el controlador PID análogo que tiene una función de transferencia PID(s).Para el caso del microcontrolador su función está sujeta a la entrada del punto de control. y la salida de corrección. const float Kp=1. Kd=0.08. Las respectivas operaciones matemáticas para su implementación son las siguientes: y p (n)  [ Kp ]e(n) Ki [e(n)  e(n  1)]  yi (n  1) 2 Fs yd (n)  2 FsKd [e(n)  e(n  1)]  yd (n  1) yi ( n )  y pid (n)  y p (n)  yi (n)  yd (n) Para el caso particular del siguiente ejemplo se emplea la técnica de conversión digital análogo por medio de una salida PWM. está función tiene una relación similar a un conjunto de filtros y su tratamiento es equivalente a un filtro IIR. de segundo orden.42718447. const float Fi=ki/(2.005562.2m segundos. //Declaración de coeficientes de integración y derivación. cuando su Kp crítico es de 1.5878906. La función de transferencia análoga es la siguiente: PID ( s)  Kp  Ki  sKd s Ecuación 14-30 De la misma forma que se hace con los filtros IIR. El siguiente código fuente muestra un control PID correspondiente a la sintonización del control PID. 239 Ecuación 14-31 .

//Resultado PID. 240 . yd0=Fd*(e0-e1)-yd1. ypid=0. YN=0.0. //a 6.0.0. ypid=Kp*e0+yi0+yd0. e0 = PUNTO_DE_CONTROL . INTCON = 0b10100000. PWM1_Init(10000).0. if(YN<0. //Actualización de muestras.SENSOR.0.0)YN=255. PWM1_Set_Duty(SALIDA). yd0=0.5536ms :: 152. SENSOR = (float)((ADC_Read(1)>>2)&0xFF). PUNTO_DE_CONTROL=127. yd1=yd0. //Adquisición del punto de control.float SENSOR. PWM1_Start(). e1=e0. void interrupt() { if( INTCON. PUNTO_DE_CONTROL = (float)((ADC_Read(0)>>2)&0xFF). //Ajuste y corrección de la SALIDA Y(n) //delimitada por los límites 0 y 255. } } void main( void ) { //Configuración del modulo PWM.F2 )// 6. //Ecuación en diferencias. int ADQUI.0.0. yi0=Fi*(e0+e1)+yi1. e1=0.5878906 Hz { //Adquisición de la variable controlada. while( 1 ) //Bucle infinito. YN += ypid. //Configuración de la interrupción de Timer 0.0)YN=0.0. //Ecuación integral. yi0=0.0. INTCON. yi1=0. if(YN>255.0. yi1=yi0. //Ecuación derivativa.F2=0.5536ms OPTION_REG = 0b00000110. float e0=0.0. //Función de interrupciones para el Timer 0. SALIDA = (unsigned short)(YN). //Calculo del nivel de error. unsigned short SALIDA. yd1=0.

La relación de estos periodos con las constantes Ki.{ } } El circuito para simular implementa los dispositivos: 16F877A. POT-HG. para determinar los valores definitivos de Kp. Cuando esto sucede se mide el periodo de la oscilación sostenida y se determina el valor de Kp. Posteriormente estos valores se remplazan en la siguiente tabla. y Kd. y Kd igual a 0. Ki. y Kd: Tipo de control P PI PID Kp 0. Ki. El esquemático del circuito es el siguiente: Circuito 14-4 Para la sintonía del controlador.45Kpc 0. y establecer las constantes Kp. Ti es el periodo de integración y Td el periodo derivativo.5Kpc 0. y Kd se especifica en las siguientes ecuaciones: 241 . Posteriormente se incrementa el valor de Kp. Ki=0 Pc/1. gradualmente hasta que la salida oscile de forma sostenida. se puede usar la siguiente estrategia: Primero se establece el controlador con las constantes Ki.125Pc Tabla 14-2 Donde.6Kpc Ti Infinito.2 0. CAP. Estos dos parámetros se denominan periodo crítico Pc. RES. OP1P La simulación trabaja con reloj de 20MHz.5Pc Td 0 0 0. y Kp crítico Kpc.

Kp Ti Kd  TdKp Ki  Ecuación 14-32 242 .

video. el control de flujo de la información.15 Transmisión de datos La transmisión de datos es una línea de las comunicaciones electrónicas que se encarga de garantizar el transporte de información de forma digital. El siguiente grafico ilustra de forma simple el tráfico de información entre dos puntos que gestionan una comunicación. La información enviada puede ser bloques de texto. Para realizar la transmisión de información se pueden seguir diversos formatos y protocolos. o cualquier otro tipo de información. de audio. Las técnicas de transmisión de datos permiten optimizar y garantizar el estado correcto de los datos durante una transmisión de datos. la transparencia de datos y el control de errores. Este es un aspecto importante dado que un medio de transmisión está expuesto a producir errores por razones de ruido o falta de continuidad en el medio de transmisión de datos. sin embargo este capítulo se centra en técnicas básicas de transmisión de datos como son. Figura 15-1 243 .

con oscilador de 4MHz. sbit LCD_D5_Direction at TRISB5_bit. case 0: //Se evalúa si llega una solicitud. sbit LCD_RS_Direction at TRISB0_bit. Dato. //Se evalúa el estado del tráfico de información. se puede implementar solamente el control de flujo. el carácter de reconocimiento negativo NAK. NULL=0x00. //Declaración de variables de trabajo. se representa con el valor 0x06. Dato = UART1_Read(). Bandera=0. //Vector de interrupciones. EOT='%'. const char ACK='A'. void interrupt() { //Se evalúa la interrupción por recepción serial. Cuando el medio de transmisión es altamente confiable. sbit LCD_EN_Direction at TRISB3_bit.. char BUFER[20]. implementado con un microcontrolador PIC 16F628A. sbit LCD_D7_Direction at TRISB7_bit. sbit LCD_D5 at RB5_bit. sbit LCD_D6_Direction at TRISB6_bit. N=0. sbit LCD_D6 at RB6_bit. Por otro lado es importante tener presente que los ejemplos mostrados en este capítulo trafican con la información en código ASCII.F5 ) { //Se lee el Dato de entrada.. los finales de cadenas de texto o de caracteres se representan con el carácter nulo NULL que equivale al código 0x00. unsigned short Estado=0. sbit LCD_D7 at RB7_bit. if( Dato == ENQ ) { //Se cambia al siguiente Estado y se 244 . sbit LCD_EN at RB3_bit. se representa con el código 0x15. //Declaración de constantes.1 Control de flujo El control de flujo permite gestionar el tráfico de información de forma ordenada. if( PIR1. NAK='N'. sbit LCD_D4 at RB4_bit.15. El siguiente ejemplo muestra una comunicación simples con el respectivo control de flujo. y el carácter de reconocimiento positivo ACK. ENQ='&'. // Declaración de constantes del LCD sbit LCD_RS at RB0_bit.. sbit LCD_D4_Direction at TRISB4_bit. switch( Estado ) { //En espera.

Lcd_Out(1. PIE1 = 0b00100000. Lcd_Init(). } else { //Se envía reconocimiento negativo. } default: break. } } } void main( void ) { //Se activan las interrupciones periféricas. //Configuración del LCD. UART1_Write(ACK). } break.//envía el reconocimiento positivo. UART1_Init(9600). Estado = 0.. N++. INTCON = 0b11000000. Estado = 1. //Configuración del modulo USART a 9600 bps. BUFER[N] = NULL. Bandera = 1. //Recibiendo. UART1_Write(NAK). } else { //Se archiva el Dato recibido y se incrementa //el indicador N BUFER[N] = Dato. case 1: // Se evalúa si lo que llega es el final de transmisión EOT if( Dato == EOT ) { //Llega el fin de la información y se vuelve //al Estado inicial y se activa la Bandera de datos //de llegada."Control de Flujo"). //Se activa la interrupción por recepción serial. while(1) //Bucle infinito. 245 .. N = 0.1.

en un circuito como el siguiente: Circuito 15-1 La transmisión de información obedece a la siguiente estructura: Figura 15-2 246 . NAK. Lcd_Out(2. if( Bandera == 1 ) { //Se imprime la información en el LCD. VIRTUAL TERMINAL.{ //Se evalúa si la Bandera esta activa. } } } Es importante notar que en la declaración de constantes se usan como caracteres de ACK. &. y LM016L." "). N. esto solo se realiza en este ejemplo de forma didáctica dado que este tipo de caracteres tienen valores y equivalencias que no se pueden ver ni digitar en el VIRTUAL TERMINAL.BUFER).1. Bandera = 0.1. los caracteres A. y %. Para realizar la respectiva simulación de este ejemplo se implementa en ISIS los dispositivos PIC16F628A. ENQ y EOT. Lcd_Out(2.

//Vector de interrupciones. De esta manera se puede ver de forma transparente la información dentro de los datos de control. y un PIC 16F877A ambos con oscilador de 4MHz.F5 ) { //Se lee el Dato de entrada. //Se evalúa el estado del tráfico de información. sbit LCD_D5_Direction at TRISB5_bit. const char ACK=0x06. STX=0x02. los programas esta fundamentados en un microcontrolador PIC 16F628A. sbit LCD_D7 at RB7_bit. void interrupt() { //Se evalúa la interrupción por recepción serial. sbit LCD_D4_Direction at TRISB4_bit. sbit LCD_RS_Direction at TRISB0_bit. ENQ=0x05. Para demostrar el funcionamiento de la estructura de transparencia se implementan a continuación dos programas uno que transmite información y otro que la recibe. N=0. sbit LCD_D7_Direction at TRISB7_bit. switch( Estado ) 247 . sbit LCD_EN_Direction at TRISB3_bit. Dato = UART1_Read(). STX comienzo de texto que equivale al código 0x02. y para finalizar el bloque de datos se usa el carácter ETX que es equivalente a 0x03. DLE=0x10. sbit LCD_D5 at RB5_bit. a continuación se puede apreciar el código fuente del modulo receptor que es similar al ejemplo del control de flujo: // Declaración de constantes del LCD sbit LCD_RS at RB0_bit. unsigned short Estado=0. //Declaración de constantes. EOT=0x04. Dato. Para encabezar la información se implementa el carácter DLE o marcador de transparencia equivalente al código 0x10. sbit LCD_EN at RB3_bit.2 Transparencia de datos La transparencia de datos es la capacidad de empaquetar información con un bloque de inicio o encabezado y un bloque final del paquete de información. if( PIR1.15. sbit LCD_D6_Direction at TRISB6_bit. NAK=0x15. NULL=0x00. Bandera=0. //Declaración de variables de trabajo. sbit LCD_D6 at RB6_bit. sbit LCD_D4 at RB4_bit. el éxito de la transparencia esta en no repetir los datos de encabezado o final dentro de la información. char BUFER[20]. ETX=0x03.

UART1_Write(ACK).. if( Dato == DLE ) { //Se cambia al siguiente Estado. case 2: // Se evalúa si lo que llega es STX. // Recibiendo información. Estado = 3. } else { //Se envía reconocimiento negativo. case 1: // Se evalúa si lo que llega es DLE.{ //En espera.. Estado = 0. //Se reinicia de nuevo al Estado inicial.. } break.. if( Dato == ENQ ) { //Se cambia al siguiente Estado y se //envía el reconocimiento positivo. BUFER[N]=NULL.. Estado = 2. } else { //Se identifica un error de orden en los datos //y se transmite un reconocimiento negativo y se //reanuda al Estado inicial. case 0: //Se evalúa si llega una solicitud. //Se reinicia de nuevo al Estado inicial. 248 .. UART1_Write(NAK).... //Delimitador de transparencia. UART1_Write(NAK). if( Dato == STX ) { //Se inicia la cadena de información y se //pasa al Estado 3 N=0. Estado = 1. } break. } else { //Se identifica un error de orden en los datos //y se transmite un reconocimiento negativo y se //reanuda al Estado inicial.

.Estado = 0.. BUFER[N]=NULL. N++. if( Dato == DLE ) { //Se cambia al Estado siguiente. Estado = 0. //Se incrementa el índice de los datos. //Se finaliza el BUFER de datos.. Estado = 4. //Se transmite reconocimiento positivo. UART1_Write(NAK). case 5: //Se evalúa si llega el EOT. } else { //Se transmite reconocimiento negativo. Bandera = 1. } break. case 3: //Se evalúa si el Dato que llega es un DLE. case 4: // Se evalúa si llega un ETX if( Dato == ETX ) { //Se cambia al Estado siguiente.. // Esperando fin de cadena. UART1_Write(ACK). 249 . } else { //Se anexa un nuevo Dato al BUFER. //Se transmite reconocimiento negativo... Estado = 5. } else { //Se cambia de nuevo al Estado inicial. UART1_Write(NAK). BUFER[N]=Dato. if( Dato == EOT ) { //Se activa la Bandera de llegada de información. } break. UART1_Write(NAK). // Esperando datos y delimitador de transparencia. } break. //Esperando fin de transmisión.

STX=0x02. ENQ=0x05. ADCON1 = 0b11000001. //Configuración del LCD."Transparencia"). //Configuración del modulo USART a 9600 bps. NULL=0x00. UART1_Init(9600). 250 . Lcd_Out(2. break. } } } El siguiente programa está diseñado para un microcontrolador PIC 16F877A. { //Se evalúa si la Bandera esta activa.1. con el pin //AN3 como voltaje de referencia.1. el cual toma la lectura de un sensor LM35." "). while(1) //Bucle infinito. const char ACK=0x06. ETX=0x03. Lcd_Out(2. PIE1 = 0b00100000. float Temp. //Texto de inicio. Lcd_Out(1. if( Bandera == 1 ) { //Se imprime la información en el LCD. y la transmite con el respectivo control de flujo y transparencia: //Declaración de constantes. default: break. Lcd_Init(). NAK=0x15. //Se configura el modulo ADC. } } } void main( void ) { //Se activan las interrupciones periféricas.1. void main( void ) { char BUFER[10]. int adc. //Se activa la interrupción por recepción serial. EOT=0x04. INTCON = 0b11000000.BUFER). Bandera = 0. DLE=0x10.} Estado = 0.

.. while( !UART1_Data_Ready() ). UART1_Write(ENQ). //Se escribe la temperatura en el BUFER. if( UART1_Read() != ACK )goto OTRO. //Se calcula el valor de la temperatura.. UART1_Init(9600). //Se transmite un STX.... UART1_Write(EOT). FloatToStr( Temp.. delay_ms(500)... //Se verifica si llega un ACK. //Retardo de lectura de 400m segundos delay_ms(100). //Se espera el ACK. //Se transmite la información del BUFER.. Temp = 0..... //Se transmite el fin de transmisión. BUFER ).... UART1_Write(ETX).. if( UART1_Read() != ACK )goto OTRO.//Se inicia el modulo USART a 9600 bps. UART1_Write(DLE). { OTRO: //Lectura del canal 0 adc = ADC_Read(0). //Se espera el ACK while( !UART1_Data_Ready() ). } } La estructura para la transparencia de datos obedece a la siguiente forma: Figura 15-3 251 . //Se transmite un DLE. //Se transmite el ETX. //Se transmite un DLE. //Se verifica si llega un ACK. //Se transmite el ENQ. UART1_Write_Text(BUFER). UART1_Write(DLE).. while(1)//Bucle infinito. UART1_Write(STX).244*adc..

esta estrategia anexa un bit más a cada paquete de información el cual representa el número par o impar de unos en el paquete. se efectúa una división binaria entre el paquete de datos que se desean transmitir y un polinomio fijo que debe respetar algunas condiciones. El ente receptor realiza la misma división con el mismo polinomio y después de culminarla el resultado consignado en el residuo debe ser 0 si los datos han llegado sin ningún error. de lo contrario los datos del paquete están deteriorados.3 Control de errores CRC El control de errores en una transmisión es de suma importancia y más cuando la calidad del medio de transmisión no es confiable. La estructura de información con el CRC incorporado se implementa en la forma que se puede apreciar en la siguiente figura: 252 . Para detectar los errores de transmisión existen diferentes técnicas entre las cuales se pueden citar. Por último se puede hablar del CRC. PIC 16F877A. Después de hacer la división el residuo de la misma es anexado al paquete de información y este es el CRC. para este fin se realiza la operación lógica o exclusiva.Para realizar la respectiva simulación se realiza el siguiente circuito que tiene los dispositivos. LRC o Verificación de redundancia longitudinal la cual verifica el número de bits pares o impares en un grupo de bytes. y que es la técnica en la cual se centra el ejemplo de este capítulo. Para llegar al resultado del CRC. o verificación de redundancia cíclica. RES. LM35: Circuito 15-2 15. PIC 16F628A. entre ellos. que es la estrategia de mayor efectividad. VRC o verificación de redundancia vertical. LM016L.

es decir por 11. para ilustrar esta situación se puede apreciar el siguiente ejemplo: Este polinomio equivale al siguiente número binario: 10100111 Para que los polinomios funciones de forma adecuada deben cumplir con las siguientes condiciones:    Debe empezar y terminar con un 1.Figura 15-4 Los polinomios que se implementan en el cálculo del CRC se denotan en términos de X. Los siguientes son polinomios estándar para diferente número de bits: CRC-4 CRC-8 CRC-12 CRC-16 CRC-32 El siguiente ejemplo aritmético muestra una división para generar el CRC. es decir por 10. No debe ser divisible por X. Debe ser divisible por X+1. y su respectiva verificación: 253 . es un 1 en el polinomio. en el cual cada X.

32. por eso es más simple realizar una operación lógica con registros de desplazamiento como estrategia para determinar el residuo de la división. Para comprender con claridad este concepto se puede observar el siguiente ejemplo que usa un polinomio de orden 8: Polinomio: 100110001 254 . 16. 24. El siguiente diagrama muestra un circuito para el polinomio: Figura 15-5 Usando como estrategia de programación el corrimiento de bits como lo muestra la figura se puede hacer un conjunto de funciones que calculen el CRC. Cuanto más larga sea la trama más difícil es realizarla. sobre una trama de longitud indefinida teniendo como base mínima un byte y polinomios con orden múltiplo de 8. tales como 8.Información Polinomio 1 0 1 1 0 1 0 1 1 1 0 0 1 1 Calculo del CRC 1 0 1 0 1 1 0 1 1 ↓ ↓ ↓ ↓ ↓ 0 1 1 ↓ ↓ ↓ ↓ 0 0 0 ↓ ↓ ↓ ↓ 0 1 1 0 ↓ ↓ ↓ 0 0 1 1 ↓ ↓ ↓ 0 1 0 1 1 ↓ ↓ 0 0 0 0 0 ↓ ↓ 1 0 1 1 1 ↓ 1 0 0 1 1 ↓ 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0 0 Chequeo del CRC 1 1 0 1 0 1 1 1 1 0 1 1 ↓ ↓ ↓ ↓ ↓ ↓ 1 0 1 1 ↓ ↓ ↓ ↓ ↓ 0 0 0 0 ↓ ↓ ↓ ↓ ↓ 1 0 1 1 0 ↓ ↓ ↓ ↓ 1 0 0 1 1 ↓ ↓ ↓ ↓ 0 1 0 1 1 ↓ ↓ ↓ 0 0 0 0 0 ↓ ↓ ↓ 1 0 1 1 1 ↓ ↓ 1 0 0 1 1 ↓ ↓ 0 1 0 0 1 ↓ 0 0 0 0 0 ↓ 1 0 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Chequeo → 0 0 Se cambia el polinomio por 0 → dado que el bit mas significativo es cero 1 0 1 1 0 0 0 1 0 0 1 1 0 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 0 1 1 0 1 0 0 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 0 0 0 0 0 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 0 0 1 0 1 0 0 0 0 0 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 0 ↓ 0 ↓ 0 0 0 0 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 1 1 0 0 ← CRC 0 0 La implementación de una división de este modo es demasiado compleja.

} Para realizar la implementación de este ejemplo. se usa el mismo hardware del ejemplo de transparencia.) CorrerBitCRC( (Byte>>B)&1 ). while( trama[N]!=0 ) { CorrerByteCRC( trama[N] ). CRC = 0. B-. B-. unsigned short CalcularRCR( char *trama.unsigned short CRC=0. //Función para insertar in bit en el cálculo del CRC. //Set X3 CRC2 = CRC2 | (CRC<<1)&4. return CRC. el código fuente para el microcontrolador transmisor implementando el CRC es el siguiente: //Función para insertar un Byte al cálculo del CRC. //Set X2 CRC2 = CRC2 | (CRC<<1)&2. //Set X5 CRC2 = CRC2 | ((CRC<<1)^(CRC>>3))&16. B>=0. CRC2 = CRC2 | (CRC<<1)&128. for( B=7. void CorrerByteCRC( char Byte ) { short B. B>=0. } 255 . void CorrerBitCRC( char _bit ) { unsigned short CRC2=0. //Set X4 CRC2 = CRC2 | (CRC<<1)&8.) CorrerBitCRC( (Byte>>B)&1 ). } //Función para insertar un Byte al cálculo del CRC. N++. //Set X0 CRC = CRC2. } CorrerByteCRC( FINAL ). //Set X6 CRC2 = CRC2 | ((CRC<<1)^(CRC>>2))&32. //Set X1 CRC2 = CRC2 | ((CRC>>7)^_bit)&1. } //Función para calcular el CRC de una trama de texto. unsigned char FINAL ) { unsigned short N=0. //Set X7 CRC2 = CRC2 | (CRC<<1)&64. for( B=7. void CorrerByteCRC( char Byte ) { short B.

CRC = 0. //Se verifica si llega un ACK...//Función para calcular el CRC de una trama de texto. //Se espera el ACK. FloatToStr( Temp. int adc.. Temp = 0. return CRC.. //Se transmite la información del BUFER. unsigned short CalcularRCR( char *trama.. //Se transmite un STX. UART1_Write(DLE). BUFER ).244*adc. while( trama[N]!=0 ) { CorrerByteCRC( trama[N] ).. if( UART1_Read() != ACK )goto OTRO. 256 . CalcularRCR( BUFER. //Se transmite el ENQ. 0 ). //Se escribe la temperatura en el BUFER. //Se inicia el modulo USART a 9600 bps. //Se calcula el valor de la temperatura.. { OTRO: //Lectura del canal 0 adc = ADC_Read(0). while(1)//Bucle infinito.. UART1_Write(STX). N++. } void main( void ) { char BUFER[10]. float Temp. con el pin //AN3 como voltaje de referencia. //Calculo del CRC. delay_ms(500). //Retardo de lectura de 400m segundos delay_ms(100). UART1_Write(ENQ). while( !UART1_Data_Ready() ). unsigned char FINAL ) { unsigned short N=0.. UART1_Init(9600). //Se configura el modulo ADC. } CorrerByteCRC( FINAL )... //Se transmite un DLE.. ADCON1 = 0b11000001.

UART1_Write(DLE). N=0. CRC_2. UART1_Write(EOT).. sbit LCD_D5_Direction at TRISB5_bit. unsigned short Estado=0... ETX=0x03. sbit LCD_D6_Direction at TRISB6_bit. sbit LCD_D7_Direction at TRISB7_bit... } } El código fuente para el microcontrolador receptor con la implementación del CRC es el siguiente: // Declaración de constantes del LCD sbit LCD_RS at RB0_bit... ENQ=0x05. UART1_Write(CRC). sbit LCD_EN_Direction at TRISB3_bit. NULL=0x00.. //Declaración de constantes. sbit LCD_D4_Direction at TRISB4_bit.. char BUFER[20]. //Función para insertar in bit en el cálculo del CRC. //Se transmite un DLE. const char ACK=0x06. void CorrerBitCRC( char _bit ) { unsigned short CRC2=0. //Se transmite el fin de transmisión.. Bandera=0. //Se verifica si llega un ACK. sbit LCD_EN at RB3_bit. sbit LCD_D6 at RB6_bit. sbit LCD_D7 at RB7_bit. //Se transmite el CRC. STX=0x02. 257 . //Declaración de variables de trabajo. //Se transmite el ETX. sbit LCD_RS_Direction at TRISB0_bit. sbit LCD_D4 at RB4_bit. Dato. while( !UART1_Data_Ready() ). UART1_Write(ETX).UART1_Write_Text(BUFER). unsigned short CRC=0. NAK=0x15. //Se espera el ACK. if( UART1_Read() != ACK )goto OTRO. Lon. DLE=0x10.. sbit LCD_D5 at RB5_bit.. EOT=0x04.

//Set X0 CRC = CRC2. //Set X7 CRC2 = CRC2 | (CRC<<1)&64. if( PIR1. //Set X1 CRC2 = CRC2 | ((CRC>>7)^_bit)&1. unsigned short CalcularRCR( char *trama. } //Función para insertar un Byte al cálculo del CRC.F5 ) { //Se lee el Dato de entrada. unsigned char FINAL ) { unsigned short N=0. for( B=7... if( Dato == ENQ ) { //Se cambia al siguiente Estado y se 258 .. } CorrerByteCRC( FINAL ).) CorrerBitCRC( (Byte>>B)&1 ).CRC2 = CRC2 | (CRC<<1)&128. //Set X3 CRC2 = CRC2 | (CRC<<1)&4. switch( Estado ) { //En espera. CRC = 0. void interrupt() { //Se evalúa la interrupción por recepción serial. B-. //Set X5 CRC2 = CRC2 | ((CRC<<1)^(CRC>>3))&16. N++. while( trama[N]!=0 ) { CorrerByteCRC( trama[N] ). //Set X2 CRC2 = CRC2 | (CRC<<1)&2. } //Función para calcular el CRC de una trama de texto. //Set X4 CRC2 = CRC2 | (CRC<<1)&8. case 0: //Se evalúa si llega una solicitud. B>=0. //Set X6 CRC2 = CRC2 | ((CRC<<1)^(CRC>>2))&32. Dato = UART1_Read(). return CRC. //Se evalúa el estado del tráfico de información. } //Vector de interrupciones. void CorrerByteCRC( char Byte ) { short B.

. Estado = 2. } else { //Se identifica un error de orden en los datos //y se transmite un reconocimiento negativo y se //reanuda al Estado inicial. } break. //Se reinicia de nuevo al Estado inicial. //Delimitador de transparencia. if( Dato == DLE ) { //Se cambia al siguiente Estado. } else { //Se envía reconocimiento negativo. Estado = 1. Estado = 3. UART1_Write(NAK)... } break. } else { //Se identifica un error de orden en los datos //y se transmite un reconocimiento negativo y se //reanuda al Estado inicial. case 3: //Se evalúa si el Dato que llega es un DLE. UART1_Write(NAK). // Esperando datos y delimitador de transparencia. Estado = 0.. // Recibiendo información. Estado = 0. UART1_Write(NAK).. } break. if( Dato == STX ) { //Se inicia la cadena de información y se //pasa al Estado 3 N=0. 259 . UART1_Write(ACK). case 2: // Se evalúa si lo que llega es STX.//envía el reconocimiento positivo. case 1: // Se evalúa si lo que llega es DLE. //Se reinicia de nuevo al Estado inicial.. BUFER[N]=NULL.

... //Se finaliza el BUFER de datos. Estado = 0. N++. case 5: //Se evalúa si llega el EOT. } break. UART1_Write(ACK).if( Dato == DLE ) { //Se cambia al Estado siguiente... // Se verifica que el chequeo sea cero. UART1_Write(NAK). BUFER[N]=Dato. if( CRC==0 ) { //Se activa la Bandera de llegada de información. BUFER[Lon-1]=0. //Se filtra el CRC. //Se transmite reconocimiento negativo. //Se transmite reconocimiento positivo... // Esperando fin de cadena. BUFER[N]=NULL. CRC_2 ). Bandera = 1.. Lon = strlen(BUFER). Estado = 4. que está en la última posición. } else { //Se anexa un nuevo Dato al BUFER. //Se calcula el CRC. Estado = 5. } break. if( Dato == EOT ) { //Se mide la longitud de la trama. CalcularRCR( BUFER. } else { //Se cambia de nuevo al Estado inicial. //Esperando fin de transmisión. CRC_2 = BUFER[Lon-1]. } 260 . //Se incrementa el índice de los datos... case 4: // Se evalua si llega un ETX if( Dato == ETX ) { //Se cambia al Estado siguiente.

break. Lcd_Out(2. //Configuración del LCD. Lcd_Init(). INTCON = 0b11000000. UART1_Write(NAK).1. } Estado = 0. } } else { //Se transmite reconocimiento negativo. while(1) //Bucle infinito. } } } void main( void ) { //Se activan las interrupciones periféricas. if( Bandera == 1 ) { //Se imprime la información en el LCD. Lcd_Out(2. } } } 261 . Lcd_Out(1. default: break. UART1_Init(9600).else { //Se transmite reconocimiento negativo. { //Se evalúa si la Bandera esta activa. //Se activa la interrupción por recepción serial. UART1_Write(NAK)." "). PIE1 = 0b00100000."Transparencia"). //Texto de inicio. Bandera = 0. //Configuración del modulo USART a 9600 bps.1.1.BUFER).

16 Actuadores y potencia Los actuadores son dispositivos eléctricos o electrónicos que permiten crear un cambio físico sobre una variable. Bajo estos términos los transistores trabajan como interruptores de corriente. que la que puede entregar un pin del PIC. permiten controlar un flujo de corriente por medio de una diferencia de potencial. lámparas incandescentes. son aquellos como motores AC. OFF. Para el uso de estos actuadores se implementan esencialmente dos tipos de dispositivos. o DC. Los actuadores AC. presión. Los transistores funcionan en tres zonas. Los MOSFET. En otras palabras los transistores se usan en corte y saturación. Está pueden ser temperatura. luminosidad. se caracteriza por controlar una corriente por medio de otra corriente. Los actuadores pueden ser de accionamiento AC. en las zonas de corte y saturación. Sin embargo para el caso de los actuadores se requieren en la mayoría de los casos ser activados y desactivados. son aquellos como motores. posición angular o lineal. saturación y activa. humedad. entre otras. La zona activa es común en los sistemas de amplificación de señal. entre otros. solenoides. En el caso de un microcontrolador el uso de un arreglo de potencia se hace indispensable cuando un actuador requiere más voltaje o corriente.1 Actuadores DC Los actuadores DC. 16. o tener un comportamiento ON. conocidas como corte. Los actuadores DC. V es la diferencia de potencial eléctrico sobre una carga eléctrica con resistencia R. resistencias eléctricas. lámparas incandescentes. relevadores. Los transistores pueden ser BJT. por la cual circula una corriente I. velocidad. requieren de corriente siempre directa es decir que siempre circula en la misma dirección. Un transistor BJT. Por otra parte la potencia es la cantidad energía eléctrica requerida para realizar un trabajo determinado. o MOSFET. La vista de los transistores en ISIS es la siguiente: Figura 16-1 262 . La tensión que entregan las salidas del PIC siempre es de 5 voltios y la corriente que puede suministrar solo puede ser del orden de pocos miliamperios. servomotores. entre otros. En el caso puntual de la electricidad la potencia está definida como: Pe  VI  V2  I 2 R Ecuación 16-1 R Donde Pe es la potencia eléctrica en vatios. los transistores de potencia o los relevadores.

este arreglo se muestra en el siguiente circuito: Circuito 16-1 16.Los transistores MOSFET. controlan la corriente que circula entre la fuente S. y con el uso constante los contactos eléctricos se deterioran con el tiempo. que tiene una carga de 120V AC: 263 . El uso de cargas inductivas como motores. por medio de la corriente que circula por la base B. de estos dispositivos es la siguiente: Figura 16-2 Por último el uso de estos dispositivos se debe hacer de forma final como se puede observar en el siguiente circuito. para evitar este problema se debe usar un diodo rectificador polarizado en inverso para descargar las cargas inductivas. por medio de la diferencia de potencial eléctrico sobre la compuerta G. y el emisor E. controlan la corriente que fluye entre el colector C. tienen la propiedad de almacenar energía en forma de campo electromagnético. que tienen un electroimán con el cual se cierra o se abre un circuito eléctrico por medio de uno o varios contactos. Los transistores BJT. dado que no pueden hacer cambios de estádo veloces. estás devuelven su energía por medio de una corriente inversa. Los relevadores son ideales para aislar los circuitos de potencia con la etapa de control electrónico. este fenómeno puede causar daños en los sistemas controlados con transistores. relevadores o solenoides.1 Relevadores Los relevadores son dispositivos electromecánicos. y el drenador D. esto implica que cuando las cargas inductivas son des energizadas.1. La apariencia física y la vista en ISIS. De igual manera son dispositivos de bajo rendimiento.

PWM1_Set_Duty(CU). PWM1_Init( 500 ). PWM1_Set_Duty(CU). } //Bucle de decremento del PWM cuando se pulsa RB0. son de naturaleza inductiva y por consiguiente merecen el mismo tratamiento de los relevadores. } } } 264 . if( CU==255 )CU=0.2 Motores DC Los motores DC. PWM1_Set_Duty(CU).Circuito 16-2 16. if( CU==0 )CU=255. //Se activan las resistencias PULL-UP. while(1) //Bucle infinito. 10. con oscilador de 4MHz. para este fin se implementa el módulo PWM de un microcontrolador 16F628A. OPTION_REG = 0. 1. En el siguiente ejemplo se puede apreciar como un microcontrolador hace cambios de velocidad en un motor DC. while( Button( &PORTB. delay_ms(10).1. delay_ms(10). //Se inicia el módulo PWM a 500Hz. 0 ) ) { CU++. El código fuente en lenguaje C es el siguiente: void main( void ) { unsigned short CU=0. while( Button( &PORTB. 10. PWM1_Start(). 0. { //Bucle de incremento del PWM cuando se pulsa RB1. 0 ) ) { CU--.

Para poder hacer la simulación de este ejemplo se debe implementar en ISIS los dispositivos: 16F628A. RES. 1N4001. Estos arreglos cuentan con dos señales de control que permiten activar la polaridad positiva. Los puentes H. se pueden implementar en discreto con transistores o usar módulos integrados que se pueden conseguir comercialmente. en función de la referencia y sus características.1. y un TIP31 puede controlar una corriente de 3 Amperios. Un diseño discreto de este arreglo se puede apreciar en el siguiente circuito: Circuito 16-4 265 . 2N3904. Un transistor como el 2N3904 puede controlar una corriente de 200m Amperios. con el suministro de solo una fuente sencilla.3 Puente H Los puentes H son arreglos transistorizados que permiten invertir la polaridad sobre una carga eléctrica. BUTTON. 16. Configurados como se puede apreciar en el siguiente circuito: Circuito 16-3 En función de la intensidad de corriente se puede cambiar el transistor para optimizar el funcionamiento de los sistemas. y MOTOR ACTIVE. y negativa. se puede asumir un máximo de corriente.

//Se apagan los pines de control. } //Bucle para detectar el botón de detener. //Se activa el pin de control 2. El siguiente ejemplo muestra el control de giro sobre un motor DC. 10. y se apaga el control 2. //Se apagan los pines de control. para activar la polaridad negativa se hace la configuración contraria con los pines de control.Para activar una polaridad positiva. while( Button( &PORTB. y referencia.F6 = 1. Se debe tener presente que nunca se podrán activar los dos puntos de control al mismo tiempo. 10. 0. se activa el control 1. PORTB. //Se activa el pin de control 1. produciendo daños considerables sobre los transistores. PORTB. //Se apagan los pines de control. } } } 266 . 1. { //Bucle para detectar el botón de giro a la izquierda. 10. 0 ) ) { PORTB = 0. 0 ) ) { PORTB = 0. PORTB = 0. //Se activan las resistencias PULL-UP. 0 ) ) { PORTB = 0. PORTB = 0. está acción genera un corto circuito entre Vcc. while( Button( &PORTB. while(1) //Bucle infinito. 2. while( Button( &PORTB.F7 = 1. por medio de un microcontrolador 16F628A: void main( void ) { OPTION_REG = 0. TRISB = 0x0F. //Configuración de salida y entrada del puerto B. } //Bucle para detectar el botón de giro a la derecha.

Manipulando la velocidad de la secuencia se controla la velocidad de giro del motor. y siempre con la misma polaridad. y 1 o 2 son terminales comunes.La simulación en ISIS. y con el orden de la secuencia se controla la dirección de giro del motor. que se energizan uno o dos a la vez. se realiza por medio del siguiente circuito electrónico: Circuito 16-5 16. En las siguientes gráficas se puede apreciar la apariencia física de estos motores y su vista en ISIS: Figura 16-3 Los motores unipolares. pero alternando la polaridad.4 Motores Paso Los motores paso. Este efecto genera una secuencia en las bobinas y su polaridad. las cuales se polarizan al mismo tiempo. Los motores paso bipolares cuentan con solo dos bobinas. cuentan generalmente con 5 o 6 terminales. unipolares y bipolares. de las cuales 4 corresponden a los embobinados. son dispositivos electromecánicos que permiten hacer giros fraccionados por grados. los motores paso son de dos naturalezas. Las bobinas de un motor unipolar se pueden apreciar en el siguiente circuito: 267 .1. Un motor unipolar cuenta con cuatro embobinados.

PORTB = 0. que cuenta con una frecuencia de reloj de 4MHz. //Inicio del puerto TRISB = 0xF0. 0b00000010. const unsigned short PASOS[4] = { 0b00000001. con una sola entrada activa. OPTION_REG = 0. void main( void ) { //Declaración de variables. La siguiente tabla muestra la forma de activar las secuencias en los dos casos: Secuencias para motores paso unipolares Secuencia con una activación Secuencia con dos activaciones LA LB LC LD LA LB LC LD ON OFF OFF OFF ON ON OFF OFF OFF ON OFF OFF OFF ON ON OFF OFF OFF ON OFF OFF OFF ON ON OFF OFF OFF ON ON OFF OFF ON Tabla 16-1 PASO 1 2 3 4 El siguiente ejemplo muestra como realizar el control de un motor unipolar con un PIC 16F628A. para este fin la secuencia del motor se guarda en un arreglo. 268 . unsigned short PASO=0. El código en lenguaje C es el siguiente: //Constantes con la secuencia de pasos.Circuito 16-6 La secuencia de activación para los motores unipolares puede ser de dos formas. Cuando se activan dos entradas simultáneamente el torque del motor es mayor pero al mismo tiempo la corriente es mayor. 0b00000100. o dos entradas activas simultáneamente. //Activación de las resistencias PULL-UP. 0b00001000 }.

6. const unsigned short PASOS[4] = { 0b00000011. if( PASO==255 )PASO=3. y el driver ULN2003A: Circuito 16-7 Para realizar el ejercicio con la secuencia de doble activación simplemente se cambia la secuencia en el arreglo del mismo programa y se usa la misma simulación. 269 . PASO++. con el MOTOR-STEPPER. 0) ) { PORTB = PASOS[PASO]. } //Bucle while para hacer girar en un sentido contrario //por medio del pin RB7 while( Button( &PORTB. if( PASO==4 )PASO=0. } } } Para simular este ejercicio se implementa en ISIS.while(1)//Bucle infinito. 100. { //Bucle while para hacer girar en un sentido //por medio del pin RB6 while( Button( &PORTB. PASO--. El nuevo arreglo es el siguiente: //Constantes con la secuencia de pasos. 7. 0b00000110. 100. BUTTON. 0) ) { PORTB = PASOS[PASO]. el siguiente circuito electrónico.

0b00001001 }. De esta forma se obtiene el siguiente arreglo: //Constantes con la secuencia de pasos. 0b00001001.0b00001100. implementa el 16F628A. La simulación en ISIS. en este nuevo arreglo se asume un 1 lógico para los +V. 0b00001010. BUTTON. de la tabla y un 0 lógico para los –V. const unsigned short PASOS[4] = { 0b00000101. característica que lo hace ideal para este tipo de motores. de tal forma que se pueda hacer una secuencia con doble polaridad. 0b00000110 }. Este integrado es el L293D. y el L293D. que es un circuito integrado que tiene en su interior dos puentes H simultáneamente. MOTORBISTEPPER. La distribución eléctrica de este tipo de motores es la siguiente: Circuito 16-8 Para el control de este tipo de motores se requiere de la siguiente secuencia de polaridad: PASO 1 2 3 4 Secuencia para Bipolar LA LB LC LD +V -V +V -V +V -V -V +V -V +V -V +V -V +V +V -V Tabla 16-2 Para realizar el ejercicio con este motor se implementa el mismo programa del motor unipolar y se altera el arreglo de la secuencia. Para los motores bipolares se implementa un puente H doble. Para la simulación del sistema se implementa en ISIS un circuito similar. pero con un motor bipolar y un driver para puente H integrado. El circuito para simular es el siguiente: 270 .

1. que puede ser gobernado por medio de una señal PWM. la referencia y la señal de control PWM. helicópteros y carros.Circuito 16-9 16. Un servomotor tiene incorporado un control de posición angular. y un sistema mecánico de piñones para ofrecer mayor fuerza pero menor velocidad. La apariencia física y la vista en ISIS de estos dispositivos es la siguiente: Figura 16-4 La característica de la señal de control es su periodo de PWM de 16m a 18m segundos y el periodo útil puede variar de 1 a 2 milisegundos. Los servomotores cuentan con un terminal de tres pines para la alimentación. La siguiente gráfica ilustra está situación: Figura 16-5 271 . esto hace referencia respectivamente a 0 y 180 grados en la posición angular. Las aplicaciones de los servomotores están desde la robótica hasta los modelos a escala como aviones.5 Servomotores Los servomotores son sistemas integrados que tienen un control de posición angular.

delay_ms(15). if( ANGULO<0.6. for( n=0. n++ ) delay_us(1). while( Button(&PORTB.A continuación se muestra un ejemplo en lenguaje C. } void main( void ) { //Angulo inicial. n<max. void Pwm_Sevo( float ang ) { unsigned int n. float ANGULO=90. //Inicio del puerto TRISB.0.F0 = 1. para controlar la posición de un servomotor: //Función para generar el PWM de //baja frecuencia. //Se activan las resistencias PULL-UP.F6==0 ) { ANGULO-=10.0 )ANGULO=180. while(1)//Bucle infinito. } } } 272 .7. { Pwm_Sevo(ANGULO).F7==0 ) { ANGULO+=10.5.0 )ANGULO=0.0. } //Si se pulsa el botón de RB7 //se incrementan 10 grados. //Se genera un pulso. ajustado para //cristal de 20MHz. if( ANGULO>180. PORTB. OPTION_REG=0. max = 1. if( PORTB. while( Button(&PORTB. //Si se pulsa el botón de RB6 //se decrementan 10 grados. if( PORTB.0.0.0) ). delay_ms(1). PORTB.0) ).F0 = 0. max.61*ang.5.F0=0.

La apariencia física y la vista en ISIS de estos dispositivos es la siguiente: Figura 16-6 Como se puede observar en la gráfica los MOC. 3021. MOTOR-PWMSERVO. A2.Para simular este ejercicio. y solo se desconecta cuando la corriente de A2 se hace 0. la primera es la implementación de relevadores con la limitación de que solo pueden hacerse acciones de conmutación ON. 3010. se pueden usar esencialmente dos técnicas. y el BUTTON.2 Actuadores AC Para el uso de actuadores AC. que generalmente es de 60Hz. los TRIAC cortocircuitan las terminales A1. y gate. el PIC 16F628A. OFF. Sin embargo se pueden implementar dispositivos en estádo solidó como los TRIAC. Un TRIAC cuenta con tres terminales que son: A1. hace su cruce por cero. esto es cosible cuando la señal AC. con frecuencia de reloj de 20MHz. El circuito correspondiente es el siguiente: Circuito 16-10 16. y A2. y a su vez lo suficientemente grande para disparar otro TRIAC de mayor potencia. etc. Los TRIAC se pueden adquirir en capacidad de la máxima corriente que pueden soportar. y pueden regular su potencia haciendo recorte de fase de la señal AC. estos dispositivos permiten manipular cargas AC. son dispositivos que internamente tienen un TRIAC. se implementa en ISIS. cuando una corriente circula entre el gate y A1. Los TRIAC son de fácil implementación y su uso puede hacerse aislando las corrientes de potencia con optó acopladores como los MOC3011. También se puede observar 273 . pero la corriente que pueden soportar es relativamente pequeña.

que la compuerta gate de los MOC. se puede observar el siguiente circuito: Circuito 16-11 Para lograr la regulación de la potencia sobre una carga AC. Lo anterior indica que el recorte de fase se hace entre 0 y 180 grados. este también está incorporado en el mismo encapsulado. y se repite igual entre 180 y 360 grados.333m segundos.666m segundos. Para entender este arreglo de forma clara. de la señal de poder seno. Para comprender de forma clara este concepto se puede observar la siguiente gráfica: Figura 16-7 274 . La red eléctrica comercial tiene 120V AC. se hace necesario hacer un recorte sobre la fase. se activa por medio de la luz emitida por un LED. y una frecuencia de 60Hz. En conclusión un TRIAC de gran potencia puede ser activado por medio de un circuito digital con solo la activación de un LED. es decir 16. Lo que indica que un siclo de la red eléctrica dura 1/60. El trabajo de recorte de la fase se debe hacer sobre cada medio siclo es decir en 8.

para sincronizarse con ella. //Se compara el contador con el punto.Para poder realizar el recorte de fase. INTCON. } //Interrupción por Timer 0 a 102. CONT = 0.4u Seg. TRISB = 0b01111111.F2 ) { //Se incrementa el contador. //PWM. CONT=0. //Se apaga la bandera de la interrupción por //Timer 0. INTCON. while( 1 )// Bucle infinito. El uso de opto acopladores es ideal para hacer un aislamiento eléctrico de la etapa de potencia con la etapa de electrónica de control. //Activación del Timer0. y se apaga el disparo y es mayor el contador.F7=1. PORTB = 0. El siguiente programa usa un PIC 16F628A. //Función de interrupciones.F1 ) { //Se apaga el pin de disparo. INTCON = 0b10110000. es indispensable detectar el cruce por cero de la señal seno. //Se reinicia el contador. Para detectar el cruce por cero existen diversas estrategias.F2 =0. y flanco de //interrupción externa. if( CONT > PWM )PORTB. if( INTCON. } } void main( void ) { //Configuración de puertos.F1=0. OPTION_REG = 0b01000000. { 275 .F7=0. //Se apaga la bandera de la interrupción //externa. unsigned short PWM=41. sin embargo en el próximo ejemplo se mostrará una que implica la implementación de opto acopladores. void interrupt() { //Interrupción externa por RB0. PORTB. CONT ++. if( INTCON. con frecuencia de reloj de 20MHz: //Variables globales.

y el OSCILLOSCOPE. El circuito electrónico es el siguiente: Circuito 16-12 Después de correr la simulación se puede apreciar en el osciloscopio virtual. while( Button( &PORTB. PWM --. MOC3023. Q4006L4. delay_ms(10). 10. 2. B80C1000. } } } Para realizar la simulación de este ejercicio se implementa en ISIS los dispositivos: 16F628A. 0 ) ) { //Incremento del recorte de fase.//Pulsador de menor potencia. //Retardo de cambio. RES. 10. VSINE. //Retardo de cambio. OPTOCOUPLER-NPN. while( Button( &PORTB. PWM ++. delay_ms(10). if( PWM == 255 )PWM=0. if( PWM > 84 )PWM=84. BUTTON. } //Pulsador de mayor frecuencia. la siguiente vista: 276 . 1. 0 ) ) { //Decremento del recorte de fase.

El canal B. muestra la señal de control que dispara el juego de TRIACs. el recorte de fase.Figura 16-8 El canal A representa la señal sobre la carga de potencia y se puede apreciar sobre está. 277 . muestra un pico cuando la señal seno presenta su cruce por cero. este pico es usado por el microcontrolador para sincronizarse con la red por medio de la interrupción externa. El canal C.

278 .

1 Tabla ASCII ASCII Hex Símbolo ASCII Hex Símbolo ASCII Hex Símbolo ASCII Hex Símbolo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 A B C D E F NUL SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF (Enter) SO SI 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 20 (Espacio) 21 ! 22 " 23 # 24 $ 25 % 26 & 27 ' 28 ( 29 ) 2A * 2B + 2C .17 Anexos 17. 2F / 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 0 1 2 3 4 5 6 7 8 9 : . 2D 2E . < = > ? ASCII Hex Símbolo ASCII Hex Símbolo ASCII Hex Símbolo ASCII Hex Símbolo 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F @ A B C D E F G H I J K L M N O 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F P Q R S T U V W X Y Z [ \ ] ^ _ 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F ` a b c d e f g h i j k l m n o 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F p q r s t u v w x y z { | } ~ ⌂ 279 .

280 .

Quinta Edicion. Coughlin Robert. PRENTICE HALL. PRENTICE HALL. Cuarta Edicion. Tratamiento digital de señales. PEARSON PRENTICE HALL. Transmisión de datos y redes de comunicaciones. Ogata Katsuhiko. PRENTICE HALL. Primera Edicion. Alfaomega. Tercera Edicion. Sexta Edicion. ISBN 84-481-3390-0. ISBN 978-84-8322-347-5. ISBN 970-17-0116-X. ISBN 978-958-682-770-6. M. ISBN 968-880-016-3. Amplificadores operacionales y circuitos integrados lineales. ISBN 970-17-0267-0. Boylestád Robert. CIRCUITOS ELECTRICOS Introducción al Analisis y Diseño. Tercera Edicion. PRENTICE HALL. Oppenheim Alan. Proakis John. ISBN 970-17-0048-1. ISBN 968-880-805-9.Bibliografía Behrouz A. Galeano Gustavo. Electronica: Teoria de Circuitos. Señales y Sistemas. Primera Edicion. 281 . Forouzan. PEARSON PRENTICE HALL. ISBN 970-15-0517-4. Segunda Edición. Ingenieria de Control Moderna. Alfaomega. Logica Digital y Diseño de Computadores. Dorf Richard. Mc Graw Hill. Segunda Edicion. Programación de Sistemas Embebidos en C. Morris Mano.

282 .

143. 113. 72. 173. 79. 148. 279 acknowledge · 88 Actuadores AC · 273 Actuadores DC · 262 Actuadores y potencia · 262 AD · 109. 109. 273. 279 ASK · 161. 61. 21. 239 bit · 20. 54. 155. 26. 218. 124. 133. 65. 77. 159 Display de 7 segmentos · 44 Display LCD de caracteres · 50 Display LCD gráficos · 64 B Bibliografía · 281 bilineal · 212. 163. 264. 269. 231 Características básicas del ISIS para simular · 35 Ch char · 20. 246. 167 COMPIM · 159 Comunicación con dispositivos · 147 Comunicación con memorias SD · 177 Comunicación RS 485 · 172 Comunicaciones seriales · 88 Condicionales e iteraciones en lenguaje C · 30 Control de display de 7 segmentos · 45 Control de displays 7 segmentos dinámicos · 47 Control de errores · 252 Control digital PID · 238 Conversión AD y DA · 109 Conversión AD. 132. 174. 139. 140. 27. 189. 189. 137. 87. 46. 146. 70. 95. 173. 50. 265. 174. 144. 113. 96 18F452 · 71. 86. 47. 238 C CAP · 113.h · 99. 21 BJT · 262. 176. 95. 129. 275. 133. 184. 185. 142. 75. 170. 174. 250. 203 DB9 · 92 Declaración de variables en lenguaje C · 20. 219. 272.Índice 1 16F628A · 58. 247. 124. 260 Creación de caracteres propios · 59 Creación de condiciones lógicas en lenguaje C · 30 Creación de un programa en lenguaje C · 29 Creación del primer programa en MikroC PRO · 38 cruce por cero · 273. 202. 151. 162. 213. 118. 128. 273. 133. 189 283 . 244. 137. 276 ByteToStr · 59. 114. 52. 276 delay_us · 46. 270. 73. 78 display · 44. 49. 71. 89. 48. 165. 76. 52. 252 Definit. 54. 175 D DA · 110. 130. 276 16F84A · 39. 177 Anexos · 279 ASCII · 20. 181. 166. 140. 47. 253. 128. 118. 150. 116. 87. 264. 256. 247. 259. 149. 164. 85. 265. 245. 158 CRC · 252. 206. 263 Button · 74. 48. 256. 130. 216. 136 AND · 25. 46. 137. 21 C COM · 47. 164. 140. 80. 174. 231 18F4550 · 88. 236 ADC_Read · 109 ADCON1 · 90. 136. 173. 266. 53. 95 18F4585 · 234. 266. 153. 257. 176. 75. 73. 126. 79. 79. 126. 100 delay_ms · 46. 241 18F2550 · 88. 272 DIN · 84 Dip-Switch · 76. 64. 272. 202 ADC · 17. 244. 249. 268. 51. 129. 175. o ADC · 109 Conversión DA con arreglo R-2R · 114 Conversión DA con PWM · 110 Conversión DA o DAC · 110 Convolución · 192 coordenadas · 55. 129. 154. 47. 203. 230. 94. 277 2 24LC00 · 89 7 74LS04 · 176 A ACK · 88. 110. 123. 120. 269. 136. 264. 130. 77. 59. 126. 254. 248. 158. 91. 89. 231. 91. 142. 270. 244. 83. 50. 73. 221. 141. 260. 251. 121. 60. 149. 45. 30. 166. 37. 145. 129. 255. 258. 257. 229. 275. 94. 159. 276 BUTTON · 35. 223. 58 16F877A · 65. 99.

159. 154. 176 int · 20. 146. 193. 194. 140. 239 Impresión de valores numéricos · 57 infrarrojos · 174. 202 GIE · 124. 158. 185. 125. 229 FLASH · 117. 260. 230. 275 INTE · 124. 125 Keypad_Init · 79 Keypad_Key_Click · 79 Keypad_Key_Press · 79. 118. 91. 116. 279 GLCD · 65. 193. 131. 183 I2C1_Stop · 89. 196. 57. 150. 65. 64. 40. 110. 215 Ejemplo de diseño para filtro pasa banda · 208. 269. 197. 213. 206. 202. 231. 200. 237 Formatos numéricos usados en el lenguaje C · 23 FSK · 161 función de transferencia · 131. 147. 186 I2C1_Wr · 89. 88. 137 GPS · 88. 189 I²C · 88. 91. 257. 219. 77. 201. 119. 167.4 · 167 IIR · 191. 226. 184. 183 I2C1_Init · 88. 185. 153. 108 HID Terminal · 100. 161. 181. 141. 204. 149. 128. 160. 237 IRLINK · 175. 137. 215. 90. 158. 73. 186 I2C1_Start · 88. 226. 70. 76. 219. 105. 90. 126. 21 DS1307 · 183. 203. 84. 71. 250. 66. 175. 49. 37. 73 Glcd_Init · 65. 35. 98. 186 I2CDEBUGGER · 89 IEEE 801. 90. 75. 99. 104. 156. 192. 147. 79. 267. 130. 251. 223. 50. 134. 125 Interrupciones · 124 INTF · 100. 21 INTCON · 98. 202. 91. 21 FloatToStr · 58. 263. 120 FLASH_Write · 118. 90. 215. 73 Glcd_Fill · 70. 212. 71. 184. 220 Ejemplo de diseño para filtro rechaza banda · 209 Ejemplo de diseño para oscilador de acople en cuadratura · 226 Ejemplos de diseño para filtros FIR · 202 El autor · 11 El ciclo iterativo for · 33 El ciclo iterativo while y do while · 32 El compilador MikroC PRO · 18 El microcontrolador PICMicro · 17 El simulador ISIS de Proteus · 34 ENQ · 246 EOT · 244. 185. 83 284 . 34. 246. 124. 237 Filtro Multi Band · 196 Filtro Pasa Altas IIR · 216 Filtro Pasa Bajas IIR · 213 Filtro Pasa Banda · 194 Filtro Pasa Banda IIR · 219 Filtro Rechaza Banda · 195 Filtro Rechaza Banda IIR · 221 Filtros FIR · 192 Filtros IIR · 212 Filtros Pasa Altas · 194 FIR · 191. 189. 276 F FFT · 232. 199. 91. 58. 270. 52. 273. 226. 228. 224.double · 20. 148. 236. 107 Hid_Disable · 99 Hid_Enable · 98. 104. 262. 189 DTF · 228 E EEPROM · 117. 69. 96. 157. 249. 184. 216. 176. 184. 130. 233. 186 I2C1_Repeated_Start · 88. 218. 176 ISIS · 1. 245. 120. 221. 234. 80. 136. 5. 191. 184. 123. 137. 134. 217 Ejemplo de diseño para filtro pasa bajas · 204. 227. 133. 212. 73 Glcd_Rectangle · 72 GP2D12 · 134. 73 Glcd_Line · 72. 159. 120 float · 20. 184. 63. 101. 271. 160. 47. 247. 157. 161 GROUND · 36 H HID · 88. 120 FLASH_Read · 119. 73 Glcd_Dot · 72. 184 I2C1_Is_Idle · 88 I2C1_Rd · 88. 185. 141. 82. 89. 243 K G Gibbs · 197. 237. 197. 212. 137. 73 Glcd_Circle · 72. 146. 152. 90. 126.15. 124. 99 Hid_Read · 99 Hid_Write · 99 I I2C · 17. 100. 183. 265. 138. 231. 135. 203. 118. 239 Funciones de transferencia · 191 Funciones en lenguaje C · 27 Funciones para imprimir cadenas de texto · 55 Funciones para imprimir caracteres · 54 Fundamentos de lenguaje C · 20. 94. 235. 103. 205. 67. 198. 71 GLCD Bitmap Editor · 66 Glcd_Box · 72. 214. 238. 103. 90. 118 EEPROM_Read · 117 EEPROM_Write · 117 Ejemplo de diseño filtro rechaza banda · 222 Ejemplo de diseño oscilador doble cuadrado · 225 Ejemplo de diseño para filtro multi banda · 210 Ejemplo de diseño para filtro pasa altas · 207. 107. 195. 183. 133. 229. 125 Introducción · 15 IntToStr · 57. 61.

55. 203 RAM · 18. 272 PWM · 17. 39. 26. 150. 84. 173. 84. 80. 87. 227 MAX232 · 93 MAX485 · 172. 190 PID · 99. 20. 225. 274 Library Manager · 51 LM35 · 128. 257. 71. 116. 175. 18. 117. 266. 146. 132. 136. 158. 176. 132. 103. 59. 166. 188. 180. 261. 75. 274. 269. 204. 21 LongToStr · 59. 246. 46. 277 Relevadores · 263 285 . 223. 146. 58. 59. 158. 226. 270 La sentencia condicional if e if else · 31 La sentencia switch case · 32 LCD · 44. 136. 275 matriz de rotación · 224. 55. 13. 266. 54. 122 Ps2_Config · 84 Ps2_Key_Read · 84. 150. 173. 154. 139. 123. 57. 247. 56. 49. 58. 129. 237. 150. 150. 169. 264. 63. 239. 73. 124. 264 PWM1_Start · 112. 50. 59 Lcd_Cmd · 64. 276 PWM1_Init · 112. 124. 172. 158 PIR1 · 98. 158. 39. 223. 266. 53. 170 Lcd_Out_Cp · 55. 109. 173. 80. 166. 45. 61. 140. 57. 88. 172. 124. 55. 158 PIR2 · 98. 77. 89. 216. 80. 129. 55. 276. 112. 140. 173. 158 PLL · 98. 115. 55. 264. 264. 112. 259. 270 Motores AC · 262 P PICMicro · 17. 87. 175. 228. 158. 175. 58. 158 PIE2 · 98. 94. 51. 77. 87 Puente H · 265 PULL-UP · 165. 228. 50. 276 M main · 29. 181 Mmc_Write_Sector · 178. 176. 109. 5. 70. 248. 118. 76. 275. 111. 72. 117. 125. 66. 38. 83. 129. 48. 133. 203. 63. 137. 61. 100 Prologo · 13 PS2 · 74. 158 Lcd_Init · 51. 224. 174. 269. 52. 65. 44. 264 PWM1_Set_Duty · 112. 30 Osciladores digitales · 224 OSCILLOSCOPE · 113. 119. 174 MAX487 · 174 MCLR · 40 Memoria EEPROM · 117 Memoria FLASH · 118 Memorias EEPROM y FLASH · 117 MikroC PRO · 1. 96. 72. 86. 268. 65. 173. 98. 268. 87. 113. 61. 124. 231. 63. 74. 136. 219. 82. 124. 204. 71. 130. 268. 79. 173. 54. 125. 57. 264 PWM1_Stop · 112 R R-2R · 114. 140. 91. 263 MOTOR · 265. 52. 264. 173. 110. 18. 131 long · 20. 83. 96. 112. 91 PORTB · 30. 126. 126. 275. 249. 64. 237. 137. 146. 126. 110. 140. 190 Mmc_Init · 178 Mmc_Read_Sector · 178. 124. 52. 93. 216. 115. 59. 271. 268. 139. 137. 245. 90. 158. 54. 173. 75. 125 recorte de fase · 273. 122. 231. 90. 181 MOC3011 · 273 MODEM · 167 Módulo de acople con un ordenador personal · 168 Modulo serial I²C · 88 Módulo USART · 91 Módulo USB · 95. 87. 126. 119. 87. 275. 260. 72. 159. 58. 109. 202. 166. 109. 275 opto acopladores · 275 OR · 25. 241 PIE1 · 98. 140. 125. 177. 264. 77. 30. 117. 272. 56. 40. 165. 159. 26. 139. 125 RCIF · 124. 145. 273 motor unipolar · 267. 130. 83. 231. 162. 83. 84. 169. 76. 98. 46. 206. 113. 170 LCD Custom character · 60 Lcd_Chr · 54. 114. 250. 129. 15. 98. 109. 266. 65. 126. 169 Lcd_Out · 55. 133 Muestreo · 190 N NAK · 244. 83. 108 Módulos GPS · 147 Módulos inalámbricos bidireccionales · 167 Módulos inalámbricos infrarrojos · 174 Módulos inalámbricos unidireccionales · 161 Módulos Timer · 121 MOSFET · 262. 116. 178. 87 Lcd_Chr_Cp · 54. 129. 56. 125. 83. 146. 51. 126. 49. 62. 80. 99. 63. 202 PORTA · 48. 126. 272. 177. 234 RCIE · 124. 90. 86. 189. 99. 165. 122. 175. 157. 57. 126. 27. 219. 206. 80. 272. 115. 75. 132. 59. 48. 181 LongWordToStr · 59 motores bipolares · 270 Motores DC · 264 Motores Paso · 267 MPX4115 · 131. 226. 140 LED · 35. 57 LDR · 138. 268. 56. 34. 276 POWER · 36. 165.L L293D · 241. 38. 132. 103. 126. 270. 80. 89. 150. 132. 238. 272. 279 NOT · 25. 30 Nyquist · 190 O Operadores en lenguaje C · 23 OPTION_REG · 122. 62.

122. 95. 181 switch case · 30. 120. 92. 91. 143. 75. 170. 176. 119. 126. 32. 170. 169. 137. 231. 18. 113. 49. 181 UART1_Read_Text · 93 Z Zigbee · 169 286 . 94. 175. 100. 228. 126. 158. 206. 126. 88.mikroe. 78. 119. 126. 119. 49. 83. 189. 189. 95. 98. 175. 276 RS 485 · 172 RS232 · 34. 275 Relojes en tiempo real · 183 RES · 35. 238 Visualización de datos · 44 VSINE · 276 VUSB · 96 T teclado · 18. 90. 119. 173. 119. 173. 110. 58. 180. 223. 181. 181 UART1_Init · 93. 231. 181.c · 99. 95.com · 34 www. 103. 120.virtual-serial-port. 176. 272. 173. 93. 122. 86. 180. 172. 177. 146. 117. 75. 170. 176. 173. 150. 174. 179 Sensor de temperatura LM35 · 128 Sensores · 128 Sensores de distancia · 134 Sensores de humedad y temperatura · 141 Sensores de presión · 131 Sensores LDR · 138 Servomotores · 271 short · 20. 226. 237 unsigned char · 21 unsigned int · 21 unsigned long · 21 unsigned short · 21 USART · 17. 81. 237 UART1_Read · 93. 188. 126. 91.labcenter. 202. 98. 125. 40. 219. 46. 124. 157. 126. 183. 158. 275 Timer 0 · 121. 212 VARs. 115. 79. 94. 204. 140. 99. 169 U UART1_Data_Ready · 93. 76. 179. 96. 88. 268. 107 Uso anidado de ciclos iterativos · 33 Uso de Dip-Switch · 76 Uso de pulsadores · 74 Uso de teclados matriciales · 78 Uso de teclados PS2 o Din · 84 V variable compleja · 191. 87 Teclados y sistemas de entrada de datos · 74 Timer · 17. 180. 178. 189. 177. 268. 107 USBCONN · 96 USBdsc. 102. 273. 119. 189. 162. 79. 202. 179.com · 18 www. 178. 125 TMR0IF · 124. 146 SPI · 88. 84. 167. 189 USB · 18. 165. 95. 125 Transformación bilineal · 213 Transformada discreta de Fourier DFT · 228 Transformada rápida de Fourier FFT · 232 Transmisión · 243 Tratamiento digital de señales · 190 TRIAC · 273 TRISA · 49 TRISB · 30. 120. 121. 189. 39. 275 TRISE · 90 TTL · 172 typedef struct · 22 W WordToStr · 59 www. 265. 77. 145. 150. 181 SPI1_Init_Advanced · 178. 141. 101. 88. 126. 80. 180.reloj · 84. 237 UART1_Write_Text · 94. 105. 172. 89. 149. 216. 83 UART1_Tx_Idle · 93 UART1_Write · 93. 93 S SD · 88. 178. 170. 104 Ventanas Blackman · 201 Ventanas fijas · 196 Ventanas Hamming · 198 Ventanas Hanning · 199 Ventanas Rectangulares · 197 VIP · 99 VIRTUAL TERMINAL · 94. 105. 121. 181. 109. 118. 178. 95. 166. 159. 122 TMR0IE · 124. 166. 77. 94. 142. 169 XBee PRO · 167 X-CTU · 167. 82. 178. 179. 237.com · 147 X XBee · 88. 96. 176. 189. 169. 266. 178. 241. 173. 21 ShortToStr · 59 SHT71 · 146 SHT7x · 141.digi. 130.com · 167 www. 47. 82. 170.h · 99. 179. 145. 98. 34. 126.

287 .

288 .

289 .

Sign up to vote on this title
UsefulNot useful