Você está na página 1de 95

010101000100011101010010

10100101010
0101010101
010101000100010101010100
1010101010101111101010010
01010100010101010

10101010100
0101010000
101010101001010100
10100101010
01010100010101010
10100101010
0110100000101101010101010 101010101001010100
01010101 01010101
101010100

0101010101000010101
01010100010101010

01010101
011010101010
10100101010

01010101010001110101

010101000100010101010100
0101010101
1010101010101111101010010
011010101010
1
101010010101010100
01010100010101010101010
10100101010
10100101010

010101000101010000111101010101010100
1010101010101111101010100101010101010
10101010100

01010100000
0101010000

01010101
10100101010
0101010101

0101010000 1010010111010100
10101010100
010101000100010101010100
1010101010101111101010010

Ing. Gil Arellano


www.credetec.com.mx
ÍNDICE:
Página
.

Índice 2

Descripción del curso, objetivo, alcance 3

Lista de material requerido 5


010101000100010101010100
1010101010101111101010010

Reseña Histórica del PIC 6

Información básica que debemos saber de los PIC 8

Tutorial del compilador CCS para PIC 9


10101010100
0101010000

Circuitos útiles utilizando sensores, amplificadores, transistores 45

Experimento 1 , Led enciende y apaga 85

Experimento 2 , Led y switch 86

Experimento 3 , Semáforo 87
2
Experimento 4 , Led rotando sólo ida 88

Experimento 5 , Led rotando ida y vuelta 89

Experimento 6 , puerto A = B 90

Experimento 7 , Display de 7 segmentos 91

Experimento 8 , Display de cristal líquido (LCD) 92

Experimento 9 , Modulación de Ancho de pulso PWM y Led 93

Experimento 10 , Generación de tonos y melodías 94

Contacto CreDeTec 95
Descripción del Curso

Este curso fue diseñado para personas que desean conocer e introducirse al
mundo de los PIC. Contando con una documentación amplia y la teoría-práctica,
hacen que este curso se vuelva dinámico, desde el primer programa hasta algún
diseño más complejo.

Recomendando siempre el auto aprendizaje, se incluye la primera parte de una


010101000100010101010100
1010101010101111101010010

excelsa compilación de libros y manuales (primera parte), que llevan a la persona


que asiste al curso, a entender y profundizar en el tema de los microcontroladores
PIC desde diferentes puntos, electrónicos, matemáticos, de programación, de
diseño y diversas aplicaciones que propician una experiencia cultural en el mundo
10101010100

de los micros PIC.


0101010000

Finalmente la persona que acuda a este curso tendrá el conocimiento e idea clara
de cómo empezar y desarrollar proyectos de gran alcance, realizando el siguiente
proceso: teoría (PIC18F), programación (CCS), simulación (PROTEUS y CCS),
físicamente (placa de pruebas que usted mismo puede realizar), esto junto a la
creatividad harán un magnífico complemento. 3
Objetivo
El objetivo principal es que la persona interesada en conocer y utilizar micros PIC,
entre mirando y salga programando.

Alcance
Los parámetros de alcance son desde personas que alguna vez hayan visto
un chip, aficionados a la electrónica de los micros, hasta estudiantes y
maestros de: electrónica, programación, computación, control, robótica,
mecatrónica, diseño, instrumentación, que deseen trascender y actualizar
los métodos educativos(micros de antaño), aplicar e implementar nuevas
formas en que la tecnología ha ido cambiando, en prácticas, tareas, incluirlos
en diseños con circuitos, y es más sencillo de lo que imaginan, con valor
personal y curricular.
4
Lista de Material Requerido:

Descripción:

Programador: pickit2 ó Programador-Herramienta 9 en 1

Microcontrolador : PIC18F1320 y/ó PIC18F4550


010101000100010101010100
1010101010101111101010010

Tarjeta de pruebas: Protoboard o bien placa elaborada


8 Leds, un switch ó pushbotton, dip switch
Display de 7segmentos cátodo común, LCD.
10101010100
0101010000

Software pickit 2 v2.6x, programador 9 en 1

pic C compiler(CCS) 5
proteus ISIS.

Documentos del curso:


Presentaciones:

Mundo PIC
Representación Digital

Curso Total
Manual Familia PIC18F
Manual CCS y Proteus
Experimentos y simulaciones
Videos de configuración
Sensores y PIC (hojas de datos)

15 ebook en pdf (PRIMERA PARTE)


Reseña Histórica:
¿Qué es un PIC?

Un PIC es un microcontrolador basado en memoria EPROM/FLASH desarrollado por Microchip


Technology. Otros nombres encontrados en la Red son:

• PROGRAMABLE INTERFACE COMPUTER

• PROGRAMABLE INTELLIGENT COMPUTER

• PROGRAMABLE INTEGRATED CIRCUIT

• PROGRAMABLE INTERFACE COMPUTER


010101000100010101010100
1010101010101111101010010

• PROGRAMABLE INTERFACE CONTROLER

En 1965 GI formó una división de microelectrónica, destinada a generar las primeras


arquitecturas viables de memoria EPROM y EEPROM. De forma complementaria GI
Microelectronics Division fue también
10101010100

Responsable de desarrollar una amplia variedad de funciones digitales y analógicas en las


0101010000

familias AY3-xxxx y AY5-xxxx.

GI también creó un microprocesador de 16 bit, denominado CP1600, a principios de los 70. Este
fue un microcontrolador razonable, pero no particularmente bueno manejando puertos de e/s.

6
Para algunas aplicaciones muy específicas GI diseñó un Controlador de Interface Periférico (PIC)
entorno a 1975. Fue diseñado para ser muy rápido, además de ser un controlador de e/s para
una máquina de 16 bits pero sin necesitar una gran cantidad de funcionalidades, por lo que su
lista de instrucciones fue pequeña.

No es de extrañar que la estructura diseñada en 1975 sea sustancialmente, la arquitectura del


actual PIC. Además, la versión de 1975 fue fabricada con tecnología NMOS y sólo estaba
disponible en versiones de ROM de máscara, pero seguía siendo un buen pequeño
microcontrolador. El mercado, no obstante, no pensó así y el PIC quedó reducido a ser empleado
por grandes fabricantes únicamente. Durante los 80, GI renovó su apariencia y se reestructuró,
centrando su trabajo en sus principales actividades, semiconductores de potencia
esencialmente, lo cual siguen haciendo actualmente con bastante éxito.

GI Microelectronics Division cambió a GI Microelectronics Inc (una especie de subsidiaria), la


cual fue finalmente vendida en 1985 a Venture Capital Investors, incluyendo la fábrica en
Chandler, Arizona. La gente de Ventura realizó una profunda revisión de los productos en la
compañía, desechando la mayoría de los componentes AY3, AY5 y otra serie de cosas, dejando
sólo el negocio de los PIC y de las memorias EEPROM y EPROM. Se tomó la decisión de comenzar
una nueva compañía, denominada Arizona Microchip Technology, tomando como elemento
diferenciador sus controladores integrados.

Como parte de esta estrategia, la familia NMOS PIC165x fue rediseñada para emplear algo que la
misma compañía fabricaba bastante bien, memoria EPROM. De esta forma nació el concepto de
basarse en tecnología CMOS, OTP y memoria de programación EPROM, naciendo la familia
PIC16C5x.

Actualmente Microchip ha realizado un gran número de mejoras a la arquitectura original,


adaptándola a las actuales tecnologías y al bajo costo de los semiconductores.
Reseña Histórica:

Tipos de PIC

Existen diversas familias de PIC, las cuales se amplían constantemente, pero las más básicas son:
C= MEMORIAS OTP (ONE TIME PROGRAMMABLE)

F= MEMORIAS FLASH (GRABABLES HASTA 10 000 VECES)

PIC16F5x: instrucciones de 12 bit, 33 instrucciones, 2 niveles de acumulador, sin


interrupciones. En algunos casos la memoria es del tipo ROM, definida en fábrica.

PIC10F, PIC12Fxx: instrucciones de 12 bit, juego de instrucciones Risk, 8 niveles de


acumulador, tamaño pequeño, 8 pines, con convertidores A/D, reloj interno, una buena opción
010101000100010101010100
1010101010101111101010010

para proyectos pequeños.

PIC16Fxx: instrucciones de 14 bit, 35 instrucciones, 8 niveles de acumulador. El PIC16C84 posee


memoria EEPROM.

PIC17Fxx: instrucciones de 16 bit, 55 instrucciones, 16 niveles de acumulador. A menos que se


10101010100

indique, la memoria es del tipo EPROM.


0101010000

PIC18Fxx: instrucciones de 16 bit, RISK(35 instrucciones), 31 niveles de acumulador, con


interrupciones, memoria flash, con gran variedad de circuitos incorporados, que permiten las
funciones como: convertidor analógico digital, contadores de 16 bits, watch dog timer,
f

detectores de bajo voltaje, comunicación serial, comunicación usb, pwm, etc. 7


PIC24xxx , PIC32xxx, RF PIC, DSPIC: son microcontroladores con un gran poder, unos
dedicados para la radiofrecuencia y transmisión de datos donde el medio de transporte es el
aire, los ds pics, microcontroladores que permiten un gran manejo para señales digitales, e
incluso podemos encontrar dependiendo del tipo de pic, RTC, que son relojes de tiempo real
dentro de la circuitería de nuestro pic, alimentado solo por baterías tipo moneda.

Adicionalmente existen otras familias derivadas, Si desea conocer todas las familias disponibles
consulte la página www.microchip.com
Información básica que debemos saber sobre los PIC:

• Un microcontrolador es diferente a un microprocesador, incluso un


microcontrolador lleva integrado un microprocesador.

• Existen diferentes familias y dependiendo de la aplicación podemos elegir el que más


nos convenga.

• Los precios son relativamente baratos, podemos conseguirlos desde 25 pesos.

• La página de Microchip se dedica a comercializar productos, por lo que ofrece


muchísimas aplicaciones para lo que ellos venden, incluso ya podemos encontrar desde
010101000100010101010100
1010101010101111101010010

conferencias en powerpoint , videos y hasta sensores, displays touch, etc.

• Son un perfecto complemento para controlar Leds de alta luminiscencia, motores,


cerebros para robots y cualquier cuestión que involucre electrónica.
10101010100
0101010000

• Con el compilador CCS, existe compatibilidad de programación, es decir, el programa


principal puede funcionar en cualquier pic, 10F,12F,16F,18F,24F, respetando los pines y
segmentos de memoria reservados.

Una de las ventajas de utilizar CCS es que los protocolos de comunicación como SPI, I2C,
pueden ser utilizados en el PIC aunque no contenga dichas terminales, haciendo una
8
librería y llamándola, el protocolo de comunicación se simula en el PIC.

• Con la versión del pickit 2 y pickit3 podemos programas casi cualquier PIC
comercializado, evitándonos así la necesidad de comprar un programador universal o
cualquier otro programador de alto costo y que luego no funcionan, se hace de la
siguiente manera:

Referencia: microchip.com

Artículo: pickit2_User_Guide_51553e.pdf
TUTORIAL del compilador de C de Custom Computer Services (CCS)
para PIC:

Esta guía o tutorial está dedicada al lenguaje de programación C, concretamente al del


compilador CCS. A lo largo de este artículo iremos viendo los rudimentos del lenguaje y el
repertorio de instrucciones incluidas en él.

Directivas

Llamadas en inglés "preprocessor directives", son comandos que interpreta el


010101000100010101010100
1010101010101111101010010

primer paso de la compilación que lleva a cabo CCS.

#ASM /
#ENDASM

Este par de instrucciones permite que utilicemos un bloque de instrucciones en


10101010100
0101010000

assembler dentro de nuestro código CCS. El siguiente es un ejemplo de uso tomado


de la ayuda del CCS:

Int find_parity (int data)


{
Int count;
#ASM 9
Movlw 0x8
Movwf count
Movlw 0
loop:
Xorwf data,w
rrf data,f
decfsz count,f
Goto loop
movlw 1
Awdwf count,f
movwf _return_
#ENDASM
}

La variable predefinida _RETURN_ puede utilizarse para transferir un valor desde el código
ASM a CCS. Si en lugar de #ASM utilizamos #ASM ASIS, CCS no intentará efectuar cambios de
bancos de memoria automáticos para las variables que no pueden ser accedidas desde el
banco actual. El código assembler es utilizado "as-is" ("como es").
#BIT

Permite crear una nueva variable de un bit de tamaño, que es colocada en la memoria
del PIC en la posición del byte x y el bit y. Esto es muy útil para acceder de una manera
sencilla a los registros. Por supuesto, estas variables pueden ser empleadas de la
misma manera que cualquier otra variable tipo short. El formato de #BIT es el
siguiente:

#BIT nombre = x.y


010101000100010101010100
1010101010101111101010010

Donde nombre es un nombre de variable CCS válido, x es una constante o una variable CCS
válida e y es una constante de 0 a 7.

Estos son algunos ejemplos de uso:

Ejemplo a)
10101010100
0101010000

#BIT T0IF = 0xb.2


.
.
T0IF = 0; // Limpia el flag de interrupción del Timer 0
10
Ejemplo b)

Int resultado;

#BIT resultado_primer_bit = resultado.0


.
.
If (resultado_primer_bit)

#BYTE

Permite crear una nueva variable de un Byte de tamaño, que es colocada en la memoria
del PIC en la posición del byte x. Esta es una herramienta muy útil para acceder de una
manera sencilla a los registros. Por supuesto, estas variables pueden ser empleadas de la
misma manera que cualquier otra variable tipo int. El formato de #BYTE es el siguiente:

#BYTE nombre = x

Donde nombre es un nombre de variable CCS válido, y x es una constante o una


variable CCS válida. Estos son algunos ejemplos de uso:

Ejemplo a)

#BYTE STATUS = 3

Ejemplo b)

#BYTE PORTB = 0XF81


#DEFINE

La instrucción #define tiene la siguiente forma:

#DEFINE <label> value

<label> es la etiqueta que usaremos en nuestro programa. Y value es el valor que estamos
asignando a esta etiqueta. Las instrucciones #DEFINE no generan código ASM, si no que el
preprocesador realiza los reemplazos que ellas indican en el momento de la compilación. El uso
de #DEFINE permite construir programas más ordenados y fáciles de mantener.

Veamos algunos ejemplos de #DEFINE:


010101000100010101010100
1010101010101111101010010

Ejemplo a)

#DEFINE TRUE 1

Cada vez que en nuestro programa aparezca la etiqueta TRUE, el precompilador la reemplazará
10101010100
0101010000

por 1

Ejemplo b)

#DEFINE pi 3.14159265359 11
Cada vez que en nuestro programa aparezca la etiqueta pi, el precompilador la reemplazará por

