Escolar Documentos
Profissional Documentos
Cultura Documentos
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
Índice 2
Experimento 3 , Semáforo 87
2
Experimento 4 , Led rotando sólo ida 88
Experimento 6 , puerto A = B 90
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.
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:
pic C compiler(CCS) 5
proteus ISIS.
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)
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.
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.
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)
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:
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:
Directivas
#ASM /
#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:
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.
Ejemplo a)
10101010100
0101010000
Int resultado;
#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
Ejemplo a)
#BYTE STATUS = 3
Ejemplo b)
<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.
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)
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)
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:
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.
Ejemplo a)
• 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.
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.
#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
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:
11. INT_EXT2 Interrupción externa #2. 12. INT_I2C Interrupción por I2C.
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.
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:
Short 1 0o1
Int 8 0 a 255
0101010000
Char 8 0 a 255
Unsigned 8 0 a 255
Long 16 0 a 65536
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:
Tipo nombre_de_la_variable;
Int temperatura;
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
Contador = 100;
Ejemplo a)
Si la variable es de tipo char, la constante que se le asigna debe estar entre tildes, como
en el siguiente ejemplo:
Ejemplo b)
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;
Si una variable se declara dentro de una función, será "visible" solo dentro de ésta:
Ejemplo a)
funcion1 ( ) 16
{
10101010100
0101010000
Char letra;
.
.
}
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:
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)
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:
long c;
c = (long) (a * b);
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
• + (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;
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;
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.
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
• == igual a
• != distinto de
Operadores Lógicos
Los operadores lógicos nos permiten realizar las operaciones AND, OR y NOT:
• p || q significa p OR q
a = b && ( q || n )
Operadores de BIT
Existen seis operadores pensados para trabajar directamente sobre los bits. Solamente pueden
010101000100010101010100
20
1010101010101111101010010
• & (AND)
• | (OR)
10101010100
0101010000
• ^ (XOR)
• ~ (complemento)
• a&b=8
• a | b = 125
• a ^ b = 117
• ~ a = 135
OPERADORES:
Operadores de bit
a = 01111000 b = 00001101
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
OPERADORES:
Estos operadores permiten sumar (o restar) uno al valor de una variable. Lo que
generalmente haríamos así:
ó así:
• ( ) 22
• signo +, signo - , ++ , - -, ! , (<tipo de operación>)
10101010100
0101010000
• *, /, %
• +, -
• ==, !=
• && , ||
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.
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
long ejemplo( );
long multiplicación( );
Parámetros:
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:
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);
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:
{
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);
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
{
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).
Esta es la pregunta que puede alborotar a más de un programador de C. Sirve para muchísimas
cosas:
• Modificar más de una variable dentro de una función (puede devolver más de un valor)
010101000100010101010100
1010101010101111101010010
• 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
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.
#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
#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;
delay_cycles(1);
}
010101000100010101010100
1010101010101111101010010
28
10101010100
0101010000
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)
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:
#include <18F1320.h>
#use delay(clock=8000000)
void main()
010101000100010101010100
29
1010101010101111101010010
*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:
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()
}
010101000100010101010100
1010101010101111101010010
Podrán observar que se usa el puntero sin el *. Esto significa que se guardará allí una
0101010000
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:
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:
void main()
31
10101010100
0101010000
void main()
long j;
long *q;
int k;
delay_cycles(1);
32
1010101010101111101010010
}
10101010100
0101010000
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.
Vamos con otro ejemplo. Supongamos que i sea del tipo float (4 bytes) pero su apuntador lo
declaramos como int (1 byte):
void main()
{
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.
void main ( )
{
q=&j;
1010101010101111101010010
delay_cycles(1);
10101010100
0101010000
}
34
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
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:
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)
#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 ( )
{
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
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.
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.
¿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.
Bueno seguro que alguien llegará y colocará a k y l como globales y entonces así se
puede modificar en cualquier lado.
}
1010101010101111101010010
void main()
{
int k,l,resultado;
k=5;
10101010100
l=2;
0101010000
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;
p=&cadena[0];
for(t=0;t<7;t++)
{
c= *p + t;
}
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
¡Grave error!
¿Cómo se soluciona?
PUNTEROS:
43
010101000100010101010100
1010101010101111101010010
10101010100
0101010000
es decir: *(p+t) =
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?
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:
#include <18F1320.h>
#use delay(clock=8000000)
char cadena[7]={'T','o','d','o','P','i','c'};
void main( )
{
010101000100010101010100
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
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:
47
Todo depende de la forma en que deseas trabajar, en el caso anterior la señal lógica
10101010100
0101010000
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:
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.
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:
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.
negada, un buffer de salida inversor que puede entregar o absorber una corriente de 200mA
0101010000
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.
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
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
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.
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
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.
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:
Aquí el pulso de salida aparece con mayor o menor retardo según aumente o disminuya
10101010100
56
IC-Datos - Circuito Integrado CD4013
Descripción:
•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.
•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:
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.
Descripción:
El Diagrama de funciones:
010101000100010101010100
1010101010101111101010010
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.
"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.
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:
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".
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.
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.
64
Tabla de verdad:
Circuito de Prueba:
65
IC-Datos - Circuito Integrado CD4066
Descripción:
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.
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:
67
IC-Datos - Circuito Integrado CD4511
Descripción:
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
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
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.
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:
corriente, que le permite Rc. Rc es la resistencia de carga, que bien podría ser un
= 0,0054 = 5,4 ma Ib =
0,0012 = 1,2 ma
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.
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.
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 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
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:
Donde:
• 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:
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:
76
010101000100010101010100
1010101010101111101010010
10101010100
0101010000
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 (-).
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).
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.
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
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.
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.
• 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:
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.
•
•
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
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.
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.
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.
87
Tareas a desarrollar:
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
*programa 4:
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.
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
*programa 5:
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.
Tareas a desarrollar:
Encender todos los leds excepto uno y rotar ese led apagado
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
*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)
• 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:
*programa 7:
Este programa está diseñado para realizar una cuenta 0-9 y realizarlo de manera
permanente.
10101010100
0101010000
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).
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!!!)
• 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
• Implementación de interrupciones
• Control de un motor de CD
• Control de velocidad, intensidad, sonido, voltaje etc.
Tareas a desarrollar:
93
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
• Implementación de interrupciones
• Modificar la duración de los tonos
10101010100
0101010000
Tareas a desarrollar:
Realizar un programa que comience a tocar una tonada al presionar un botón y que
la deje de tocar al presionar de nuevo.
curso.micropic@gmail.com
www.credetec.com.mx