3.14159265359

Ejemplo c)

#DEFINE MENOR_DE_EDAD (EDAD < 18)

If MENOR_DE_EDAD

Printf(“JOVEN”);

El ejemplo anterior permite una mayor claridad en el programa. Por supuesto, no hay que abusar
de #DEFINE, porque podemos obtener el efecto contrario, haciendo nuestros programas bastante
difíciles de comprender. #DEFINE es una potente herramienta para la creación de
macroinstrucciones, ya que soporta el uso de variables. Veamos algunos ejemplos de esto:

Ejemplo d)

#DEFINE var(x,v) unsigned int x=v; var(a,1)

Var(b,2) , Var(c,3)

Cuando el preprocesador se encuentra con el código anterior, hace lo mismo que si hubiésemos
escrito lo siguiente:

Unsigned int a=1; unsigned int b=2; unsigned int c=3;


#DEVICE

Esta directiva informa al compilador que arquitectura de hardware utilizaremos, para que pueda
generar código apropiado para la cantidad de RAM, ROM y juego de instrucciones disponibles.
Para los chips con más de 256 bytes de RAM se puede seleccionar entre emplear punteros de 8 o
16 bits. Si deseamos emplear punteros de 16 bits basta con añadir *=16 a continuación del
nombre microcontrolador seleccionado.

Veamos algunos ejemplos:

Ejemplo a)

#DEVICE PIC16F74 //familia PIC 16C74, punteros de 8 bits.

#DEVICE PIC16F67 *=16 //familia PIC 16C67, punteros de 16 bits.


010101000100010101010100

Hay más opciones que podemos agregar en las líneas #DEVICE:


1010101010101111101010010

• ADC=x : Determina el número de [bits] que devuelve la función read_adc().

#DEVICE PIC16F877 *=16 ADC=10 //PIC16F877, punteros de 16 y 10 bits en el ADC.


10101010100

• ICD=TRUE : Genera código compatible con el ICD de Microchip [www.microchip.com ].


0101010000

#DEVICE PIC16F877 ICD=TRUE//PIC 1616F877, punteros de 8 bits y código para ICD.

• WRITE_EEPROM=ASYNC : 12
• HIGH_INTS=TRUE : Define la prioridad de las interrupciones en los PIC18.

#FUSE

Permite modificar el valor de los fuses del microcontrolador que estamos empleando. Los valores
posibles dependen de cada microcontrolador en particular, y los valores posibles se cargan al
utilizar #INCLUDE seguido del archivo correspondiente. La forma de #FUSE es la siguiente:

#FUSE opciones

Donde opciones es una lista de las opciones posibles separadas mediante comas. Antes de seguir,
recuerda que puedes ver dentro del archivo con extensión .h correspondiente cuales son los
valores posibles para ese microcontrolador. Están al comienzo del archivo, en forma de
comentarios.

Algunos valores comunes son:

• Tipo de oscilador: LP, XT, HS, RC

• Wach Dog Timer: WDT, NOWDT

• Protección de código: PROTECT, NOPROTECT

• Power Up Timer: PUT, NOPUT

• Brown Out Reset: BROWNOUT, NOBROWNOUT


#INCLUDE

Permite incluir en nuestro programa uno o más archivos (conocidos como header file) que posean
extensión .h. Estos archivos contienen información sobre funciones, sus argumentos, el nombre
de los pines de un modelo determinado de PIC o cualquier otra cosa que usemos habitualmente
en nuestros programas. Esto permite no tener que escribir un montón de cosas cada vez que
comenzamos un programa nuevo: basta con incluir el .h correspondiente.

La forma de utilizar esta instrucción es la siguiente:

#INCLUDE <archivo>

Esto hará que el contenido de <archivo> se compile junto con nuestro programa. Por ejemplo:

Ejemplo a)

#INCLUDE <PIC18F1320.H>
010101000100010101010100
1010101010101111101010010

#INCLUDE<LCD.C>

Hace que todas las especificaciones de nombres y registros del PIC18F1320 se incluyan en nuestro
programa. Esto permitirá referirnos al pin 0 del PORTB del PIC mediante PIN_B0.
10101010100

Existe la posibilidad de utilizar #INCLUDE "archivo" en lugar de #INCLUDE <archivo>. La diferencia


0101010000

es que si usamos " ", el archivo se buscará primero en el directorio actual. Si empleamos <>, el
archivo será buscado primero en la ruta por defecto para los archivos .h
13
#INT_xxx

#INT_xxx indica que la función que le sigue (en el código fuente CCS) es una función de
interrupción. Estas funciones no deben tener parámetros. Por supuesto, no todos los pics
soportan todas las directivas disponibles:

1. INT_AD Conversión A/D finalizada. 2. I NT_ADOF Conversión A/D timeout.

3. INT_BUSCOL Colisión en bus. 4. INT_BUTTON Pushbutton.

5. INT_CCP1 Unidad CCP1. 6. INT_CCP2 Unidad CCP2.

7. INT_COMP Comparador. 8. INT_EEPROM Escritura finalizada.

9. INT_EXT Interrupción externa. 10. INT_EXT1 Interrupción externa #1.

11. INT_EXT2 Interrupción externa #2. 12. INT_I2C Interrupción por I2C.

13. INT_LCD Actividad en el LCD. 14. INT_LOWVOLT Bajo voltaje detectado.

15. INT_PSP Ingreso de datos en el Parallel Slave Port. 16. INT_RB Cambios en el port B (B4-B7).

17. INT_RC Cambios en el port C (C4-C7). 18. INT_RDA Datos disponibles en RS-232.

19. INT_RTCC Desbordamiento del Timer 0 (RTCC). 20. INT_SSP Actividad en SPI o I2C.

21. INT_TBE Buffer de transmisión RS-232 vacío. 22. INT_TIMER0 Desbordamiento del Timer 0 (RTCC).

23. INT_TIMER1 Desbordamiento del Timer 1. 24. INT_TIMER2 Desbordamiento del Timer 2.

25. INT_TIMER3 Desbordamiento del Timer 3.


Variables

La programación sería prácticamente imposible sin el uso de variables. Podemos hacernos una
imagen mental de las variables consistente en una caja en la que podemos guardar algo. Esa caja
es una de las muchas que disponemos, y tiene en su frente pegada una etiqueta con su nombre.
Estas cajas tienen ciertas particularidades, que hace que solo se puedan guardar en ellas
determinados tipos de objetos.

En esta analogía, cada caja es una variable, su contenido es el valor que adopta, y la etiqueta es el
nombre de la variable. Como su nombre lo indica, y como veremos más adelante, el contenido de
una variable puede ser modificado a lo largo del programa.

Tipos

El lenguaje C proporciona cinco tipos básico de datos, con cuatro modificadores posibles.
Podemos utilizar variables de cualquiera de esos tipos. La tabla siguiente muestra los tipos
disponibles:

Tipo Ancho (Bits) Rango


010101000100010101010100
1010101010101111101010010

Short 1 0o1

Short int 1 0o1


14
10101010100

Int 8 0 a 255
0101010000

Char 8 0 a 255

Unsigned 8 0 a 255

Unsigned int 8 0 a 255

Signed 8 -128 a 127

Signed int 8 -128 a 127

Long 16 0 a 65536

Long int 16 0 a 65536

Signed long 16 -32768 a


32767

Float 32 3.4E-38 a
3.4E+38

Si miras con atención la tabla anterior, puedes ver que hay tipos que parecen estar
repetidos. En realidad, ocurre que CCS permite una "forma corta" para escribir
algunos de los tipos. Concretamente, podemos utilizar unsigned, short, o long en
lugar de unsigned int, short int, o long int.
Así se Declaran las Variables:

Las variables deben ser declaradas antes de ser utilizadas en el programa. El


proceso de declaración de variables le dice a CCS de que tipo son y como se
llaman. Al igual las demás instrucciones CCS que veremos a lo largo de este
tutorial, debe terminar con ;

La forma en que se declara una variable es la siguiente:

Tipo nombre_de_la_variable;

Donde tipo es alguno de los enumerados en la tabla anterior. Veamos un ejemplo:


Ejemplo a)

Int temperatura;

Esa línea permite a nuestro programa emplear la variable temperatura, que


será capaz de albergar cualquier valor comprendido entre 0 y 255.
010101000100010101010100
1010101010101111101010010

Asignación de Valores

Asignar un valor a una variable es una tarea bien simple. Basta con hacer lo
siguiente: 15
10101010100

Nombre_de_variable = valor;
0101010000

Donde nombre_de_variable es el nombre de la variable que contendrá el valor. Al


igual que todas las instrucciones de CCS, debe terminar con un ; (punto y coma).
Por ejemplo, supongamos que queremos asignar el valor "100" a la variable
"contador". Lo hacemos de la siguiente manera:

Contador = 100;

Donde 100 es una constante. Podemos asignar un valor a una variable en


el momento en que la declaramos. Los siguientes son algunos ejemplos de
esto:

Ejemplo a)

Signed long a = 125, b, c = -10;

a,b y c son declaradas como long. A toma el valor de "125" y c "-10".


Así se Declaran las Variables:

Si la variable es de tipo char, la constante que se le asigna debe estar entre tildes, como
en el siguiente ejemplo:

Ejemplo b)

Char nombre = 'gil arellano';

Por último, también podemos asignar a una variable el contenido de otra. En el siguiente
ejemplo, el valor de i será igual a 10,

Ejemplo c)

int i = 10;

int j;

j = 1;

Variables LOCALES y GLOBALES


010101000100010101010100
1010101010101111101010010

Si una variable se declara dentro de una función, será "visible" solo dentro de ésta:

Ejemplo a)

funcion1 ( ) 16
{
10101010100
0101010000

Char letra;
.
.
}

En el ejemplo anterior, la variable tipo char llamada letra solo podrá


utilizarse dentro de la función funcion1( ). Si intentamos utilizarla fuera de
ella, el compilador nos dará un error. Si declaramos una variable fuera de
cualquier función, el alcance de esta será global, lo que quiere decir que
estará disponible en cualquier parte de nuestro programa. Vemos un
ejemplo de este último caso.

Ejemplo b)

Char letra;

Main()
{
.
}

Funcion1()
{
.
}
La variable tipo char llamada letra podrá utilizarse dentro de main() o de funcion1().
Así se Declaran las Variables:

Conversiones entre Tipos de Variables

CCS nos permite mezclar diferentes tipos de variables dentro de una misma expresión. Y existen
un conjunto de reglas que nos permiten saber de qué tipo será el resultado de la misma.

Por ejemplo, el compilador convertirá automáticamente a int cualquier expresión que contenga
variables char, short o int. Esta conversión solo tiene efecto mientras se realizan los cálculos. Las
variables en sí mismas no cambian su tipo.

Las reglas de conversión de tipos hacen que el resultado de una operación sea siempre el mismo
que el de la variable más larga que intervenga en ella.

Sin embargo, podemos forzar a que el resultado sea de un tipo en particular, de la siguiente
forma:

(tipo) valor

Donde tipo es el tipo al que queremos que pertenezca valor. El siguiente ejemplo nos aclarará
todo esto:
010101000100010101010100
1010101010101111101010010

Ejemplo a)

int a = 250, b = 10;


17
long c;
10101010100
0101010000

c = a * b;

Tal como explicamos, la letra ”c” no contendrá el valor 2500 como podría parecer a simple vista,
por que el tipo de c no se modifica. CCS calcula a * b y obtiene efectivamente el resultado 2500,
pero “c” sólo contendrá los 8 bits menos significativos de ese resultado, es decir, el decimal 196.

Si hubiésemos hecho:

int a = 250, b = 10;

long c;

c = (long) (a * b);

El valor almacenado en la letra “c” hubiese sido efectivamente 2500.


OPERADORES :

En CCS los operadores cumplen un rol importante. Quizá C sea uno de los lenguajes
que más operadores tiene. Una expresión es una combinación de operadores y
operandos. En la mayoría de los casos, los operadores de CCS siguen las mismas
reglas que en álgebra, y se llaman de la misma manera.

Operadores aritméticos

CCS posee cinco operadores aritméticos:

• + (suma)

• - (substracción)

• * (multiplicación)

• / (división)

• % (módulo)
010101000100010101010100
1010101010101111101010010

Los primeros cuatro operadores mencionados se pueden utilizar con cualquier tipo
de dato. Estos son algunos ejemplos de cómo usarlos: 18
10101010100

a = b + c;
0101010000

a = b - c;

a = b * c;

a = b / c;

a = -a; //Cambia el signo de "a".

a = a + 1; //suma 1 al valor de "a".

El operador % (módulo) solo puede emplearse con enteros. Devuelve el resto de una
división de enteros. Veamos un par de ejemplos:

Ejemplo a)

int a = 10, b = 5, c;

c = a % b; //"c" valdrá cero.

Ejemplo b)

int a = 20, b = 3, c;

c = a % b; //"c" valdrá 2.
OPERADORES:

CCS también provee atajos para utilizar los operadores aritméticos. Hay algunas
operaciones que se repiten a menudo cuando creamos nuestros programas, y estos
atajos ayudan a que podamos escribir nuestro código más rápidamente. Los atajos
provistos son los siguientes.

• a*= b es lo mismo que a=a*b

• a /= b es lo mismo que a=a/b

• a += b es lo mismo que a=a+b

• a-=b es lo mismo que a=a-b

• a%=b es lo mismo que a=a%b

Operadores Relacionales
010101000100010101010100
1010101010101111101010010

Los operadores relacionales comparan dos valores, y devuelven un valor lógico basado en
el resultado de la comparación. Los operadores relacionales disponibles son los
siguientes:
19
• >mayor que
10101010100
0101010000

• >= mayor que o igual a

• < menor que

• <= menor que o igual a

• == igual a

• != distinto de

El resultado de la comparación, será siempre 0 o 1. 0 significa que el resultado de la


comparación ha sido falso, y 1 que ha sido verdadero.
OPERADORES:

Operadores Lógicos

Los operadores lógicos nos permiten realizar las operaciones AND, OR y NOT:

• p && q significa p AND q

• p || q significa p OR q

• !P significa NOT p o negada

Por supuesto, puede emplearse más de un operador lógico en la misma expresión:

a = b && ( q || n )

Y se pueden combinar con los demás operadores vistos:

a = !(maximo <= 100) //a será 1 si maximo es mayor que 100.

Operadores de BIT

Existen seis operadores pensados para trabajar directamente sobre los bits. Solamente pueden
010101000100010101010100

20
1010101010101111101010010

usarse con variables tipo int y char. Son los siguientes:

• & (AND)

• | (OR)
10101010100
0101010000

• ^ (XOR)

• ~ (complemento)

• << (desplazamiento a la izquierda)

• >> (desplazamiento a la derecha)

Estas operaciones se llevan a cabo bit por bit, veamos un ejemplo:

Supongamos que a = 120 y b = 13.

• a&b=8

• a | b = 125

• a ^ b = 117

• ~ a = 135
OPERADORES:

Operadores de bit

El porqué de estos resultados puede comprenderse mejor si se pasan los valores de a y b


a binario:

a = 01111000 b = 00001101

01111000 AND 00001101 = 00001000

01111000 OR 00001101 = 01111101

01111000 XOR 00001101 = 01110101

a NOT 01111000 = 10000111

b NOT 00001101 = 11110111

Operadores de Desplazamiento << y >>

Los operadores de desplazamiento "corren" el contenido de la variable a la derecha o a la


izquierda, rellenando con ceros. Veamos algunos ejemplos: 21
Ejemplo a)
010101000100010101010100
1010101010101111101010010

A = a >> 2 //"corre" el contenido de a dos lugares a la derecha Si a era igual a 120 (


01111000 en binario) pasará a valer 30 (00011110 en binario). Ejemplo b)

A = a << 3 //"corre" el contenido de a tres lugares a la izquierda


10101010100
0101010000

Si a era igual a 120 (01111000 en binario) pasará a valer 192 (11000000 en binario).

CCS también provee atajos para utilizar los operadores de bits. Hay algunas operaciones
que se repiten a menudo cuando creamos nuestros programas, y estos atajos ayudan a
que podamos escribir nuestro código más rápidamente. Los atajos provistos son los
siguientes:

• a <<= b es lo mismo que a = a << b

• a >>= b es lo mismo que a = a >> b

• a &= b es lo mismo que a = a & b

• a |= b es lo mismo que a = a | b

• a ^= b es lo mismo que a = a ^ b
OPERADORES:

Otros Complementos utilizados.

Quedan por ver aun dos operadores más:

• ++ Operador incremento (incrementa uno)

• -- Operador decremento (decrementa uno)

Estos operadores permiten sumar (o restar) uno al valor de una variable. Lo que
generalmente haríamos así:

a = a + 1 se puede hacer así: a++

ó así:

a = a – 1 se puede hacer así: a--

Precedencia de los operadores


010101000100010101010100

Al igual que ocurre en álgebra, en CCS los operadores se evalúan en un orden


1010101010101111101010010

determinado. La siguiente lista muestra este orden, ordenado de mayor a menor:

• ( ) 22
• signo +, signo - , ++ , - -, ! , (<tipo de operación>)
10101010100
0101010000

• *, /, %

• +, -

• < , <= , > , >=

• ==, !=

• && , ||

• =, +=, -=,*=, /=, %=


FUNCIONES:

Las funciones son segmentos de programa que tecleamos una vez, puede ser varias líneas,
las nombramos de algún modo y posterior mente en vez de volver a escribir todas las líneas
sólo hacemos un llamado a la función por su nombre.

Las funciones tienen la siguiente forma:

nombre_de_la_funcion ( )

instruccion;
instruccion;
instruccion;

Para evitar que surjan errores o avisos (warnings) al compilar nuestros programas,
debemos declarar las funciones antes de utilizarlas.

Prototipos de Funciones:

Existen dos formas de decirle al compilador CCS que tipo de valor devolverá nuestra
función. La forma general es la siguiente:
010101000100010101010100
1010101010101111101010010

Tipo nombre_de_funcion ( ) ;
23
Donde tipo es cualquiera de los tipos de variables soportados por CCS. Al igual que
10101010100
0101010000

cualquier instrucción de CCS, la línea debe termina con ; (punto y coma).

El siguiente ejemplo declara la función ejemplo( ) que devuelve como resultado un


valor del tipo long:

long ejemplo( );
long multiplicación( );

Parámetros:

Además de determinar el tipo de resultado que devolverá la función, en el prototipo


podemos especificar que parámetros recibirá, y de que tipo serán. La forma de hacerlo es
la siguiente:

tipo nombre_de_funcion ( tipo var1, tipo var2, …....., tipo varn);

La diferencia con el caso anterior es que se han incluido dentro de los ( ) una serie de
nombres de variables (var1, var2, ..., varn), cada una asociado a un tipo en particular.
FUNCIONES:

Supongamos que queremos crear una función que lleve a cabo la suma de dos de tipo
int, que le son pasados como argumentos, y nos devuelva el resultado en formato
double. Deberíamos escribir así su prototipo:

double suma(int a, int b);

Donde a y b son los valores a sumar. El llamado a la función se puede hacer de la siguiente
manera:

int a, b;
double resultado;
a = 50;
b = 250;
resultado = suma (a, b);

Por ende resultado contendrá el valor "300", resultado=300

Return
24
La forma en que se asigna en la función y el valor que ésta debe devolver es mediante la
instrucción return. Veámoslo con el ejemplo de la función suma vista más arriba. La función
podría ser como sigue:

double suma(int a, int b)


{
double auxiliar;
auxiliar = (double) (a + b );
return auxiliar;
010101000100010101010100
1010101010101111101010010

Otra forma, más corta, de escribir la misma función es la siguiente:

double suma(int a, int b)


10101010100

{
0101010000

return (double) a + b;
}
FUNCIONES:

VOID
Void significa que la función no devolverá ningún parámetro. Supongamos que la
función ejemplo( ) no debe regresar ningún valor luego de ser llamada. Su prototipo
debería ser como sigue:

void ejemplo( );

Además, podemos usar void para indicar que la función no recibe parámetros:

void ejemplo2(void);

En el ejemplo, la función ejemplo2( ) no recibe parámetros, ni devuelve ningún valor.

MAIN

Como hemos visto, el lenguaje C permite la utilización de funciones. Pero hay una función
especial, llamada main( ) que obligatoriamente debe estar presente, y es el punto de
010101000100010101010100

entrada a todo programa en C que escribamos.


1010101010101111101010010

La función main( ) tiene la siguiente forma: 25


main ()

{
10101010100
0101010000

instruccion;
instruccion;
instruccion;

}
Donde “instruccion;” puede ser cualquier instrucción válida del CCS o una llamada a otra
función.
PUNTEROS:

Una de las características más interesantes de las diferentes versiones de C son los punteros. Por
supuesto, CCS permite el manejo de punteros, con lo que nuestros programas pueden aprovechar
toda la potencia de esta herramienta.

¿Qué es un puntero?

Un puntero es una variable cuya finalidad es almacenar números ENTEROS POSITIVOS. Estos
números no son números al azar, son direcciones de la memoria que posee el hardware del
microcontrolador (memoria de programa o RAM).

¿Para qué pueden servir los punteros?

Esta es la pregunta que puede alborotar a más de un programador de C. Sirve para muchísimas
cosas:

• Acceso a la memoria RAM del PIC.

• Ahorrar memoria RAM.

• Modificar más de una variable dentro de una función (puede devolver más de un valor)
010101000100010101010100
1010101010101111101010010

• En arreglos y cadenas strings (arrays, matrices) juega un papel importantísimo.

• Permite crear tablas grandes de datos (solo PIC con acceso a la memoria de programa). 26
• En una computadora se amplía el abanico de opciones.
10101010100
0101010000

• Más abajo veremos detalladamente cómo hacer todo esto.

¿Cómo funcionan los punteros?

Para entender el uso de estas variables especiales hay que comprender bien un concepto:

Cuando se crea una variable en CCS (llamado registro en ensamblador), el compilador reserva un
espacio de memoria cuyo tamaño varía de acuerdo al tipo de dato.

Como todo en el mundo electrónico/digital, está basado en 2 cosas:

• El registro: es la casilla (casa) donde se almacena el dato.

• La dirección del registro: es la posición (dirección) en la memoria donde está alojado el


registro.

Así pues tenemos 2 elementos diferentes pero que se relacionan.

Conociendo la dirección del registro o variable y pudiéndolo manejar nos da un poderosa


herramienta para agilizar/simplificar nuestros programas.
PUNTEROS:

¿Cómo podemos acceder a la dirección de una variable?

En CCS se hace a través del operador &. Veamos un ejemplo:

#include <18F1320.h>
#use delay(clock=8000000)

void main ( )
{
int t,k;
t=5;
k = &t;
delay_cycles(1);
}
010101000100010101010100
1010101010101111101010010

27
Cuando detenemos en delay_cycles(1) vemos que en k se guarda la dirección de la
variable t, ¿y qué guarda t? Guarda el número 5. Todo se realiza usando memoria RAM ó
el registro de propósito general GPR.
10101010100
0101010000

Vamos a cambiar ligeramente el código. Usemos 3 variables tipo entero (int):

#include <18F1320.h>
#use delay(clock=8000000)

void main ( )
{
int k,l,m;
int t,u,v;
t=0XFA , u=0XFB , v=0XFC;
k= &t , l= &u , m= &v;

delay_cycles(1);
}
PUNTEROS:

Se repite lo mismo, el resultado de las direcciones en k, l y m son contiguas. Pero... ¿por qué?

Para responder esta pregunta vamos a cambiar el código otra vez, declarando los 3 tipos de
registros conocidos, int, long y float:

void main ( )

int k,l,m,n;

int t; long u; float v; int z;

t=0xfa; z=0xff; u=0xfffa; v=3.45000000; k= &t; l= &u; m= &v; n=&z;

delay_cycles(1);

}
010101000100010101010100
1010101010101111101010010

28
10101010100
0101010000

Observa que las direcciones de t, u y v saltan. ¿Por qué?

Dependiendo del tipo de dato se consume >= 1 byte de memoria. En el caso de t es un entero, y
los enteros ocupan 1 byte (0-255),u es un dato "entero largo", ocupa dos bytes (0-65535) v es un
dato "coma flotante", con parte fraccionaria en el sistema decimal y toma 4 bytes de memoria (32
bits)

• en t tenemos una dirección que ocupa un byte [0xa]

• en u tenemos una dirección que ocupa 2 byte [0xb - 0xc]

• en v tenemos una dirección que ocupa 4 bytes [0xd - 0x10]


PUNTEROS:

Probando Punteros

Siempre que se declare una variable puntero, al momento de usarlo se debe especificar la
dirección de apuntamiento de la variable normal, porque entonces no se puede guardar un dato
sino sabemos dónde lo vamos a guardar. (Es obvio pero es cierto) .Esto quiere decir que se le
debe pasar el número por valor de la dirección de la variable normal. Recordemos que:

• Pasar un dato por valor: se copia el dato de una variable a otra.

• Pasar un dato por referencia: se mueve/modifica el dato en la misma variable.

• Variable normal: la variable que normalmente usamos.

• Variable puntero: es la variable especial que estamos estudiando.

Veamos un ejemplo sencillo usando punteros:

#include <18F1320.h>

#use delay(clock=8000000)

void main()
010101000100010101010100

29
1010101010101111101010010

int k; // variable normal

int *p; // la variable puntero k=0xfa; // k <- 0xfa


10101010100

*p=0x5;
0101010000

delay_cycles(1);

Dentro del código reconocemos de inmediato quien es el puntero: el que tiene el símbolo * debe
ir antes de la letra p y sin separación:

*p // así es como se debe declarar

Pero... ¡no aparece nada en p! ¿Por qué?


PUNTEROS:

Es simple: porque no fijamos una dirección que apuntara p, y esto es muy importante
saberlo, era lo que se decía al inicio. Vamos a darle la dirección de k:

#include <18F1320.h>
#use delay(clock=8000000)

void main()

int k; // variable normal


int *p; // la variable puntero
p=&k; // dirección de k copiada en p…… k=0xfa; // k <- 0xfa
*p=0x5; // k <- 0x5 delay_cycles(1);

}
010101000100010101010100
1010101010101111101010010

Ahora si funciona correctamente el código. Si ven la línea: 30


p=&k; // dirección de k copiada en p
10101010100

Podrán observar que se usa el puntero sin el *. Esto significa que se guardará allí una
0101010000

dirección y el compilador lo interpreta de esa manera.

Y con esta línea:

*p=0x5; // k <- 0x5

Se está modificando el contenido de k, (indirectamente)

Otro detalle a tomar en cuenta es que para apuntar ciertos tipos de datos, es que se debe
declarar al apuntador con el mismo tipo de datos:

int k; // si queremos apuntar a k int *p; // p debe ser tipo int

char c; // si queremos apuntar a c char *p // p debe ser tipo char

No quiere decir que el tipo de datos que contendrá el puntero sea de ese tipo de datos, el
puntero siempre soportará números enteros positivos, en realidad esto ya es a nivel
interno del compilador.
PUNTEROS:

Un ejemplo más:

En este código hay 2 punteros y 3 variables normales:

void main()

int i; // variable normal


int *p; // la variable puntero int j;
int *q;
int k;
p=&i; // dirección de i copiada a p q=&j;

i=0xfa; // i <- 0xfa j=0x11;


k=0x22;
*p=0x5; // i <- 0x5
*q=0x33;
delay_cycles(1);
}
010101000100010101010100
1010101010101111101010010

31
10101010100
0101010000

• Entre i, p hay 1 byte -> i ocupa 1 byte.

• Entre p, j hay 2 bytes -> puntero p ocupa 2 bytes.

• Entre j, q hay 1 byte -> j ocupa 1 byte

• Entre q, k hay 2 bytes -> puntero q ocupa 2 bytes.

Veamos otros ejemplos más abajo.


PUNTEROS:

Modificando el código para que i sea del tipo float:

void main()

float i; // variable normal


float *p; // la variable puntero

long j;
long *q;
int k;

i=2.51; // i <- 0xfa


j=0x11;
k=0x22;

p=&i; // dirección de i copiada en p,


q=&j;

*p=3.99; // i <- 0x5


*q=0x33;
010101000100010101010100

delay_cycles(1);
32
1010101010101111101010010

}
10101010100
0101010000

• Entre i, p hay 4 bytes -> i ocupa 4 bytes.


• Entre p, j hay 2 bytes -> puntero p ocupa 2 bytes.
• Entre j, q hay 2 bytes -> j ocupa 2 bytes.
• Entre q, k hay 2 bytes -> puntero q ocupa 2 bytes.

En ambos casos a pesar que cambiamos el tipo de declaración de los punteros, se mantienen
en 2 bytes, eso quiere decir que para el compilador el tamaño de un puntero es de 2 bytes.
No confundir con el tipo de datos a direccionar, pues eso es otra cosa.

Veamos otro ejemplo más abajo.


PUNTEROS:

Vamos con otro ejemplo. Supongamos que i sea del tipo float (4 bytes) pero su apuntador lo
declaramos como int (1 byte):

void main()
{

float i; // variable normal


int *p; // la variable puntero
long j;
long *q;
int k;

i=2.51; // i <- 0xfa


j=0x11;
k=0x22;

p=&i; // dirección de i copiada en p


q=&j;

*p=3.99; // i <- 0x5


*q=0x33;
010101000100010101010100

delay_cycles(1);
1010101010101111101010010

} 33
10101010100
0101010000

Noten que el nuevo valor de i no corresponde con el valor que indirectamente le dimos con
el apuntador.

¿Por qué? Porque declaramos a ese apuntador como entero (int) y con ello le estamos
diciendo al compilador que reserve para p 1 byte de dirección en vez de 4 bytes que son
los que se necesitan y por eso ocurre ese truncamiento y da ese valor extraño.

¿Cómo vamos a corregir esto?


PUNTEROS:

Para corregir esto, se declara a p del MISMO tipo de dato de i:

void main ( )
{

float i; // variable normal


float *p; // la variable punter
long j;
long *q;
int k;

i=2.51; // i <- 0xfa


j=0x11;
k=0x22;

p=&i; // dirección de i copiada en p


010101000100010101010100

q=&j;
1010101010101111101010010

*p=3.99; // i <- 0x5


*q=0x33;

delay_cycles(1);
10101010100
0101010000

}
34

Aquí se lee que está correcto el resultado.

Nota: los punteros tiene un máximo de 2 bytes para almacenar direcciones y el CCS
sigue la misma normativa. Hay una directiva llamada #device xxxxxxx

Pon atención a los siguiente.


PUNTEROS:

010101000100010101010100
1010101010101111101010010

Con 4 modos de selección: CCS2,CCS3,CCS4 y ANSI. Con CCS2 y CCS3 el tamaño (size) del
10101010100

puntero es de 1 byte en partes de 14, 16 bits y con CCS4 (modo por defecto) el size es de 2
0101010000

bytes. 35
Analizando nuevamente lo hablado referente al size de los punteros en CCS, y en un
intento de explicar que el tipo de dato y el tamaño del apuntado son 2 cosas distintas,
vamos hacer un ejemplo donde se verá claramente.

Para ello vamos a usar una directiva llamada #locate, sobre la que la ayuda del compilador
es algo así:

#LOCATE funciona del mismo modo que #BYTE además previene que C utilice datos de esta
área reservada.

Bueno esto quiere decir que la variable normal la puedo alojar en cualquier dirección de la
RAM (dentro de ciertos limites). Algo así como si en ensamblador escribiéramos:

variable_normal EQU 0xFFAA

Y para lo que nos va a servir y diseñar un programa claro y limpio.


PUNTEROS:

Esto nos servirá porque sería como manipular el contenido de un puntero pero en tiempo de
diseño

#include <18F1320.h>
#use delay(clock=8000000)

int dato=0xaa; // declaramos dato (GPR) y lo cargamos con 0xaa

#locate dato = 0xff // le decimos al compilador que dato estará en la dirección 0xff
//del área de registro de propósito general, traducido, en la RAM del
//PIC

void main ( )
{

int *p; // declaramos un puntero como entero (igual que dato)


int t; // otra variable normal
010101000100010101010100
1010101010101111101010010

p=&dato; // inicializamos al puntero


*p=0xbb; // dato <- 0xbb

delay_cycles(1); // un nop
}
10101010100
0101010000

36
PUNTEROS:

Fíjense que el puntero p ocupa 2 bytes a pesar que está declarado como int (1 byte).
Vamos a modificar este ejemplo pero usando float (4 bytes) y colocando el GPR en la
dirección 0xaf
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

37
PUNTEROS:

Observen que el puntero p se mantuvo en 2 bytes siendo éste declarado como float.
Supongamos un ejemplo para el PIC18F1320, en el que tenemos una memoria de datos que
llegara hasta 0x7FF (Ver su hoja de datos pues es un ejemplo). Para que funcione 0x7FF debe
ser el 4 byte para un float, entonces:

void main ( )
{

float dato=1.23456789;
#locate dato = 0x7fb

}
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

38

Si que funcionó. Pero, ¿qué pasa si asignamos el dato a 0x800?

Allí vemos que el puntero se cargó bien, pero el SIMULADOR delata el desbordamiento,
¿Por qué? Es que a partir de allí no hay memoria de datos y las direcciones se deberían
leer como 0x00 a pesar que compiló bien, (similarmente en programas de
computadoras pueden ocurrir los lazos infinitos popularmente nominado: ‘se colgó la
máquina, se trabó, se pasmó’).
PUNTEROS:

Punteros en Funciones.

Todo lo que hagamos en CCS se hace a través de funciones o procedimientos, desde el


punto de vista matemático una función se define así:

Una función es una relación entre dos variables numéricas, habitualmente las denominamos
x e y; a una De ellas la llamamos variable dependiente pues depende de los valores de la otra
para su valor, suele ser la y; a la otra por tanto se la denomina variable independiente y suele
ser la x.

Pero además, para que una relación sea función, a cada valor de la variable independiente le
corresponde uno o ningún valor de la variable dependiente, no le pueden corresponder dos o
más valores.

Aplicándolo a la programación, significa que podemos tener varios argumentos o


parámetros de entrada, pero solo tendremos un dato de salida. Y eso no es todo, en C una
010101000100010101010100

función pasa los argumentos por valor.


1010101010101111101010010

¿Qué quiere decir esto? Que cuando llamemos a la función y le pasemos el dato como
argumento, ésta copiará ese dato en su propia función sin alterar la variable original.
Veamos un ejemplo:
10101010100

#include <18F1320.h>
0101010000

#use delay(clock=8000000)
39
int mi_funcion(int argumento1, argumento2)
{
delay_cycles(1);
return (argumento1 + argumento2);
}

void main( )
{
int k,l,resultado;
k=5;
l=2;
resultado = mi_funcion(k,l);
delay_cycles(1);
}

Noten que cuando llamo a mi_funcion, se copia el contenido de k -> argumento1 y l ->
argumento2 , luego hace la suma y regresa un dato con el resultado de la suma. k y l se
quedan con el mismo valor anterior.

¿Y si queremos cambiar esas variables como se hace?


PUNTEROS:

Bueno seguro que alguien llegará y colocará a k y l como globales y entonces así se
puede modificar en cualquier lado.

Pero si la variable es local, dentro de main( ), no se puede modificar fuera de main( ) a


menos que usemos punteros. ¿y cómo se haría eso?

Simple: se haría pasando el argumento a la función como referencia, haciendo referencia a


la dirección, es decir lo que se pasará a la función es la dirección de k, l entonces allí si se
puede modificar a gusto. Un ejemplo:

int mi_funcion(int argumento1, argumento2, *la_k, *la_l)


{
delay_cycles(1);
*la_k=0xff;
*la_l=0xaf;
return (argumento1 + argumento2);
010101000100010101010100

}
1010101010101111101010010

void main()
{
int k,l,resultado;
k=5;
10101010100

l=2;
0101010000

resultado = mi_funcion(k, l, &k, &l);


delay_cycles(1);
40
}

Punteros en Arreglos(Arrays)
Como sabrán los arrays son arreglos de datos que van en direcciones consecutivas, es decir,
uno detrás del otro. Un ejemplo de ello:

Char cadena[7]={'T','o','d','o','P','i','c'};

#include <18F1320.h>
#use delay(clock=8000000)

Char cadena[7]={'T','o','d','o','P','i','c'};

Void main()
{
char c;
int t;

for(t=0;t<7;t++)
{
c=cadena[t];
}
delay_cycles(1);
}
PUNTEROS:
Se pueden usar punteros en el ejemplo anterior. Veamos como:
Declarando un puntero como char:
char c, *p;

Lo inicializamos (le damos la dirección del primer elemento del array):

p=&cadena[0];

Luego hacemos un barrido de direcciones para tomar el contenido de cada elemento y


guardarlo en la variable c.

for(t=0;t<7;t++)
{
c= *p + t;
}

Pero ese programa tiene 2 errores y no funcionará:


010101000100010101010100

El primer error es que según la precedencia del operador primero está el puntero y luego
1010101010101111101010010

viene la suma, y así estaríamos sumando direcciones que varían, la solución es usar *(p+i)

¿Y qué es eso de que varían? Pues que cuando se recorre el arrays con el puntero, este
debe ir sumando direcciones, pero direcciones de números constantes, es decir, si el tipo
de datos es 1 byte, entonces el puntero debe acumular números enteros de 1 byte en 1
41
10101010100

byte.
0101010000

Si el tipo de datos es long (entero largo) entonces el puntero debe ir sumando direcciones
de 2 bytes en 2 bytes. ¿Por qué es esto? Es que p quedará fijo (la dirección) y el truco está
en desplazar al puntero tantas posiciones sea el size del tipo de dato. Este sería el segundo
error y la solución es la misma: *(p+i) Vamos a cambiar ese ejemplo por números long
para que sea claro.

#include <18F1320.h>
#use delay(clock=8000000)

Long cadena[7]={1000,2000,3000,4000,5000,6000,7000};

void main()
{
long c, *p;
int t;
p=&cadena[0];

for(t=0;t<7;t++)
{
c= *p + t;
}
delay_cycles(1);
}
PUNTEROS:

010101000100010101010100

42
1010101010101111101010010
10101010100
0101010000

Fíjense que p queda inmutable, y lo que hace el programa es contenido[0] + t.

¡Grave error!

¿Cómo se soluciona?
PUNTEROS:

Arreglando el programa con *(p+t):

43
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

Con esto estamos garantizando que el puntero se moverá de 2 bytes en 2 bytes,

es decir: *(p+t) =

0x5 + 0x2 (desplazamiento de 2 byte)-> dame el contenido de la dirección 0x5

0x5 + 1x2 (desplazamiento de 2 byte)-> dame el contenido de la dirección 0x7

0x5 + 2x2 (desplazamiento de 2 byte)-> dame el contenido de la dirección 0x9

0x5 + 3x2 (desplazamiento de 2 byte)-> dame el contenido de la dirección 0xa

Noten que la suma se realiza no en intervalos de t sino en intervalos del ancho del tipo de
dato. Pero ¿esto no es lo mismo que se hizo en el código del inicio?

O sea que ¿ c = cadena[t]; es igual a c = *(p + t) cuando p = &cadena[0]; ?


PUNTEROS:

Pues SI, acabamos de ver un array al desnudo, como funciona en realidad, no es más
que un puntero escondido a nuestra vista. Solo que para hacer fácil la programación el
compilador lo acepta de esta manera.

Si p es un puntero -> p = cadena (para el primer índice del arreglo) es totalmente válido,
se acepta que cadena es un puntero constante, también se podría llamar un puntero nulo
(ya que no se ve y tampoco se puede modificar).

Ejemplos válidos:

cadena[0] = *cadena cadena[2] = *(cadena + 2) cadena = *(cadena + i)

Nota: el operador ( ) es el primero que atiende el compilador, antes que al resto.


Acomodando el código original, el que tenía la cadena de caracteres:

#include <18F1320.h>
#use delay(clock=8000000)

char cadena[7]={'T','o','d','o','P','i','c'};
void main( )
{
010101000100010101010100

char c, *p; int t; p=cadena;


44
1010101010101111101010010

for(t=0;t<7;t++)
{
c=*(p+t);
}
delay_cycles(1);
10101010100
0101010000

}
CIRCUITOS ÚTILES USANDO: SENSORES, AMPLIFICADORES, TRANSISTORES Y MÁS

Introducción

Existe una gran variedad de sensores en el mercado de los cuales puedes disponer, claro, que
antes habría que clasificarlos y aquí lo haremos brevemente. Los sensores pueden ser de dos
tipos, analógicos y digitales. Considerando que no conocieras los PIC, la circuitería igual funciona.

Los sensores digitales son aquellos que frente a un estímulo pueden cambiar de estado ya sea de
cero a uno o de uno a cero (hablando en términos de lógica digital) en este caso no existen
estados intermedios y los valores de tensión que se obtienen son únicamente dos, 5V y 0V (o
valores muy próximos).

ADC

Ahora bien, como los sensores comúnmente serán utilizados con circuitos lógicos, y más si se
trata de robótica en cuyo caso posiblemente incluyas un microcontrolador, habrá que ver cómo
trabajar con los sensores analógicos. Por suerte existen unos Circuitos integrados llamados
Convertidores Analógico/Digital (Convertidores A/D) que transforman la señal analógica en señal
digital, y por supuesto también están los Convertidores D/A, pero analicemos los primeros.
010101000100010101010100
1010101010101111101010010

Los Convertidores Analógico/Digital los puedes seleccionar entre otras cosas, de acuerdo a la
cantidad de bits de salida, por ejemplo. 45
Un Convertidor A/D de 3 bits dispone de una entrada analógica y 3 terminales de salida Digitales,
es decir que combinando las salidas puede tomar 8 posibles valores binarios según el nivel de
10101010100
0101010000

tensión en su entrada. Por aquello de 2n es decir que tendrás valores entre 000 y 111, veamos
cómo se corresponden estos valores con los niveles de tensión.

0.0 V 000

0.625 V 001

1.25 V 010

1.875 V 011

2.5 V 100

3.125 V 101

3.75 V 110

4.375 V 111
Qué pasó con los 5V? Bueno, es que el Convertidor necesita un nivel de tensión para utilizarlo
como referencia y en este caso utilicé los 5V, también podría ser 0V, o mejor aún ambos.

Aquí puedes ver una imagen representativa de un Convertidor A/D, en ella se indican en la salida
dos términos muy utilizados MSB y LSB.

MSB es el valor binario más significativo y LSB es el menos significativo (en nuestro ejemplo, 111 y
000 respectivamente).

Lo visto hasta el momento te puede servir si en caso deseas decodificar una señal analógica
y utilizarla como si fuera digital, por ejemplo en el caso de una fotocelda, esta varía su
resistencia según la iluminación que recibe, por lo tanto es un sensor de tipo analógico. Un
pulsador tiene dos estados, activado o no, por lo tanto es de tipo digital.

LDR
010101000100010101010100
1010101010101111101010010

La LDR es quizás una de las más utilizadas en los sistemas sensores para robótica compiten
a gran escala con los fototransistores.

Para comenzar debes saber que las LDR's son resistores que varían su valor de acuerdo a la
46
intensidad de la luz, razón por la cual se trata de un sensor analógico, es decir que siempre
toma valores distintos, no podrías tomar un valor lógico 1 o 0 como en lógica digital, pero lo
10101010100
0101010000

podemos arreglar con un par de trucos.

La fotocelda en total oscuridad puede llegar a tomar valores de 1MΩ, y a plena iluminación a
unos pocos KΩ quizá menos. Lo que se puede hacer, es un arreglo entre la fotocelda al polo
(-) y una resistencia fija al polo (+), de esa manera el punto de unión entre estos dos
componentes podrá tomar dos valores según la variación de la LDR, señal que se puede
utilizar como salida del sensor, este tipo de circuitos es conocido como divisor de tensión.

El tema es que la señal aun sigue siendo analógica, y para convertirla en señal digital
podríamos utilizar un disparador Schmitt como el CD40106 que tiene 6 disparadores
inversores en su interior, y nos quedaría averiguar las características de la fotocelda y la
tensión de disparo del Schmitt y así seleccionar el nivel de tensión al que quieres trabajar,
lo cual podrías hacerlo con un potenciómetro en lugar de la resistencia de 10k. E
implementarlo con microcontrolador sería aun más sencillo y barato.
Con el potenciómetro P1 puedes seleccionar la sensibilidad a tu gusto, bueno, con alguna
que otra limitación. Si deseas realizar los cálculos para averiguar la tensión en el punto
medio, lo puedes hacer del siguiente modo:

Y así sabrás el nivel de tensión en distintas situaciones.


Esta no es la única forma, también puede darse el caso opuesto, observa.
010101000100010101010100
1010101010101111101010010

47

Todo depende de la forma en que deseas trabajar, en el caso anterior la señal lógica
10101010100
0101010000

obtenida a plena iluminación es "0", mientras que en esta última es "1".

Ahora bien, Teniendo un poco de conocimiento de compuertas lógicas también puedes


adoptar este circuito.
SWITCHES

Aunque parezca mentira, los switch son muy utilizados como dispositivos-sensores, por
ejemplo, si deseas que un modelo realice una determinada acción cuando choque con algún
obstáculo recurres a ellos, al margen del tipo de interruptor que quieras utilizar, el circuito
básico será siempre el mismo, un divisor de tensión:

A pesar de que los interruptores son sensores de tipo lógico (por trabajar con niveles 0 y 1)
es mejor acondicionar los niveles de tensión para ellos, es por eso que se incluye el
CD40106. Debemos aclarar que el circuito anterior presenta un pequeño inconveniente, y es
que al activarse se pueden producir rebotes eléctricos, es decir, cuando crees haber enviado
un 1 lógico en realidad enviaste varios, es como si se los hubiera presionado varias veces,
tiene solución.
010101000100010101010100
1010101010101111101010010

48
10101010100
0101010000

Agregándole un pequeño capacitor, como los de 0.1uf puedes evitar esos rebotes. Claro
que según el tipo de señal que tu modelo necesite, ya sea 0 o 1 te servirá el circuito
anterior o este:

En fin, el tema es que hay muchos modelos de este interruptor, pero los más utilizados en
microbótica son los bumpers, ya sean comerciales, los que nosotros implementemos, el
tiempo necesario para evitar rebotes con swtich utilizando un pic, son 10ms
aproximadamente. Entonces, si deseas no utilizar esta circuitería exterior basta con poner un
retardo de 10ms cuando preguntas por una entrada lógica, esperas ese tiempo y vuelves a
preguntar si continúa presionado el botón y aseguras que no fue ruido.
SWITCHES:

Veamos algunos ejemplos:

Luego los aplicaremos a algún modelo específico, esto sólo fue para que tuviéramos una idea.
010101000100010101010100
1010101010101111101010010

Los dos primeros son comerciales, y los dos de abajo son arreglos para implementarlos como
sensores tipo bigote de gato.

IC-Datos - Circuito Integrado NE555


10101010100
0101010000

49
Es uno de los Circuitos Integrados más famosos, de los más utilizados. Según el tipo de fabricante
recibe una designación distinta tal como TLC555, LMC555, ua555, NE555C, MC1455, NE555,
LM555, etc. Aunque se lo conoce como "el 555" y ya todos saben de qué se está hablando.

Respecto al formato o encapsulado, puede ser circular metálico, hasta los SMD, pasando por los
DIL de 8 y 14 patillas.

Existen versiones de bajo consumo con el mismo patillaje y versiones dobles, es decir que
contienen 2 circuitos iguales en su interior, que comparten los terminales de alimentación y se
conocen con la designación genérica de 556, observa la siguiente imagen:
IC-Datos - Circuito Integrado NE555

Utilización:

Este circuito es un "Timer de precisión", en sus orígenes se presentó como un circuito de


retardos de precisión, pero pronto se le encontraron otra aplicaciones tales como
osciladores astables, generadores de rampas, temporizadores secuenciales, consiguiéndose
unas temporizaciones muy estables frente a variaciones de tensión de alimentación y de
temperatura.

Características generales:

El circuito puede alimentarse con tensión continua comprendida entre 5 y 15 volts, aunque
hay versiones que admiten tensiones de alimentación hasta 2 V, pero no son de uso común.
Si se alimenta a 5V es compatible con la familia TTL.

La corriente de salida máxima puede ser de hasta 200mA, muy elevada para un circuito
integrado, permitiendo excitar directamente relés y otros circuitos de alto consumo sin
necesidad de utilizar componentes adicionales. La estabilidad en frecuencia es de 0,005%
por °C, considerando temperatura ambiente.

Necesita un número mínimo de componentes exteriores, la frecuencia de oscilación se


controla con dos resistencias y un condensador. Cuando funciona como monoestable el
retardo se determina con los valores de una resistencia y de un condensador.
010101000100010101010100
1010101010101111101010010

Diagrama de Bloques Interno:


50
El funcionamiento y las posibilidades de este circuito se pueden comprender estudiando el
diagrama de bloques. Básicamente se compone de dos amplificadores operacionales
montados como comparadores, un circuito biestable del tipo RS del que se utiliza su salida
10101010100

negada, un buffer de salida inversor que puede entregar o absorber una corriente de 200mA
0101010000

y un transistor que se utiliza para descarga del condensador de temporización:

Una red de tres resistencias iguales fija los niveles de referencia en la entrada inversora del
primer operacional, y en la no inversora del segundo operacional, a 2/3 y 1/3
aproximadamente de la tensión de alimentación.

Cuando la tensión en el terminal umbral (THRESHOLD) supera los 2/3 de la tensión de


alimentación, su salida pasa a nivel lógico "1", que se aplica a la entrada R del biestable, con
lo cual su salida negada, la utilizada en este caso, pasa a nivel "1", saturando el transistor y
comenzando la descarga del condensador, al mismo tiempo, la salida del 555 pasa a nivel
"0".
IC-Datos - Circuito Integrado NE555

Pasemos ahora al otro amplificador operacional, si la tensión aplicada a la entrada


inversora, terminal de disparo (TRIGGER), desciende por debajo de 1/3 de la tensión de
alimentación, la salida de este operacional pasa a nivel alto, que se aplica al terminal de
entrada S del biestable RS, con lo que su salida se pone a nivel bajo, el transistor de descarga
deja de conducir y la salida del 555 pasa a nivel lógico alto.

La gama de aplicaciones del circuito se incrementa, pues se dispone de una terminal de


reset, activo a nivel bajo, que se puede utilizar para poner a nivel bajo la salida del 555 en
cualquier momento.

Algunas de sus aplicaciones:

Circuito Monoestable:

La salida del circuito es inicialmente cero, el transistor está saturado y no permite la carga
del condensador C1. Pero al pulsar SW1 se aplica una tensión baja en el terminal de disparo
TRIGGER, que hace que el biestable RS cambie y en la salida aparezca un nivel alto. El
transistor deja de conducir y permite que el condensador C1 se cargue a través de la
010101000100010101010100

resistencia R1. Cuando la tensión en el condensador supera los 2/3 de la tensión de


1010101010101111101010010

alimentación, el biestable cambia de estado y la salida vuelve a nivel cero.

R2 está entre 1kΩ y 3,3 MΩ, el valor mínimo de C1 es de 500pf.


51
10101010100
0101010000
IC-Datos - Circuito Integrado NE555

Circuito Astable:

Cuando se conecta la alimentación, el condensador está descargando y la salida del 555 pasa
010101000100010101010100

a nivel alto hasta que el condensador, que se va cargando, alcanza los 2/3 de la tensión de
1010101010101111101010010

alimentación, con esto la salida del biestable RS pasa a nivel "1", y la salida del 555 a cero y
el condensador C1 comienza a descargarse a través de la resistencia RB. Cuando la tensión
en el condensador C1 llega a 1/3 de la alimentación, comienza de nuevo a cargarse, y así
sucesivamente mientras se mantenga la alimentación.
10101010100

RA toma valores entre 1kΩ y 10MΩ, RB<RA


0101010000

52
Circuito astable con onda simétrica:

En este circuito astable se muestra cómo puede obtenerse una onda simétrica; el modo de
hacerlo es que el condensador tarde el mismo tiempo en cargarse que en descargarse, los
caminos de carga y descarga deben ser iguales y se separan con dos diodos. El
condensador C2 evita fluctuaciones de tensión en la entrada de control.
IC-Datos - Circuito Integrado NE555

Terminal de Reset:

El terminal de reset puede conectarse directamente al positivo o bien mantener el nivel alto
por medio de una resistencia, por ejemplo de 2.2kΩ. Al actuar sobre el pulsador, la salida del
555 pasa a nivel bajo directamente. Es como poner el integrado en un estado de reposo.
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

53

Una red de tres resistencias iguales fija los niveles de referencia en la entrada inversora
del primer operacional, y en la no inversora del segundo operacional, a 2/3 y 1/3
respectivamente de la tensión de alimentación.

Cuando la tensión en el terminal umbral (THRESHOLD) supera los 2/3 de la tensión de


alimentación, su salida pasa a nivel lógico "1", que se aplica a la entrada R del biestable,
con lo cual su salida negada, la utilizada en
Este caso, pasa a nivel "1", saturando el transistor y comenzando la descarga del
condensador, al mismo tiempo, la salida del 555 pasa a nivel "0".

Pasemos ahora al otro amplificador operacional, si la tensión aplicada a la entrada


inversora, terminal de disparo (TRIGGER), desciende por debajo de 1/3 de la tensión de
alimentación, la salida de este operacional pasa a nivel alto, que se aplica al terminal de
entrada S del biestable RS, con lo que su salida se pone a nivel bajo, el transistor de
descarga deja de conducir y la salida del 555 pasa a nivel lógico alto.
La gama de aplicaciones del circuito se incrementa, pues se dispone de una terminal
de reset, activo a nivel bajo, que se puede utilizar para poner a nivel bajo la salida del
555 en cualquier momento.

Algunas de sus aplicaciones


010101000100010101010100
1010101010101111101010010

Circuito monoestable:

La salida del circuito es inicialmente cero, el transistor está saturado y no permite la carga
del condensador C1. Pero al pulsar SW1 se aplica una tensión baja en el terminal de disparo
TRIGGER, que hace que el biestable RS cambie y en la salida aparezca un nivel alto. El
10101010100
0101010000

transistor deja de conducir y permite que el condensador C1 se cargue a través de la


resistencia R1. Cuando la tensión en el condensador supera los 2/3 de la tensión de
alimentación, el biestable cambia de estado y la salida vuelve a nivel cero.

R2 está entre 1kΩ y 3,3 MΩ, el valor mínimo de C1 es de 500pf.

54

Circuito astable:

Cuando se conecta la alimentación, el condensador está descargando y la salida del 555 pasa
a nivel alto hasta que el condensador, que se va cargando, alcanza los 2/3 de la tensión de
alimentación, con esto l a salida del biestable RS pasa a nivel "1", y la salida del 555 a cero y
el condensador C1 comienza a descargarse a través de la resistencia RB. Cuando la tensión
en el condensador C1 llega a 1/3 de la alimentación, comienza de nuevo a cargarse, y así
sucesivamente mientras se mantenga la alimentación.

RA toma valores entre 1kΩ y 10MΩ, RB<RA


Circuito astable con onda simétrica:

En este circuito astable se muestra cómo puede obtenerse una onda simétrica; el modo
de hacerlo es que el condensador tarde el mismo tiempo en cargarse que en
descargarse, los caminos de carga y descarga deben ser iguales y se separan con dos
diodos. El condensador C2 evita fluctuaciones de tensión en la entrada de control.
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

Terminal de Reset:

El terminal de reset puede conectarse directamente al positivo o bien mantener el


nivel alto por medio de una resistencia, por ejemplo de 2.2kΩ. Al actuar sobre el
pulsador, la salida del 555 pasa a nivel bajo directamente. Es como poner el 55
integrado en un estado de reposo.
Modulación del ancho de pulso PWM:

Aplicando una señal de nivel variable a la entrada de CONTROL el pulso de salida


010101000100010101010100 aumenta de ancho al aumentar el nivel de esa tensión.
1010101010101111101010010

Modulación del retardo de pulso:

Aquí el pulso de salida aparece con mayor o menor retardo según aumente o disminuya
10101010100

la tensión aplicada al terminal de control.


0101010000

56
IC-Datos - Circuito Integrado CD4013

Descripción:

Este integrado es un doble flip-flop tipo D, en la figura siguiente se puede observar la


disposición de terminales y la tabla de verdad correspondiente, numerada de 1 a 6 para
interpretar con mayor claridad el análisis de la misma.
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

•1 : En este renglón las entradas están todas en "0"; la transición en sentido positivo del pulso
de reloj, no tiene efecto en las salidas, por lo que la salida Q se mantiene en 0 y -Q en 1.

•2 : Con las entradas Set y Reset a potencial 0 y el dato a 1, si en la entrada reloj se presente
un pulso de transición positiva el Flip-Flop cambia de estado y se mantiene en él, aun después
de desaparecer dicho pulso. 57
•3 : Si el pulso de reloj es de transición negativa, aunque las entradas Set y Reset estén a 0, no
conmutará independientemente del nivel de la entrada Dato, que puede ser 1 o 0, ya que sólo
lo hace en la transición positiva.

•4 : En este caso x en la entrada de Reloj y Dato significan que es irrelevante el nivel que
tengan ya que al estar a 1 la entrada Reset, el Flip-Flop no producirá ningún cambio.

•5 : No tiene importancia la polaridad de las entradas de Reloj y Dato, ya que el cambio de


estado se produce llevando Set a 1, y se mantendrá en él aunque esta entrada vuelva a 0. Sólo
se volverá al estado anterior (reposo) llevando momentáneamente la entrada Reset a 1.

•6 : Esta es una situación en la cual continúa funcionando como R-S, pero con la
particularidad de ser seguidor de la señal presente en la entrada Set. Sigue sin tener
importancia los niveles de Reloj y Dato. Al llevar el Set a 1, la salida -Q cambia también a 1,
pero no lo hace la Salida Q, con lo que no se obtienen los estados complementarios; la salida Q
se mantendrá a 1 todo el tiempo que esté a 1 la entrada Set, en cuanto esta entrada vuelva a 0
la salida Q también volverá a 0, esto es así porque la entrada Reset está a nivel 1, y como ya
sabemos con positivo en este terminal el Flip-Flop se mantiene en estado de reposo.
Circuitos Prácticos:

A continuación se describe un circuito general que verifica el funcionamiento del Flip-Flop


según su tabla de verdad:
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

Los cables de prueba son los que estas con líneas de puntos. Los pulsos de la entrada de
reloj (CL) se simulan mediante la conexión y desconexión del cable de prueba del terminal
3, (Tierra (0) - Flanco de descenso, Vcc (1)- flanco de ascenso).

El LED solo brillará ante la conmutación o puesta a uno del Flip-Flop, puedes
experimentar reseteando el circuito (con un 1 en Reset). Un detalle a tener en cuenta es
que se pueden producir ruidos eléctricos (rebotes) al conectar los cables de prueba, pero
puedes utilizar un capacitor para solucionar este inconveniente.
58
Contador Divisor por dos y/o Llave Oscilante I :

En este caso a modo de ejemplo se estableció 10khz, con lo que se obtiene a la salida Q :
5khz.La llave oscilante o vaivén, cambia de estado con cada pulso de flanco ascendente en
la entrada Reloj, en el primero se pone a uno, en el segundo se pone a cero.
Contador Divisor por dos y/o Llave Oscilante II :

Se obtiene el mismo efecto que en el caso anterior pero con dos señales de control una para
010101000100010101010100 la puesta a 1 y otra de las mismas características para la puesta a 0.

1010101010101111101010010

En "A" se trabaja para la puesta a 1 con la entrada de Reloj, y para la puesta a 0 con
Reset. Se requiere que la entrada Dato se encuentre en 1.
10101010100
0101010000

En "B" se utiliza como un clásico FF R-S, es decir la entrada Set para la puesta a 1 y la
entrada Reset para la puesta a 0. Las entradas de Reloj y Dato, en este caso son
irrelevantes, es como si no existieran, por lo tanto no tiene importancia el nivel en
ellas, en este caso se conectaron a masa para no dejarlas al aire, si estarían a VCC daría
igual.

Flip-Flop Conectado como Monoestable:


59
Para realizar un monoestable con este integrado solo se requiere de una Resistencia, un
Diodo y un Capacitor. También en este caso se puede operar desde las entradas Reloj o
Set y se obtendrán los mismos resultados.

En "A" es operado con la entrada Reloj.


En "B" es operado desde la entrada Set.

En ambos casos un pulso de transición positiva produce el cambio o puesta a 1, con


lo cual la salida Q pasa al estado alto. A partir de este instante, el capacitor
conectado en Reset comienza a cargarse. Cuando la magnitud de la tensión de
carga supera el estado intermedio, actúa la entrada Reset y produce un nuevo
cambio que lo pone en estado de reposo nuevamente. La duración del pulso de
salida lo determina la red R-C. El diodo se conecta para sacarlo rápidamente del
estado intermedio una vez producida la vuelta a 0. Durante la carga del capacitor
se encuentra en oposición a la corriente, pero una vez que la salida Q vuelve a 0,
queda en directo para la carga obtenida y lleva este potencial a 0 casi
instantáneamente.
IC-Datos - Circuito Integrado CD4017

Descripción:

Se trata de un contador/divisor o decodificador con 10 salidas. Estructuralmente está


formado por un contador Johnson de 5 etapas que puede dividir o contar por cualquier
valor entre 2 y 9, con recursos para continuar o detenerse al final del ciclo.

El Diagrama de funciones:
010101000100010101010100
1010101010101111101010010

Para comprender mejor su funcionamiento lo haremos utilizando el diagrama de funciones.

60
10101010100
0101010000

Con las entradas "Habil. Reloj" y "Reset" a tierra, el contador avanza una etapa a cada
transición positiva de la señal de entrada (Reloj). Partiendo entonces de la situación
inicial en que "S0" se encuentra a nivel alto y todas las demás a nivel bajo. Con la llegada
del primer pulso de entrada tenemos la primera transición. "S0" pasa a nivel bajo y "S1" a
nivel alto, todas las demás permanecen en cero.

Con el segundo pulso, "S1" pasa a nivel bajo y "S2" a nivel alto, y así sucesivamente hasta la
última.
Las otras terminales:

"Habil. Reloj" si está a tierra, hará que se inicie un nuevo ciclo. Si está a VDD se
consigue solo un ciclo de funcionamiento.

"Carry-Out" Este terminal proporciona un ciclo completo a cada 10 pulsos de entrada,


pudiendo usarse para excitar otro 4017 para división sucesiva de frecuencia o
recuento por un número superior a 10.
010101000100010101010100
1010101010101111101010010

"Reset" Si se le aplica un nivel alto, lleva ese nivel al terminal "S0", volviendo a iniciar el
recuento. Eso significa que si conectamos este terminal a cualquier salida, cuando ésta se
lleve a nivel alto se iniciará un nuevo ciclo. Es decir que si conectamos "S4" a la entrada
"Reset" tendremos un recuento sólo hasta 4.
10101010100
0101010000

61
Circuitos de prueba:

Las salidas de este integrado proporcionan corrientes lo suficientemente intensas como para
excitar LED's y en aplicaciones de mayor potencia, transistores comunes.

Los leds pueden conectarse de dos maneras:


• En modo positivo : Solo el LED que tiene el nivel alto permanece encendido.
• En modo negativo: Solo el LED que tiene el nivel alto permanece apagado.

También puedes realizar una secuencia completa con todas las salidas, algo así como lo que se
ve en la siguiente imagen: 62
010101000100010101010100
1010101010101111101010010
10101010100
0101010000
IC-Datos - Circuito Integrado CD4029

Descripción:

El CD4029 es un contador sincrónico "Up-Down", Contador Progresivo/Regresivo,


Preajustable, Binario/Década. Es un contador de 4 bits que opera como década (0000 a
1001), tanto en el sentido creciente como decreciente.

Además de la entrada normal de clock (información de tipo serie) posee 4 entradas


disponibles para información paralela. Esas entradas permiten que el contador sea
cargado con un determinado dato (número binario) e inicie a partir de ahí la cuenta.
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

En esta imagen tenemos la disposición de los pines del CD4029, y a continuación, una
relación de todas sus entradas y salidas, con sus respectivas funciones.

Habilitación Preajuste (Pin 1): Entrada para lectura paralela. Cuando aplicamos un
nivel lógico "1", en esta entrada, el contador se carga con la información presente en las 63
entradas paralelas; si no es utilizada debe ser mantenida en "0".

J1, J2, J3, J4 (Pin's 3, 4, 12 y 13 respectivamente): Entradas paralelas de datos. Estas


entradas actúan directamente en las salidas del contador, independientemente de
cualesquiera otras condiciones, una vez que representan los "presets" de cada flip-flop del
contador. En este circuito el digito más significativo (con peso 8) es alterado a través de la
entrada J4 (pin3). Si quisieras por ejemplo, que el contador vaya al número 6, debes
aplicar a las entradas paralelas el número correspondiente en binario, o sea 0110 (J4=0,
J3=1, J2=1, J1=0).

Q1, Q2, Q3 y Q4 (Pin's 6, 11, 14 y 2 respectivamente): Salidas del contador. La salida


Q4 representa el dígito más significativo del número en binario, por lo tanto Q1 es el
menos significativo.

Reloj (Pin 15): Entrada de Reloj. A cada transición ascendente (de "0" a "1") de la
señal de Reloj el contador cambia de estado.

Avance/Retroceso (Pin 10): Entrada de control para cuentas crecientes o decrecientes.


Un nivel lógico "1" hace que el circuito realice la cuenta creciente, con un nivel "0" la
cuenta será decreciente.

Binario/Década (Pin 9): Entrada de control para la cuenta en binario o


década/decena. En nivel lógico "1" el circuito cuenta en binario (de 0000 hasta 1111)
y en nivel "0" el contador se comporta como una década (contando de 0000 hasta
1001 o sea de 0 a 9).
Entrada Carry (Pin 5): Habilitación del contador Con "1" la cuenta se paraliza. Con
"0" el contador funciona normalmente.

Salida Carry (Pin 7): Salida de término de cuenta. Representa la salida "Carry" o "va 1"
del contador. El nivel lógico de esta salida varía de "1" a "0" toda vez que el contador
alcanza el número máximo de la cuenta, cuando está conectado como contador creciente,
o cuando alcanza el menor número de la cuenta, al funcionar como contador decreciente.

En las siguientes imágenes se describe el diagrama de funcionamiento en Modo Década y


en Modo Binario:
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

64
Tabla de verdad:

En la siguiente tabla de verdad se muestran los posibles modos de operación y sus


correspondientes entradas de control. En esta tabla los estados representados por "x" son
irrelevantes, o sea, pueden ser "1" o "0", indistintamente.
010101000100010101010100
1010101010101111101010010

Circuito de Prueba:

Para verificar la tabla de verdad citada anteriormente puedes utilizar el siguiente


10101010100

circuito de ejemplo, las resistencias son todas de 220Ω.


0101010000

65
IC-Datos - Circuito Integrado CD4066

Descripción:

El integrado en sí, es un cuádruple interruptor Bilateral, diseñado para la transmisión o


multiplexado de señales digitales o analógicas, puedes considerarlo como un
interruptor de conmutación, cada uno de los interruptores dispone de un pin de control
y dos pines de entrada/salida, una representación de lo que se vería por dentro sería
algo así:

Como se observa, también se incluye otro integrado en el mismo esquema ya que son
compatibles pin a pin, es decir puedes sustituirlo sin ningún inconveniente.

Que sea bidireccional significa que cualquiera de los dos pines de cada interruptor
exceptuando al pin de control, puede hacer de entrada mientras el otro es de salida.
010101000100010101010100

66
1010101010101111101010010

Cada interruptor entra en conducción cuando se presenta un nivel alto (superior al 70%
de VCC), y en corte cuando se envíe un nivel bajo (inferior al 30% de VCC) por el mismo
pin de control.

Veamos ahora un par de aplicaciones en las cuales puedes utilizarlo.


10101010100

Selector de señales analógicas:


0101010000

En este caso puedes utilizar señales digitales en los pines de control para seleccionar
una de las cuatro señales analógicas presentes en los canales A, B, C o D y enviarlo como
señal de salida, observa el siguiente esquema:
Convertidor Digital/ Analógico

Descripción:

Otra de las aplicaciones, es implementarlo como un Convertidor D/A (Digital/Analógico)


de 4 bit's, en donde las distintas combinaciones de los terminales de control entregan en la
salida del circuito un nivel de tensión que corresponde particularmente a cada
combinación, veamos cómo sería la forma de conexión:
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

Puede ser utilizarlo como un potenciómetro pero controlado digitalmente.

67
IC-Datos - Circuito Integrado CD4511

Descripción:

Se trata de un excitador/decodificador/cerrojo BCD a 7 Segmentos. El circuito provee las


funciones de un cerrojo (latch) de almacenamiento de 4 bit, un decodificador BCD 8421 a 7
segmentos, y gran capacidad de excitación de salida:

Las entradas de prueba (LT), borrado (BI) y habilitación de Cerrojo (LE), se usan para probar
el visualizador, para apagar o modular por pulsos el visualizador, y para almacenar un código
BCD, respectivamente. Se puede usar con indicadores o Diodos LED.

68
010101000100010101010100
1010101010101111101010010
10101010100
0101010000
NOTA:

X = Sin Importancia
* = Depende del código BCD aplicado durante la transición de 0 a 1 de LE

La segunda línea borra la salida, sin importar lo que hay en las entradas, el display se
mantendrá apagado, esto es por BI=0 y LT=1.

Aquello que ves en azul, es el código para mostrar los respectivos valores en el display, los
6 siguientes son ignorados.

El último depende del valor en que se encontraban las entradas en el momento de pasar de
0 a 1 en e l pin LE. Aquí tienes una imagen de lo que verías en el Display para cada valor de
salida del integrado.

También la disposición de los segmentos y su identificación, nota que aquí no figura el punto
en el segmento. Estas son otras características con las que cuenta este integrado: 69
010101000100010101010100

• Baja disipación de potencia del circuito lógico


1010101010101111101010010

• Salidas que suministran alta corriente (hasta 25 ma)


• Almacenamiento de código en cerrojo
• Entrada de borrado
• Borrado del indicador en todas las condiciones ilegales de entrada
• Facilidad de tiempo compartido (multiplexado)
10101010100
0101010000

• Equivalente al Motorola MC14511

Para hacer pruebas con este integrado, puedes guiarte con el siguiente circuito:
Esto en caso de que el Display sea de Cátodo Común, de lo contrario necesitarás
inversores, puesto que el código será totalmente distinto, para ello, simplemente
invirtiendo el valor de la salida, solucionas el problema.

En este circuito también tienes la posibilidad de probar que es lo que ocurre con el pin LE,
si lo dejas como está en la imagen, trabajará normalmente, si lo cambias, se habilitará el
cerrojo y retendrá el último valor ingresado, pero, deberías probarlo, y así comprenderás
que es eso del cerrojo, ya que no es el único integrado que lo tiene, hay muchos más.

Creo que son datos suficientes para comprender como puedes implementarlos en tus
proyectos.

70
010101000100010101010100
1010101010101111101010010
10101010100
0101010000
TRANSISTORES

Transistores en circuitos de conmutación

Muchas veces se presenta la difícil situación de manejar corrientes o tensiones más


grandes que las que entrega un circuito digital, y entonces nos disponemos al uso de
transistores, el tema es hacer que estos trabajen en modo corte y saturación sin estados
intermedios, es decir que cambien su estado de plena conducción a un profundo corte.

Los transistores a utilizar en estos casos deben tener la suficiente ganancia para que la
onda cuadrada, aplicada en su entrada (Base), no sufra ninguna deformación en la
salida (Colector o Emisor), o sea que conserve perfecta simetría y sus flancos
ascendente y descendente se mantengan bien verticales.

La corriente máxima que puede circular de colector a emisor está limitada por la
tensión de polarización de Base y el Resistor o la carga del colector.

Polarización de un transistor NPN como Emisor Común

En este caso el emisor está conectado a tierra, se dice que este terminal es común a la
señal de base y de colector. El utilizado en este caso un BC547 y estos son algunos de
sus datos:

• Tensión Base-Colector (VCBO) = 50 V


• Corriente de Colector (Ic) = 100ma = 0,1 A
71
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

Cuando la base de Q1 se polariza positivamente, éste conduce la máxima

corriente, que le permite Rc. Rc es la resistencia de carga, que bien podría ser un

LED, un relé, etc.

Ic = E/R = 12V / 2200

= 0,0054 = 5,4 ma Ib =

E/R = 12V / 10000 =

0,0012 = 1,2 ma

Es decir la corriente total Colector-Emisor es 6,6ma.


Conexión como seguidor emisivo:

En esta situación se toma la señal de salida desde el Emisor donde se encuentra la Resistencia
de carga, observa que este esquema comparado al anterior tiene la misma fase de salida que
la de entrada.

También hay casos en que necesitas que el transistor esté conduciendo permanentemente
(estado de saturación) y que pase al corte ante la presencia de un pulso eléctrico, esto sería lo
inverso de lo visto anteriormente, para lograr esto, los circuitos anteriores quedan como
están y sólo se reemplazan los transistores por los complementarios, o sea donde hay un NPN
se conecta un PNP.

Cuando la señal es negativa


010101000100010101010100
1010101010101111101010010

En ocasiones se da el caso en que las señales lógicas recibidas son negativas o de nivel bajo,
para entonces se puede utilizar un transistor PNP, por ejemplo: el BC557, que es
complementario del BC547, para conseguir los mismos resultados. En la siguiente figura se
representa esta condición, es decir, un acoplamiento con transistor PNP.

72
10101010100
0101010000
Análisis para la conexión de un RELE

El diodo en paralelo con la bobina del relé cumple la función de absorber las tensiones
que se generan en todos los circuitos inductivos.

Si la bobina del relé tiene 50Ω de resistencia y funciona a 12 V, puedes calcular el


consumo de corriente que tiene el relé, para así saber que transistor utilizar:

Ic = E/R = 12V / 50 = 0,24 = 240 mA

Con este resultado no se puede utilizar el BC547, cuya corriente máxima es de 100ma,
pero si lo puede hacer un BC337, es conveniente no superar el 50% de la corriente que
entregan los transistores.

Ahora bien, si la señal que se aplique a la base del transistor tiene la suficiente amplitud
(tensión) y suficiente intensidad (amperaje), no habrá dificultad y la corriente de base
también será suficiente para saturar el transistor, que conmutará en forma efectiva el
relé.
73
010101000100010101010100
1010101010101111101010010

Montajes Darlington:

En esta conexión se utiliza un BC337 (NPN) el cual si soporta los 240mA que se
necesitaba anteriormente, pero además un transistor de baja potencia como el BC547
(NPN).
10101010100
0101010000

En este tipo de montajes, hay que lograr previamente una ganancia en corriente y esta
corriente aplicarla a la base del BC337, esta es la finalidad del montaje en Darlington.
En este circuito el Transistor BC337 es el que recibe la carga del relé y el BC547 solamente
soporta la corriente de base del BC337, además la ganancia se multiplica sin cargar la salida
del componente que entrega la señal, ya que ahora la corriente que drena el 547 es tomada de
la misma fuente y aplicada a la base del 337. De este modo la resistencia de base del 547
puede ser elevada ya que necesitamos una corriente mucho menor en la misma.

En el siguiente gráfico se describe cómo lograr la conmutación de un relé con un transistor de


salida NPN, incluso utilizando tensiones diferentes.

En esta situación como vemos es necesario agregar un transistor de baja potencia, ya que
la corriente que debe manejar es la de base.

Con la entrada en "1": El BC547 conduce y envía a tierra la base del BC337 de este
modo se mantiene el corte. 74
010101000100010101010100

Con la entrada en "0": El 547 pasa al corte y su colector queda "abierto", ahora sí se
1010101010101111101010010

polariza la base del 337 y conmutando el relé.

Otro caso de conmutación con diferentes tensiones.


10101010100

Suponiendo que el consumo de un relé sea 200ma. Para los cálculos de polarización
0101010000

siempre se debe tomar el menor Beta-B-β-(hfe) que indiquen los manuales de los
transistores, o sea que si dice 100 a 300, tomamos 100. Veamos que corriente de base se
necesita de acuerdo a estos datos:

Ib = Ic / Hfe = 200ma / 100 = 2ma

Donde:

• Ib = Intensidad de Base (en ma)

• Ic = Intensidad de Colector

• Hfe = Ganancia
Ahora veamos qué valor de resistencia de base es necesario para lograr 2ma con una
fuente de 5V, que es la salida que entrega un microcontrolador o un c.i. Como en el
ejemplo:

R = E / I = 5V / 0,002A = 2500 ohm (un valor normalizado es 2.2kΩ)


010101000100010101010100
1010101010101111101010010

Muchas veces en los proyectos que encontramos en internet vemos la salida de nuestro
microcontrolador a una resistencia de 2.2kΩ y a transistor, esta es la explicación y ejemplo
de aplicación del cual se basan.
10101010100
0101010000

75
Proyectos comunes:

Fuente de Tensión Regulada a 5V


Como las comúnmente llamadas pilas o baterías suelen agotarse en muy corto tiempo es
bueno armarse con una fuente de alimentación, incluso podemos usar fuentes de
computadoras pues hay terminales de 5V, o de cualquier otro equipo que luego
desarmamos, eliminadores de baterías e incluso podemos utilizar cargadores de teléfonos
celulares, ya que la mayoría trabaja alrededor de los 5 V y con corrientes desde 300ma y
hasta 1A .
¿Qué necesitas para comenzar?
En primer lugar un transformador, que lo puedes extraer de cualquier aparato en desuso, 4
diodos en buenas condiciones, unos cuantos capacitores, y lo que seguramente no
encontrarás por allí es un regulador de tensión, estos últimos mantienen un nivel de tensión
estable a 5V, 9V, 12V, etc.
Existen los reguladores de tensión positivos y los negativos, se distinguen fácilmente por su
nombre. Los Primeros corresponden a la serie 78XX y los negativos a la serie 79XX, donde
XX es el voltaje a regular.
Veamos un ejemplo; si deseas regular la tensión a +5V utilizarás un 7805, si deseas hacerlo a
+9V acudirás a un 7809, y si deseas +12V, un 7812.

76
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

Para comprender mejor el proceso de rectificación de la fuente, lo vamos a dividir en varias


etapas.

Primera Etapa - Reducción de Tensión:

Nuestra red de suministro en México, entrega aproximadamente 110-127 V de corriente


alterna, los cuales reducimos a 12V por ejemplo de la misma tensión a través de un
transformador.

Nota la diferencia de las dimensiones de la onda a la entrada comparada con la salida.


Recordemos que la corriente alterna en un momento es positiva y en otro negativa, por lo
tanto el nodo A en un instante es (+) y B (-) en otro instante la polaridad en estos extremos
se invierte.
Segunda Etapa - Puente Rectificador:

Vamos a considerar dos momentos;

Primero, con A (+) y B (-), únicamente se polarizan el diodo D3 haciendo el nodo C (+),
y el diodo D2, haciendo el nodo D (-), D1 pone una barrera y D4 no participa ya que el
nodo B es (-).

Segundo, con A (-) y B (+), únicamente se polarizan D4 haciendo el nodo C (+), y D1,
haciendo el nodo D (-), D2 pone una barrera y D3 no participa ya que el nodo A es (-).

Logramos obtener en el nodo C constantes pulsos positivos y en D una constante polaridad


negativa, así eliminamos la corriente alterna, o al menos una parte de ella.

Tercera Etapa - Los Filtros:


010101000100010101010100
1010101010101111101010010

Acabo de mencionar la palabra pulsos y no necesitamos corriente pulsante sino continua, o


sea que deberemos levantar esos descensos de la curva, y para ello le agregamos dos 77
capacitores.
10101010100
0101010000

Cuarta y última Etapa - Regulación de tensión:

Fue todo bien pero al momento de consumir energía se regeneran fluctuaciones en la


fuente, por esto habrá que compensarlas de nuevo, es aquí donde aparece el regulador de
tensión.

Su tarea será mantener constante el nivel de tensión sin importar el consumo que halla
en la salida de la fuente, C3 será su colaborador en esta tarea. Finalmente tendremos un
polo positivo (+5V) y uno negativo (GND).
La disposición de terminales del regulador de tensión, está en la figura de abajo y será la
misma para todos los reguladores de tensión positivos, sus terminales son Entrada (E),
Común (C) y Salida (S).

Secuenciador de 5 Canales y 2 Efectos

Consiste en la implementación de un Circuito Integrado CD4017, los datos de este


integrado y sus múltiples aplicaciones se encuentran en la sección de circuitos
integrados.

El funcionamiento es muy simple, el integrado CD4017 es un contador divisor por diez


con posibilidades de detener la cuenta en un momento determinado y hacer que se
reinicie nuevamente, o bien permitirle la cuenta completa, o que se detenga.

Pues eso es justamente lo que haremos, trabajar con estas dos primeras posibilidades.

Lo que necesitamos es un timer para la entrada de reloj de este integrado y con el 555
es suficiente, pero le agregaremos un potenciómetro para regular la velocidad de los
pulsos de salida que entrega en el pin 3 o mejor aún podemos implementar un pic
para que nos entregue esta salida.
78

Ahora al CD4017. El pin 13 habilita la cuenta si esta a 0 (GND o VSS) para que esta se
reinicie al terminar un ciclo. Si está a 1 (VCC o VDD) realizará la cuenta sólo una vez, y ahí
quedará, como deseamos que continúe la pondremos a CERO.

Los pines 8 y 16 son la alimentación del integrado por lo que estarán a GND y VCC
(5V) respectivamente.
El pin 14 es la entrada de Reloj, ahí irá la salida del 555 (pin3) o bien la salida de un
microcontrolador. Los pines 3, 2, 4, 7, 10, 1, 5, 6, 9 y 11 son las salidas, todas en ese orden.

El pin 15, será el que nos seleccionará el efecto.

Si está unido al pin 1 la cuenta se realizará hasta 5 y volverá a comenzar (en realidad es hasta
6, pero solo veremos 5), de este modo:
010101000100010101010100

Si está a GND la cuenta será hasta 10, pero observa aquello de los diodos. Cuando cuente 6 se
1010101010101111101010010

encenderá el 5to LED, cuando cuente 7 se encenderá el 4to, cuando cuente 8 el 3ro y así
sucesivamente.

Observemos el esquema:
10101010100
0101010000

79

CN1 es el conector que va al monitor de LED's, CN2 es para la etapa de potencia que bien
podría ser con transistores o con optoacopladores. El que se presenta aquí está hecho a
transistores y no tuvimos problemas hasta ahora, el esquema es el que sigue:

Tengamos en cuenta que sólo representamos una de las salidas, por lo que deberás repetirlo
para las 4 restantes.
El esquema del circuito completo sería como el siguiente:

010101000100010101010100
1010101010101111101010010

Secuenciador CQPIC
10101010100
0101010000

Se trata de un Secuenciador controlado con un PIC18F1320. Basta analizar cómo se manejan


las entradas y las salidas del micro, para comprender como se lo debe programar.

Si ya viste los efectos, te darás cuenta de que se trata. Este secuenciador tiene 8 salidas 4
interruptores para seleccionar uno de los 16 efectos y una entrada para un timer (en este
80
caso con un 555), es decir que del micro no nos quedó ningúna terminal libre.

Entonces vamos por partes, El circuito del 555 debe disponer de un control de velocidad, por
lo que le pondremos un potenciómetro, bueno, y si quieres más velocidad cambia el capacitor
de 10 uf por uno de 1uf, la salida (el pin 3) la conectaremos al terminal RA4 del micro,
observa el esquema:

Los interruptores serán conectados a los pines RA0, RA1, RA2, RA3 y un terminal común a +5
V, cuando los interruptores están en off la señal que ingresa por estos pines es GND a través
de una resistencia de 10k. Podemos utilizar un array de resistencias.
Por otro lado, RB0 a RB7 son las salidas, el detalle es que sólo entregan 5V, así que lo
levantaremos con un ULN2803 que es un Array de 8 transistores, solo hay que tener en
cuenta que este integrado es de colector abierto, o sea que tiene las salidas invertidas, es
decir que cuando el micro envíe un 1, el ULN enviará un cero, por lo tanto los LED's para
monitorear las salidas deben ir con el ánodo al positivo de la fuente (con su debida
resistencia) y el cátodo al ULN, así:
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

Desde la misma salida del ULN puedes conectar el circuito de potencia que bien puede ser
con transistores o con opto triac's como en este caso, no olvides la inversión del ULN, esta es
la etapa de potencia que se utiliza en este secuenciador, la resistencia que va al opto puede
ser de 220Ω ó 330 Ω.
81

Claro que sólo representamos 3 de las etapas de potencia, faltarían las otras 5 pero son lo
mismo, el MOC3020 es un opto triac, muy fácil de conseguir, el Triac de potencia es un BT137
en el PCB figura BT136, la disposición de pines es la misma, pero la preferencia es el BT137
que puede manejar 6 ampers, la disposición de pines del MOC3020 es:
La fuente de alimentación es una muy sencilla con un solo diodo pero con 2 reguladores de
tensión, un 7805 para el micro y un 7809 para el ULN2803, los LED's y la Etapa de Potencia,
así no tendremos problemas en las salidas.

Emisor y Receptor Infrarrojo


010101000100010101010100
1010101010101111101010010

La teoría y la práctica se unen para hacer este pequeño proyecto, pues anteriormente se
habló de sensores y este es el ejemplo y proyecto que se propone para la implementación de
los mismos.
10101010100
0101010000

82
El esquema del circuito es muy sencillo, aquí se incorporaron dos emisores IR, para futuras
aplicaciones, donde los pines se colocaron con la idea de montarlo en cualquier otro circuito,
sin necesidad de atornillar, ni nada por el estilo.

Respecto al alcance que se puede lograr con este circuito, depende de la tensión de trabajo, y
uno que otro ajuste en el receptor, por ejemplo con una batería de 9V alcancé medio metro
más o menos. El que viene a continuación surgió con la idea de hacer un seguidor de líneas,
éste está basado en el conocidísimo integrado CD40106, el módulo incorpora emisor y
receptor, más una salida para cada sensor el derecho y el izquierdo que refleja su estado en
forma lógica es decir con un 0 o un 1.
Bien, el esquema del circuito es el que sigue:
010101000100010101010100
1010101010101111101010010
10101010100
0101010000

A destacar, que tanto los led's IR y los fotodiodos, fueron acomodados en la placa 83
de tal forma que en lugar de ellos pueda ir un conector de 4 pines, y de esta manera, el par
Emisor-Receptor será externo al módulo.

Detalles a comentar:

• RV1 (que aparece allí como REG. IR) Es la resistencia variable (Preset) que me permite
regular la frecuencia del oscilador.

• RV2 (REG1) y RV3 (REG2), son los Preset que regulan la sensibilidad de los fotodiodos
conectados a la izquierda y a la derecha del módulo respectivamente.

• J1 y J2 son los Jumper de selección del estado lógico que reflejará la salida de los sensores, y
como cumplen la misma función en ambos lados, sólo se describe uno.

Con J en la posición 1, el receptor estará trabajando con un sólo inversor, en esta situación
enviará un 1 lógico a la salida siempre que se esté recibiendo la señal del IR.

Con J en la posición 2, la situación será totalmente opuesta, el receptor enviará un 0 lógico a


la salida siempre que se esté recibiendo señal del IR.
• La bornera verde es para la fuente de alimentación, que si la utilizarás con un
microcontrolador por ejemplo deberá trabajar a 5V.

• La bornera azul es la que envía la información del estado de los sensores que mencionamos
anteriormente, del lado de la fuente el estado del sensor izquierdo y el que sigue corresponde
al derecho. Esto, será muy útil cuando trabajemos con nuestro PIC 18F1320.

No se incluyó en el PCB el par Receptor-Emisor, cada uno podrá armarlo, lo único a comentar
es que deberás regular el ángulo de incidencia entre el emisor y el receptor, ya que eso
dependerá de los componentes (IR y Fotodiodo o fototransistor) que utilices en este montaje,
esto es algo así como armar tu propio CNY70, para que tengas una idea, mira como me
quedaron en un ejemplo.
010101000100010101010100
1010101010101111101010010

Este sería un ejemplo de robot seguidor de línea a reserva de programar las salidas con
nuestro micro (claro que ya existen muchísimos sensores más):
10101010100
0101010000

84
Experimento 1 : Led enciende y apaga
Descripción:

Este experimento trata de acercar la teoría y lo platicado con la práctica, en su primer


programa conoceremos como realizar las configuraciones necesarias con el compilador
para comenzar a programar nuestro pic, terminando este proceso con el programa 1*
compilado y cargado en nuestro microcontrolador.

Mostraremos como utilizar nuestro programador pickit 2, que será el encargado de


010101000100010101010100

transferir el archivo hexadecimal creado por el compilador a nuestro pic. Se realizará


1010101010101111101010010

una plática sobre cómo funciona dicho programador y cómo lograr programar
diferentes microcontroladores de microchip con el mismo pickit 2, sin necesidad de
gastar en programadores universales y se proponen programadores alternativos de
gran calidad.
10101010100
0101010000

*programa 1:
Este programa está diseñado para encender y apagar un led, determinado
tiempo, en un bucle infinito.

Las variaciones del programa pueden ser:



Que no sea en bucle infinito, que se haga sólo una vez
Cambiar los tiempos de retardo en encendido y en apagado 85
• Encender y apagar varios leds a diferentes tiempos y diferentes
secuencias programables.
• Implementación de interrupciones
• Encendido y apagado de un led para determinar un evento, puede ser
una señal informativa, alarma, tiempo de disparo de algún circuito,
tiempo de activación de un relé y toda manipulación que se pueda
imaginar al llevar a un nivel alto o bajo un pin de nuestro pic.

Tareas a desarrollar:

Un semáforo

Alarma visual con led de alta luminiscencia

Conectar un buzzer y realizar mismas variaciones que con un led.


Encontrar un dispositivo que sea de última tecnología y se active con un
“1” o “0” lógico y sea manipulable del mismo modo que un led.
Experimento 2 : Led y Switch
Descripción:

Este experimento prueba el estado de un pin del puerto B(RB1) y dependiendo del mismo apaga o
enciende un led (RB0). Lo interesante es la configuración del puerto de ocho bits, donde poner 1
significa que ese pin es entrada y colocar un cero significa que ese pin es salida.

*programa 2:
010101000100010101010100
1010101010101111101010010

Este programa está diseñado para encender y apagar un led, dependiendo de la señal que
manda un botón (sea uno o cero), en un bucle infinito.

Las variaciones del programa pueden ser:


10101010100
0101010000

• Que no sea en bucle infinito, que se haga sólo una vez


• Encender y apagar varios leds, presionando varios botones.
• Implementación de interrupciones
• Encendido y apagado de otra circuitería, activar un buzzer, o simplemente mandar
una señal que se relacione con presionar el botón.

Tareas a desarrollar:
86
Realizar un programa que al presionar un solo botón vaya incrementando un
tiempo en que permanece encendido el led.

Realizar una implementación que al presionar el botón incremente los datos del
puerto B.

Imaginar que otras aplicaciones podemos realizar al presionar un solo botón.


Experimento 3 : Semáforo
Descripción:

Este experimento está diseñado para mostrar un ejemplo de la vida cotidiana y sencillo de
indicación con leds. En este caso un semáforo.

*programa 3:
010101000100010101010100

Se presenta el programa de manera rudimentaria para dar una clara idea de lo que sucede dentro
1010101010101111101010010

del micro y simular un semáforo. El programa esta paso a paso con el propósito de generar nuevas
ideas de cómo mejorarlo y hacerlo más funcional.

Las variaciones del programa y que se ven en clase son:


10101010100
0101010000

• Que no sea en bucle infinito, que se haga sólo una vez


• Cambiar los tiempos de retardo en encendido y en apagado
• Implementación de interrupciones
• Como utilizar funciones
• Como realizarlo con una librería

87
Tareas a desarrollar:

Que el semáforo después de realizar la rutina 3 veces, comience a mandar la señal


preventiva que normalmente se ve después de media noche

Imaginar una nueva manera de presentar este programa, utilizando nuevos


recursos.
Experimento 4 : Led´s rotando sólo ida
Descripción:

En esta práctica se pretende mostrar un led rotando, es un programa escrito de manera laboriosa,
utilizando la rotación prácticamente a mano, diciéndole al microcontrolador que dato enviar al
puerto y cuanto tiempo (500ms).

La rotación de un bit es uno de los ejemplos más comunes en el mundo de los microcontroladores, y
010101000100010101010100
1010101010101111101010010

utilizando la herramienta en c, existen diversas formas de llevar a cabo este ejemplo.

*programa 4:

El microcontrolador tiene conectado en el puerto b, 8 leds, desde RB0 hasta RB7, el


programa está diseñado para encender el primer led, mantenerlo encendido medio segundo
10101010100
0101010000

y apagarlo, después el segundo led y así hasta llegar al led 8, y el programa vuelve a iniciar.
Sólo lleva un sentido.

Las variaciones del programa pueden ser:

• Realizar el movimiento de leds de forma inversa


• Manipular el tiempo de encendido y apagado
• Comenzar en otro dato que no sea 0000 0001 88
Tareas a desarrollar:

Encontrar la anomalía del programa y pensar como corregirla

Encender dos led y rotarlos a la derecha

Encender tres leds y rotarlos a la izquierda

Realizar efecto de parpadeo, que el led parpadee 3 veces antes de rotar

Encender todos los leds excepto uno y rotar ese led apagado

Implementar técnicas de programación para realizarlo con : for, do, switch, while.
Experimento 5 : Led´s rotando ida y vuelta
En esta práctica se pretende mostrar un led rotando de izquierda a derecha, es un programa
escrito de manera laboriosa, utilizando la técnica de multiplicación de dato, pues considerando que
los datos de 8 bits se escriben 0000 0000, y que cada posición es 2n (0000 0001 = 1,
0000 0010=2, 0000 0100=4, 0000 1000=8, 0001 0000= 16, 0010 0000=32,
0100 0000=64, 1000 0000 =128, 1 0000 0000 =256).
010101000100010101010100

La rotación de un bit es uno de los ejemplos más comunes en el mundo de los microcontroladores,
1010101010101111101010010

y utilizando la herramienta en c, existen diversas formas de llevar a cabo este ejemplo.

*programa 5:

El microcontrolador tiene conectado en el puerto b, 8 leds, desde RB0 hasta RB7, el


10101010100

programa está diseñado para encender el primer led, mantenerlo encendido medio
0101010000

segundo y apagarlo, multiplicar el dato del puerto por 2 y mandar ese dato al puerto donde
están los leds, generando el efecto de rotación e ida y al llegar al ultimo led, dividir el dato
entre 2, para realizar el efecto de regreso.

Las variaciones del programa pueden ser:

• Realizar el movimiento de leds de forma inversa


• Manipular el tiempo de encendido y apagado 89
• Comenzar en otro dato que no sea 0000 0001
• Realizar el programa utilizando operadores << y >>
• Realizar el programa con una exponencial.

Tareas a desarrollar:

Encender dos led y rotarlos a la derecha

Encender tres leds y rotarlos a la izquierda

Realizar efecto de parpadeo, que el led parpadee 3 veces antes de rotar

Encender todos los leds excepto uno y rotar ese led apagado

Encontrar y corregir el error en el programa de operadores y corregirlo.


Experimento 6 : Puerto A = B
Descripción:

El experimento actual trata de mostrar un manejo claro del puerto, como enviar 8 bits a nuestro
puerto B del pic y manipular la información de modo tal que se vea reflejado a la salida del mismo.

Se muestra como configurar los puertos del pic para hacerlos funcionar como entrada o como
salida, para realizar la manipulación de datos. Podemos entonces configurar todo un puerto como
010101000100010101010100

entrada o todo como salida según nos convenga, o bien, sólo configurar unos bits del puerto como
1010101010101111101010010

entrada y otros como salida, también a nuestro gusto.

*programa 6:

El programa está diseñado para enviar datos al puerto B dependiendo de lo que haya en el
10101010100

puerto A, de modo tal que hacemos puerto B = puerto A (lo que esté en el puerto A será
0101010000

asignado al puerto B)

Las variaciones del programa pueden ser:

• Hacer la acción contraria puerto A= puerto B, considerando que el pin RA5 solo
funciona como entrada, no como salida (colocar resistencia de pull-up).
• Cambiar las configuraciones de entrada y salida de los puertos a nuestra
voluntad, haciendo algunas entradas y algunas salidas. 90
• Implementar con interrupciones
• Dependiendo de una entrada seleccionada, mandar una señal a la salida, podemos
utilizar cualquier sensor.

Tareas a desarrollar:

Comparar una entrada y enmascarar los datos para evitar errores, se puede generar
una secuencia de datos de entrada y si es correcta activar una salida.

Imaginar que otras cosas podemos realizar con cambios de estado de un pin o bien de
un puerto completo.
Experimento 7 : Display de 7 segmentos
Descripción:

En este experimento utilizaremos un display de 7 segmentos, de cátodo común, con ayuda de


una tabla, que es como normalmente se maneja, comenzar a enviar datos por el puerto, desde 0
y hasta 9.
010101000100010101010100
1010101010101111101010010

*programa 7:

Este programa está diseñado para realizar una cuenta 0-9 y realizarlo de manera
permanente.
10101010100
0101010000

Las variaciones del programa pueden ser:

• Que no sea en bucle infinito, que se haga sólo una vez


• Cambiar los tiempos en que se muestran los dígitos
• Implementación de interrupciones
• Cuenta regresiva
91
Tareas a desarrollar:

Contador de objetos
Que el dígito incremente al presionar un botón
Que el dígito decremente al presionar un botón.
Crear un dígito que no sea numérico (puede ser una letra).

Implementar un contador de 0000 a 9999


Experimento 8 : Display de cristal líquido (LCD)
Descripción:

El desarrollo de esta práctica nos muestra como conectar y comenzar a trabajar con un LCD de
16x2, las configuraciones necesarias y manejo de la librería lcd.c, enviando un texto y
mostrándolo en el display de cristal líquido.
010101000100010101010100
1010101010101111101010010

*programa 8:
Este programa está diseñado para mandar un texto, el que sea a la LCD. Debemos conocer
el límite de la pantalla 16x2, considerando la memoria virtual. (En 15 minutos!!!)

Las variaciones del programa pueden ser:


10101010100
0101010000

• Implementación de interrupciones
• Mandar un dato a la LCD cuando suceda un evento
• Hacer un menú de selecciones

Tareas a desarrollar:
92
Escribir un programa que mande el abecedario
Mover una palabra por toda la pantalla
Posicionarse en una coordenada que no sea (0,0)
Mostrar un mensaje y después de 3 segundos que muestre otro
Realizar un contador que se visualice en el LCD
Implementar un teclado & LCD
Realizar una animación
Implementar un programa que muestre datos en la LCD, un reloj, termómetro,
multímetro, frecuencímetro, comunicación etc.
Describir en qué consiste la inicialización de una LCD.
Experimento 9 : Modulación de ancho de pulso (PWM) y Led

Descripción:

Este experimento nos enseña la modulación de ancho de pulso, de 0 a 5v, manejando los ciclos de
trabajo de la señal digital.
010101000100010101010100
1010101010101111101010010

*programa 9:
Este programa está diseñado para encender un led conectado en la terminal
RB3(ccp1/pwm) y controlar la intensidad y brillo de un led de alta luminiscencia,
partiendo desde 0 y hasta el máximo de encendido.
10101010100
0101010000

Las variaciones del programa pueden ser:

• Implementación de interrupciones
• Control de un motor de CD
• Control de velocidad, intensidad, sonido, voltaje etc.

Tareas a desarrollar:
93

Control de velocidad de un ventilador de DC

Medir la salida 0-5v

Realizar un programa que al presionar un botón incremente el PWM

Realizar un programa que al presionar un botón incremente el PWM y otro botón


que al presionarlo decremente el PWM

Realizar un programa que mande datos determinados, por ejemplo, voltaje y los
despliegue en una LCD o al puerto serie.
Experimento 10 : Generación de tonos y melodías
Descripción:

Este experimento nos enseña como generar tonos específicos y duración de los mismos utilizando
la librería tones.c, y con instrucciones claras para la misma utilización.

*programa 10:
El diseño de este programa es sencillo y muestra una pequeña tonada, el clásico happy
010101000100010101010100

birthday.
1010101010101111101010010

Las variaciones del programa pueden ser:

• Implementación de interrupciones
• Modificar la duración de los tonos
10101010100
0101010000

• Realizar la tonada una octava arriba

Tareas a desarrollar:

Generar la tonada del himno nacional mexicano.

Generar la tonada del tema de la pantera rosa.


94
Generar la tonada de estrellita.

Realizar un programa que toque 3 tonadas diferentes y con duraciones aleatorias.

Desarrollar un programa que tenga conectados botones en un puerto y al


presionarlos suene un tono diferente, tipo piano.

Realizar un programa que comience a tocar una tonada al presionar un botón y que
la deje de tocar al presionar de nuevo.

Realizar una simulación de sirena desde 350hz y hasta 5 khz


010101000100010101010100
1010101010101111101010010 CONTACTO
10101010100
0101010000

curso.micropic@gmail.com

Teléfono Móvil: solicitar vía mail 95

Att. Ing. Gil Arellano

No olvides darle like a la página de Facebook, para


fomentar y difundir el desarrollo Tecnológico en México!

www.credetec.com.mx

Você também pode gostar