DTMF Control Por Celular

Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 37

DTMF , Control a distancia con teléfono celular

Por
Gustavo Circelli
-
marzo 6, 2015
0
3682

Control DTMF

DTMF es la sigla que identifica a Dual-Tone-Multi-Frecuency. En esta oportunidad vamos a


realizar un proyecto de control a distancia utilizando el teléfono Celular. Para tal objetivo, nos
basaremos en un control por tonos duales, o DTMF.

El tema de decodificación DTMF, es conocido, lo que resulta interesante es la manera de agregar


ciertas características profesionales como contraseña de entrada y claves para activar y desactivar,
así también como un dispositivo audible desde el mismo celular con que se opera, para
la confirmación de las acciones realizadas. La idea también es aprovechar para aprender algo
sobre la implementación con Microcontroladores PIC.

El sistema conceptual puede dividirse en 3 módulos como se ve en la figura

Bloques control DTMF

Describiremos en profundidad cada bloque con el Hardware y el Software asociado.

DETECTOR DTMF
Los tonos de audio que se generan en un teclado convencional de telefonía están normalizados.
En realidad no es un tono simple sino una combinación de tonos formado por dos grupos de
tonos, el grupo bajo y el grupo bajo. Cada dígito o carácter del teclado posee una combinación
de grupos única e irrepetible que permite determinar el dígito que ha sido pulsado. En este
módulo utilizaremos el popular HT9170 que es relativamente simple de utilizar. Este chip puede
configurarse de 2 maneras diferentes, en modo diferencial y en modo común. Se utilizó el modo
común.

La función de este chip es detectar los grupos validos de un digito y ofrecer un código binario a
la salida para que se pueda leer desde un Controlador, o algún otro circuito decodificador.

Les presento el diagrama en bloques:


Chip DTMF

Si bien el Datasheet está disponible en internet y lo pueden bajar, voy a considerar alguna
cuestiones de diseño:

Como se observa, a la entrada se encuentra un amplificador operacional para ajuste de ganancia


y filtrado. Para operación en modo común el esquema es el siguiente:
Operacional de entrada DTMF

En esta configuración, se ingresa por la pata negativa del operacional y se observan las ecuaciones
de ganancia, impedancia y frecuencia de corte, S=JW y es el operador Laplaciano que se usa para
modelar las respuestas de frecuencia y trazar diagramas de Bode. Sin entrar en demasiados
detalles, se aprecia que la ganancia posee una raíz en el origen S y un polo (lugar donde la función
se hace infinita) en S=1/RC. Para analizar esto matemáticamente es necesario trabajar con el
módulo por un lado y con la fase por el otro. Para evitar esto se puede trazar el diagrama de Bode
que opera en escalas logarítmicas. Las frecuencias se expresan en dedadas y las amplitudes en
decibeles. Así por ejemplo vemos que 1 Hz la amplitud es de -100DB, es decir casi cero. El cero no
lo representamos porque el Log base 10 de 0 no existe, en el límite de frecuencia tendiente a cero,
la ganancia es prácticamente cero, es lógico dado el capacitor C que filtra la continua. Los ceros
en Bode se representan con una pendiente de +20Db/década y los polos a -20 Dpb por década.
Cuando se llega a la frecuencia del Polo S=1/RC los +20, se cancelan con -20 y queda una
constante que es nuestra ganancia Rf/R. La otra frecuencia de corte queda determinada por el
operacional.
Diagrama de BODE respuesta entrada DTMF

report this ad

Como se ve queda una banda de paso. Dentro de esta banda deben ingresar todos los tonos a
ser detectados, cuya tabla es la siguiente:
Tabla e todos y decodificación binaria DTMF
El grupo de frecuencia más pequeño corresponde a 697 Hz, por lo tanto S=1/RC < 697 Hz. Bode
también permite saber que justo en la frecuencia de corte la caída o perdida de ganancia es de -
3db, es decir 0.707 por la ganancia que tenía hasta ese momento, es decir Rf/R. Todo esto es
verificable matemáticamente. El manual recomienda lo siguiente:

HT9170-DTMF
Como vemos RF=R es decir la ganancia en la banda de paso es 1, la frecuencia del polo es W=1/RC,
sabiendo que W = 2*PI*f, se tiene que fc= 16 Hz, es decir que va a atenuar los tonos por debajo
de los 16 Hz y entre 16 y el corte del operacional va a amplificar en 1 los tonos que vengan. Por
mi parte lo que hice fue darle una ganancia ajustable que no modifica la respuesta pero si la
impedancia de entrada, es una relación de compromiso. RF la formé con una resistencia de 12 K
en serie con un Preset Multivueltas de 50 K y R=12 K. Luego la ganancia máxima será de 12+50/12
= 6 veces y el corte me quedó en fc=1/12K*0.1Uf = 132 Hz lo cual limita más la banda de paso y
lo hace más inmune al ruido. Todo esto a expensas de bajar la impedancia de entrada lo cual por
un lado favorece cuestiones de ruido, pero por el otro carga a la salida de audio del celular y va a
bajar un poco el nivel, pero funciona muy bien. Ustedes pueden experimentar con sus propios
valores.

Ganancia ajustable DTMF


El resto, se configura similar al Datasheet, con cristal 3.58Mhz, es importante elegir un cristal
adecuado, de bajo corrimiento. Yo use uno de 3.575611 MHz y capacitores de 18pf.

En cuanto al resto que se observa RG/CT y EST configuran las constantes internas de detección de
un tono válido y se pueden estudiar en el Datasheet y su diagrama de tiempos, pero respetando
estos valores funciona correctamente.

Lo importante es la señal DV que emitirá un pulso de 5v indicando que se ha Latcheado o


memorizado en el registro de salida D3 a D0 el código binario de un tono dual válido, ver la tabla
de tonos anterior de códigos binarios de decodificación.

El resto de los pines del HT9170

OE= 5V fijos

INH y PWDN = 0v

En la tabla de tonos, se ve que del 1 al 9 sigue la conversión convencional de decimal a binario, el


0 sería el 10 en decimal, el “ * “el 11 y el “ # “ el 12. Las letras no las usamos.

MICROCONTROLADOR
Este bloque tiene el objetivo de procesar las funciones del equipo, Las señales D0 a D3 del DTMF
van a conectarse a un Puerto del microcontrolador, La señal DV a otro pin del mismo con ciertas
características. El puerto de salida del microcontrolador actuará sobre el Driver del sistema de
actuación.

Se utilizó un PIC 16F873 con el entorno de programación MPLABX de microchip y compilador XC8.
El micro operará a 4 MHz de frecuencia.
Les dejo los siguientes enlaces:

 Micro: Micro Pics enlace


 Compilador XC8 y MPLAB X: Enlace Herramientas

Esta es la parte más compleja, en el sentido que involucra no solo conocimiento de Hard del micro
sino también como herramientas de programación, es decir Soft, y conocimiento del lenguaje C
como base que usan los compiladores. A diferencia de ARDUINO, los sketches son más simples,
es decir, los sketches ya son un C precompilado lo cual lo hace más simple. La idea es tratar de
explicarles lo más simple posible este proyecto DTMF ya que hay lectores que no tienen
experiencia y por otro lado los que ya conocen del tema puedan usarlo como referencia. La idea
no es un Post sobre PIC’s , sino una implementación usando PIC’s, por lo tanto puede haber
lectores que no comprendan algunas cuestiones si no han trabajado antes con ellos.

Este POST, como dije, no trata las características de un PIC sino la implementación del proyecto
DTMF, no obstante se tratará de ser lo más didáctico posible.

En principio, vemos que cada pin tiene varias identificaciones esto es debido a que los PIC
multiplexan

PIC usado en el DTMF


diferentes funciones en cada pin, como Digitales, Analógicas, de eventos externos, fuentes de reloj
diversas, protocolos asincrónicos como USART , y sincrónicos como I2C , MSSI para comunicarse
con sensores, memorias, etc.

Para que el micro tenga determinada funcionalidad hay que configurar los Registros internos de
acuerdo a lo deseado. Esta es una gran diferencia con ARDUINO, que no es necesario conocer el
Hard interno.

El esquema de conexiones para el Microcontrolador como parte del DTMF será el presentado a
continuación. He utilizado el Eagle 6.1.0, para realizar el esquemático y el diseño final de la placa
.
Esquema Conexiones Microcontrolador
DTMF

Concentrándonos en este bloque del Micro, observamos lo necesario:

 Un pulsador de reset general, que es útil para las pruebas.


 El oscilador externo de 4mhz con los capacitores de 18 pf
 RA0 a RA3 serán entradas digitales conectadas a D3-D0 del decodificador DTMF, es decir
RA0-D3, RA1-D2, RA2-D1 y RA3-D0.
 El pulso DV de salida de tono válido del DTMF HT9170 se conecta a RB0/INT.
 Las RB7 a RB4 serán salidas digitales que activarán el driver, módulo que sigue. Cada una
maneja la activación / desactivación de una carga de 220 VCA, es decir, este equipo tiene
la capacidad de manejar 4 cargas.
 RB1 será la salida de activación de un Buzer sonoro para dialogar vía celular.
 RB0/INT será entrada digital del pulso DV del DTMF.

RB0/INT indica que además de ser un pin de entrada/salida digital, puede ser fuente de
Interrupciones de eventos externos (INT). De hecho se utilizará de esa manera, es decir cuando se
genere un tono válido el pulso de DV disparará una interrupción de software interna en el PIC e
irá a atender un bloque de programa especial que atiende dicha interrupción, más adelante se
explicará cómo se implementa esto.

Características de funcionamiento

El equipo de control DTMF, funcionará de la siguiente manera:

 Contraseña de 3 dígitos , por ejemplo “7#*” o la que se prefiera


 Clave de activación / desactivación de salidas, ejemplo 11* activa salida 1 y 10* la
desactiva. Lo mismo para la 2, 3 y 4. <Salida><1-on, 0-off><*>.
 El sistema opera en 2 estados, estado 0 y estado 1. El estado 0 es cuando se ha conectado
y se espera validar la contraseña, sin ella no se podrán operar las salidas. El estado 1
cuando se ha validado la contraseña.
 En estado 0, el equipo valida 5 segundos de espera entre digito y digito, si expira este
tiempo habrá que comenzar de nuevo.
 En el estado 1, las salidas se pueden operar y permite un timeOut de 5 minutos desde el
último pulsado. Aun cuando se corte la comunicación, se podrá llamar nuevamente y
operar las salidas sin necesidad de la clave. Pasado ese tiempo volverá al estado 0.
 Un buzzer sonoro indicará determinadas acciones. Contraseña invalida o clave mal
ingresada, serán 10 beeps seguidos. Contraseña válida, un beep largo. Activación /
desactivación válida 3 beeps. Estos beeps serán escuchados desde el celular de origen
de llamada.

Necesidades de configuración

Pin RBO/INT como entrada y fuente de interrupciones

Timer que cuente 5 segundos y 5 minutos

Funciones de Beep sonoros

Cambios de estado 0 y 1
Contraseñas y claves

Software

Vamos a ir explicando las secciones de código y explicando lo que hacen y su interacción con el
Hardware del micro. Para esto es necesario utilizar el MPLABX que es el entorno de desarrollo
donde programamos, compilamos, simulamos y guardamos el ejecutable en el propio PIC.
Nuevamente, no voy a detenerme en el MPLABX ya que no es el interés de este post. El MPLABX
tiene la plataforma del NetBeans de Java.

El proyecto en el MPLABX se verá de esta manera:

Proyecto DTMF
Librerías: Se deben incluir en el proyecto y vienen con el compilador XC8

#include<string.h> // Necesaria para trabajar con cadenas


1
2
#include <stdio.h> // Ansi C de entrada salida
3
4
#include <stdlib.h> // Ansi C estandard
5
6
#include "delays.h" // header externo incluido, cuando se importa una librería copiada pero no // //disponible para PIC16, para generar
7
retardos
8
9
// hay que implementar las funciones en el código de dicha librería, yo agregue las funciones en los archivos fuentes.c
10
11
#include "pic16f873.h" // librería de definiciones del PIC
12
13
#include <xc.h> // compilador

Preprocesadores: todo micro posee registros especiales que preconfiguran globalmente al PIC por
ejemplo le decimos que usamos un cristal tipo XT, el WatchDog timer en Off, El Power On Reset
que indica que el micro arranca cuando la fuente de alimentación es estable, Programación de
bajo voltaje Off, etc. Para más detalle hay que consultar el DataSheet.

// CONFIG
1 #pragma config FOSC = XT // Oscillator Selection bits (XT oscillator)
2 #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
3 #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
4 #pragma config CP = OFF // FLASH Program Memory Code Protection bits (Code protection off)
5 #pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled)
6 #pragma config LVP = OFF // Low Voltage In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for
7 programming)
8 #pragma config CPD = OFF // Data EE Memory Code Protection (Code Protection off)
9 #pragma config WRT = ON // FLASH Program Memory Write Enable (Unprotected program memory may be written to by EECON
control)

Declaraciones: Crear nombres de referencia para mapear bits

1 Declaraciones: Crear nombres de referencia para mapear bits


2 #define Sal_1 PORTBbits.RB7 // Salida de control 1 con reles
3 #define Sal_2 PORTBbits.RB6 // Salida de control 2
4 #define Sal_3 PORTBbits.RB5 // Salida de control 3
5 #define Sal_4 PORTBbits.RB4 // Salida de control 4
6 #define Sal_5 PORTBbits.RB3 // Salida de control 5 NO SE PUEDE DIRECTO RA4 ES OPEN DRAIN
7 #define Sal_6 PORTBbits.RB2 // Salida de control 6
8 #define Beep PORTBbits.RB1 // Buzer de indicacion sonora
9 #define FlagPulsoDTMF INTCONbits.INTF // Flag de evento RBO/INT salisa DV del DTMF

Vemos que por ejemplo el pin RB7 del PortB lo llamamos SAL_1. Es decir creamos nuestras propias
definiciones de nombres más manejables en el programa. Se ve que el programa posee hasta 6
salidas pero yo solo he implementado 4 por tema de espacio en la plaqueta y dimensiones del
gabinete final.

Observamos también que la definición FlagPulsoDTMF que se mapeará con el registro INTCON,
bit INTF. Para saber que es esto vamos al datasheet:

PIC 16F873 DTMF


Este registro controla muchas cosas, lo que nos interesa es que el bit1, INTF es un bit o flag que
se enciende cuando ha ocurrido una solicitud de interrupción RB0/INT, para nuestro caso el pulso
del DTMF, DV.
Variables

1 // VARIABLES
2 char Digito[14]={'0','1','2','3','4','5','6','7','8','9','0','*','#',''};
3 // Digito[0], no se usa ya que el Digito[10]='0' caracter , '' fin de cadena String
4 char codigo[4]={'','','',''}; // guarda en formato cadena 3 caracteres de entrada + el null de fin ''
5 const char contrasena [4]="#9*"; // Contraseña para operacion del DTMF
6
7 unsigned char estado=0; // Variable que indica el estado del controlador DTMF
8 // 0 contraseña invalida o no recibida
9 // 1 contraseña valida y se pueden operar las salidas
10 // DETECCION DE CODIGO DE DIGITO CONTRA DTMF
11 // PUERTO B entradas RA0=D3, RA1=D2, RA2=D1, RA3=D0 DEL DTMF
12 unsigned int contador=0; // Contador de degitos que van ingresando
13
14 unsigned int ciclosT1=0; // ciclos de cuentas Timer1
15 unsigned char flagEntrada=0; // Indica que vino un pulso del DTMF 0/1
16 // ciclosT1= 10 SERAN 5 SEG, 10 desbordes de T1 a 65536 * 0.1us * 8
17 // ciclosT1= 20 SERAN 10 SEG, 20 desbordes de T1 a 65536 * 0.1us * 8
18
19 char S1ON [4]="11*"; // Codigos de activacion y desactivacion salidas
20 char S1OFF [4]="10*"; // agrega '' al final de cada cadena
21 char S2ON [4]="21*";
22 char S2OFF [4]="20*";
23 char S3ON [4]="31*";
24 char S3OFF [4]="30*";
25 char S4ON [4]="41*";
26 char S4OFF [4]="40*";
27 char S5ON [4]="51*";
28 char S5OFF [4]="50*";
29 char S6ON [4]="61*";
30 char S6OFF [4]="60*";
31 char SALLON[4]="666" ;// ENCIENDE TODAS
32 char SALLOFF[4]="000";// APAGA TODAS

Aquí hay varias cosas, el arreglo Digito[14], es un vector timo string o cadena que relaciona el
índice con el carácter, de manera que Digito[código D3-D0] =Carácter buscado, es decir es como
un puntero. Por ejemplo si mi código D3-D0 que es un nible de 4 bits es el 00001010 (solo válido
lo últimos 4 bits), entonces Digito [10] = ‘0’ carácter 0. Las variables tipo Unsigned Char, son de 8
bits, es decir representan el código Ascii entre 0 y 255 del carácter guardado.

La variable Char código [4] también es un arreglo de caracteres. El Ansi C, al crear una variable
cadena o arreglo siempre hay que finalizarla con el carácter NULL de fin de cadena o ‘’. En esta
variable vamos a ir concatenando los dígitos de entrada, la cadena se arma cada 3 dígitos más el
NULL, por eso su dimensión es 4.

La variable Const Char Contraseña[], similar, aquí definimos la cadena de contraseña.

Unsigned Char estado =0; es el estado del equipo , 0 o 1, ya explicado.


unsigned int contador, lleva la cuenta de los dígitos o caracteres ingresados en la cadena código[].

unsigned int ciclosT1=0, es una variable entera entre 0 y 65535. Para explicar esto debemos saber
como es la temporización en los micros.

El cristal es de 4 MHz, es decir que cada pulso de reloj es de 1/4Mhz= 0.25 us. El TCY o ciclo de
instrucción es el tiempo que tarda en ejecutarse una instrucción dentro del micro y es una cantidad
conocida. La ejecución se lleva a cabo en 4 etapas dentro de la PIPELINE que posee las etapas de
Fetch (búsqueda de instrucción) – Decoding (decodifica la instrucción)- OP (operandos) – Ex
(ejecución y almacenamiento), es decir que cada TCY = 4 pulsos de reloj. Hay instrucciones que
como las de salto que consumen más de 4 ciclos de reloj. Entonces 1TCY= 4×0.25us = 1us. Los
TCY son las fuentes de conteo de los timers internos del PIC, como TMR0 ( timer 0), TMR1 ( timer
1), etc.

El Timer 1 es de 16 bits es decir que puede contar entre 0 y 65535 y luego pasa a cero nuevamente,
es decir que cada vuelco del Timer1 o ciclo completo puede generar 65536*1us= 65.536 ms. Es
65536 ya que se considera un pulso más desde 65535 a cero cuando desborda y vuelca, lo cual es
indicado por el bit flag TMR1IF. Al Timer 1 se le puede asignar un Prescaller, esto es que el pulso
que cuente sea el TCY pero dividido previamente:

16F873 DTMF
Los bits del registro T1CON permiten asignar el prescaller al Timer1. Entonces si lo afectamos por
un prescaller de 1:8:

ciclosT1= 10 SERAN 5 SEG, 10 desbordes de T1 a 65536 * 0.1us * 8

Prototipo de Funciones

Se definen funciones o métodos para estructurar mucho más todo el software, es decir dividir el
problema en sus problemas.
1 void configIO(void); // Configuracion de perifericos
2 void configIRQ(void); // Configuracion para la interrucion por Flanco ACS RBO/INT
3 void Beeper(int cant, int tpon, int tpof); // Funcion de indicacion sonora
4 // cant=cantidad de beeps, tpon=HIGH de cada beep , tpof= LOW
5 unsigned char LeerPuerto(void); // Lee el puerto del DTMF
6 void limpiarTodo(void); // Inicializa todo nuevamente
7 void configT1(void); // Configuración del Timer 1 para tiempos entre teclas 5 seg y 5 minutos
8 void on_T1(void); // Enciende Timer 1
9 void chequeaTiempo(void);// Chequea si se ha superado el tiempo entre digito y digito
10 bit chequeaEstadoT1(void); // consulta si el timer esta encendido o
11 void apagaT1(void); // apaga el timer1
12 void limpiaT1(void); // limpia el Timer1, lo reseta junto con sus ciclos
13 void controlSalidas(void); // controla las salidas
14 void limpiarCodigo(void); // limpia variable de cadena de código

Configuración I/0

1 void configIO(void){
2 ADCON1=0x06; // A/D desactivados Y PORT A RA0-RA5 como Digital pag 114 datashhet
3 ADCON0bits.ADON=0; //Modulo interno de conversion apagado
4 TRISA =0xFF; // PUERTO A todo entrada para señales DTMF
5 //TRISC=0x00; // PUERTO C salida
6 TRISB=0b00000001; // PUERTO B todo salida a excepcion de RB0/INT
7 PORTB=0x00;
8 Sal_1=0;
9 NOP();
10 Sal_2=0;
11 NOP();
12 Sal_3=0;
13 NOP();
14 Sal_4=0;
15 NOP();
16 Sal_5=0;
17 NOP();
18 Sal_6=0;
19 NOP();
20 Beep=0;
21 }

Los ADCON1 y 0 regulan la operación analógica digital. Cuando arranca el micro siempre están
activados impidiendo el uso de pines I/O digital. Por eso se desactivan. El ADCON1 escribirndo un
0x06 en hexadecimal. Fila 7 de la tabla.
16F873 DTMF
Los TRISA, B, C son los registros de dirección del PUERTO A, B, C, etc. El “1” significa entrada y el
“0” salida, inversamente a ARDUINO. Son registros de 8 bits, TRISB0 = definirá RBO, TRISB1, RBI y
así siguiendo.

Cuando hacemos uso de PORTB, A, C, etc. estamos leyendo el puerto o escribiendo.

PORTB=0, escribirá 0x00 (00000000) todos 0 a la salida de cada RB7 al RB0. RBO es una entrada
por lo cual no tendrá efecto la escritura de RB0.

Configuración Interrupción
1 void configIRQ(void){
2 OPTION_REGbits.INTEDG=1; // configura RB0(PulsoDigito) para IRQ Flanco Ascendente
3 INTCONbits.PEIE=0; // interrupción periféricos desactivada RBO/INT no necesita permiso periféricos
4 // Me aseguro que solo RBO/INT sea la fuente de las interrupciones
5 INTCONbits.RBIE=0; // interrupción por RB0 desactivada para cualquier cambio puerto B
6 INTCONbits.INTE=1; // Habilita IRQ RBO/INT
7 INTCONbits.INTF=0; // Limpia Flag de IRQ es lo mismo que FlagPulsoDTMF=0;
8 INTCONbits.GIE=1; // Habilita interrupciones globales
9
10 }

16F873 DTMF
Si volvemos a la tabla del INTCON veremos los significados de los diferentes bits. El bit INTEDG
del registro OPTION_REG define el flanco de disparo de la interrupción IRQ.
Normalmente las interrupciones se habilitan con permisos de periféricos , con bits especiales y el
permiso global de interrupciones bit GIE. Cada periférico sea un conversor AD, PWM, TIMERS, ETC
poseen Flags de disparo de eventos, al igual que como hemos explicado el RB0/INT. Veamos el
siguiente esquema lógico de permisos:

167873 INTERRUPCIONES DTMF


Los bits IE son los permisos y los IF los flags de eventos. Vemos que GIE=1, habilita globalmente (
compuerta lógica AND). El INTE es el bit que habilita el permiso de Interrupción de RB0/INT via su
flag RBIF que ya lo hemos visto. El bit PEIE=0, no permite otra fuente de interrupción de otros
periféricos, es un 0 en una AND.

Indicación Sonora
1 void Beeper(int cant, int tpon, int tpof){
2 // TPO MINIMO BASE =0.1 SEG
3 /*
4 10000* 10 * tpon o tpof
5 HIGH= 0.1* tpon
6 LOW= 0.1 * tpof
7 Contraseña invalida: 10 beeps de 0.1 seg Beeper(10,1,1);
8 Digito valido: 1 beep de 0.5 seg no lo usamos
9 contraseña valida : 1 beep de 2 segundos Beeper(1,20,1);
10 salida activada/desactivada: 3 beep de 0.1 seg Beeper(3,1,1);
11
12 */
13 int i;
14 int j;
15 for(i=1;i<=cant;i++){ // la variable unit compilada en la libreria es el
16 // argumento de 10KTCYx y va de 0 a 255 es unsigned char
17 Beep=1;
18 for (j=1;j<=tpon;j++){
19 Delay10KTCYx(10);
20 }
21 for(j=1;j<=tpof;j++){
22 Beep=0;
23 Delay10KTCYx(10);// Toff es siempre el mismo
24 }
25 }
26 }

Esta función se encarga de generar una onda cuadrada , el tiempo en ON, y en OFF asi también
como la cantidad de ciclos a generar. La función Delay10Ktcy(A), genera 10*1000*1TCY*A demora
de tiempo. Esta función debe ser incluida como archivos fuente y está dentro del compilador XC8.

10KTCY = 10*1K*1TCY. El argumento cant, pasado determina la cantidad de ciclos de beep.

Lectura del Puerto del DTMF

1 unsigned char LeerPuerto(void){ // Lee el puerto y devuelve un decimal del digito DTMF
2 unsigned char puerto; // almacena los bits leidos del DTMF
3 unsigned char puertoFinal=0x00; // arma el byte correcto
4 puerto=PORTA; // SOLO INTERESAN RA0-RA3 Lee el puerto
5 puerto&=0x01; // lee D3 MSB
6 puerto<<=3; // QUEDA 0000 D3 000
7 puertoFinal|=puerto; //0000 D3 000
8 puerto=PORTA;
9 puerto&=0x02; // Lee D2
10 puerto<<=1; // queda 000000 D2 00
11 puertoFinal|=puerto; // QUEDA 0000 D3 D2 0 0
12 puerto=PORTA;
13 puerto&=0x04; // Lee D1
14 puerto>>=1; // queda 000000 D1 0
15 puertoFinal|=puerto; // QUEDA 0000 D3 D2 D1 0
16 puerto=PORTA;
17 puerto&=0x08; // Lee D0
18 puerto>>=3; // queda 0000000 D0
19 puertoFinal|=puerto; // QUEDA 0000 D3 D2 D1 D0
20 puertoFinal&=0x0f;
21 //puerto>>=4; // desplaza 4 lugares a la derecha asi se puede usar como indice
22 return puertoFinal;
23 }

Esta función es compleja debido a que en la construcción , para que el HT9170 se conecte al PIC
línea a línea y sin cruces de pistas, el PORTA leído y llevado a una variable puerto de 8 bits queda
de la siguiente manera:

Variable puerto:

x x x x D0 D1 D2 D3

Como vemos está invertido en los pesos binarios , y hay que llevarlo a que quede de esta manera:

0 0 0 0 D3 D2 D1 D0

Esto va a permitir leer la variable puerto directamente el valor decimal del código.

Esto se logra leyendo de a cada bit individual e ir desplazándolo en la variable final juntamente
con operaciones OR. Lo dejo para ustedes para analizar.

Ahora el byte retornado será usado de índice para el arreglo Digito[].

Limpiar Todo

1 void limpiarTodo(void){
2 estado=0;
3 contador=0;
4 limpiarCodigo();
5 flagEntrada=0;
6 ciclosT1=0;
7 apagaT1();
8
9}

Esta función, básicamente vuelve al estado del micro a estado=0, reinicia las variables como si
arrancara nuevamente.

El Timer 1
1 void configT1(void){ // Configura Timer 1 para que cuente 5 seg y 10 seg
2 // Configurarlo con prescaller 1:8 T1CKPS1=T1CKPS0=1
3 // Oscilador timer1 externo desactivado T1OSCEN=0
4 // EN FORMA SINDRONICA T1SYNC#=0
5 // Fuente de cuentas interno xtal/4 TMR1CS=0
6 // TMR1ON =1/0, enciende y apaga
7 T1CON=0x30;
8 //T1CONbits.TMR1ON=0; // apagado
9 apagaT1();
10 TMR1H=0x00; // Puesta a cero
11 TMR1L=0x00;
12 // ciclosT1= 10 SERAN 5 SEG, 10 desbordes de T1 a 65536 * 0.1us * 8
13 // tiempo de validacion entre tecla y tecla para la clave Estado=1
14 // ciclosT1= 600 SERAN 300 SEG=5 minutos, 600 desbordes de T1 a 65536 * 0.1us * 8
15 // Una vez entrada la clave permite 5 minutos a partir del último ingreso de tecla
16 // pasados los 5 min vuelve al Estado= 0
17 }

Básicamente se le asigna la fuente de conteo al Timer1, que sea el reloj o Xtal= 4mhz, la fuente de
reloj secundaria y externa apagada, el prescaller en 8, su cuenta en cero, y se lo deja apagado.
Todo esto se logra con T1CON=0x30;

Encender Timer1

1 void on_T1(void){
2 T1CONbits.TMR1ON=1; // enciendo Timer
3 limpiaT1();
4
5}

Enciende el Timer, ver tabla del registro T1CON, el bit TMRION

Chequear Tiempo

1 void chequeaTiempo(void){
2 if(PIR1bits.TMR1IF==1){ // DESBORDO??
3 ciclosT1++;
4 PIR1bits.TMR1IF=0; // limpia desborde
5 if (estado==0){
6 // validar clave
7 if(ciclosT1==10){ // pasaron los 5 segundos
8 limpiarTodo();
9 }
10 }
11 if(estado==1){ // ya esta validada la clave
12 if(ciclosT1==600){ // 5 MINUTOS
13 limpiarTodo();
14 }
15 }
16 }
17
18 }

Si el estado=0, no valido contraseña, el Timer1 se cuentan 10 desbordes del Timer , 5 seg. Si el


estado es 1, se cuentan 600 desbordes del Timer. Cada desborde lo acusa el flag TMR1IF , el cual
es borrado manualmente ya que no se borra solo después de seteado.

Chequear el estado del Timer1

1 bit chequeaEstadoT1(void){
2 if(T1CONbits.TMR1ON==1){
3 return 1;
4 }
5 return 0;
6}

Simplemente chequea si el Timer esta encendido o apagado..

Apaga el Timer1

1 void apagaT1(void){
2 T1CONbits.TMR1ON=0; // enciendo Timer
3}

Limpiar el Timer1

1 void limpiaT1 (void){


2 TMR1H=0x00;
3 TMR1L=0x00;
4 PIR1bits.TMR1IF=0; // Limpia desborde
5 ciclosT1=0;
6}

Lo pone a cero, su cuenta interna actual, pone a cero la bandera de desborde y el contador de
ciclos de espera en 0. Este proceso se realiza al validar un digito presionado.

Control de Salidas
1 void controlSalidas(void){
2 unsigned char resultado=0;
3 if(strcmp(codigo,S1ON)==0){
4 Sal_1=1;
5 Beeper(3,1,1);
6 resultado=1;
7 }
8 if(strcmp(codigo,S1OFF)==0){
9 Sal_1=0;
10 Beeper(3,1,1);
11 resultado=1;
12 }
13 if(strcmp(codigo,S2ON)==0){
14 Sal_2=1;
15 Beeper(3,1,1);
16 resultado=1;
17 }
18 if(strcmp(codigo,S2OFF)==0){
19 Sal_2=0;
20 Beeper(3,1,1);
21 resultado=1;
22 }
23 if(strcmp(codigo,S3ON)==0){
24 Sal_3=1;
25 Beeper(3,1,1);
26 resultado=1;
27 }
28 if(strcmp(codigo,S3OFF)==0){
29 Sal_3=0;
30 Beeper(3,1,1);
31 resultado=1;
32 }
33 if(strcmp(codigo,S4ON)==0){
34 Sal_4=1;
35 Beeper(3,1,1);
36 resultado=1;
37 }
38 if(strcmp(codigo,S4OFF)==0){
39 Sal_4=0;
40 Beeper(3,1,1);
41 resultado=1;
42 }
43 if(strcmp(codigo,S5ON)==0){
44 Sal_5=1;
45 Beeper(3,1,1);
46 resultado=1;
47 }
48 if(strcmp(codigo,S5OFF)==0){
49 Sal_5=0;
50 Beeper(3,1,1);
51 resultado=1;
52 }
53 if(strcmp(codigo,S6ON)==0){
54 Sal_6=1;
55 Beeper(3,1,1);
56 resultado=1;
57 }
58 if(strcmp(codigo,S6OFF)==0){
59 Sal_6=0;
60 Beeper(3,1,1);
61 resultado=1;
62 }
63 if(strcmp(codigo,SALLON)==0){
64 Sal_1=1;
65 Sal_2=1;
66 Sal_3=1;
67 Sal_4=1;
68 Sal_5=1;
69 Sal_6=1;
70 Beeper(3,1,1);
71 resultado=1;
72 }
73 if(strcmp(codigo,SALLOFF)==0){
74 Sal_1=0;
75 Sal_2=0;
76 Sal_3=0;
77 Sal_4=0;
78 Sal_5=0;
79 Sal_6=0;
80 Beeper(3,1,1);
81 resultado=1;
82 }
83 limpiarCodigo();
84 if(resultado==0){ // error de tecleado
85 Beeper(10,1,1);
86 }
87 }

Esta función compara el código cadena de 3 digitos con las respectivas claves. Se Observa que
dentro de esta función se encuentran strcmp() para comparar cadenas , perteneciente a la librería
String.h.

Limpiar código

1 void limpiarCodigo(void){
2 int i;
3 for(i=0;i<4;i++){ // Limpia la entrada de codigos todos a NULL
4 codigo[i]='';
5 }
6}

Esta función lo que hace es resetear la variable cadena código[], con caracteres NULL.

ARMANDO EL PROYECTO DE SOFTWARE COMPLETO


Vamos a comenzar ahora a unir los métodos aislados en una función o método principal que es
el main(). Antes de esto vamos a analizar la rutina de Interrupción que es llamada con cada pulso
DV del DTMF:

Notar que en todo el código , todo lo colocado entre /*…….. */ son comentarios no tenidos
en cuenta por el compilador.
1 void interrupt pulso (void){ // Rutina de Interrupción Flanco ASC RB0/INT
2 /*Beep=1;
3 Delay10KTCYx(50);
4 Beep=0;*/
5 FlagPulsoDTMF=0; // Limpia flag evento Flanco RBO/INT
6 limpiaT1();
7 flagEntrada=1;
8 char caracter[2]={'',''}; // string local que guarda el digito del DTMF recordar ''
9 unsigned char indice; // digito leido de 0 al 12 del puerto
10 contador++;
11 indice=LeerPuerto(); // del 0 al 12
12 caracter[0]=Digito[indice]; // trae el caracter según subindice y lo trnasforma en cadnena
13 strcat(codigo,caracter); // Concatena el caracter leido dentro de la cadena string del codigo[]
14 // para armar la cadena de 3 caracteres
15 if(contador==3){
16 contador=0;
17 if( estado==0){ // contraseña no validada
18 //controlSalidas();
19 if(strcmp(contrasena,codigo)==0){ // Cadenas iguales
20 estado=1;
21 Beeper(1,20,1);
22 limpiarCodigo();
23 }
24 else{ // contraseña invalida
25 Beeper(10,1,1);
26 limpiarTodo();
27 }
28 }
29 else{ // Estado =1
30 controlSalidas();
31 }
32 }
33 }

Lo primero que se hace es limpiar el Flag de interrupción ya definido, esto evita que al salir de esta
rutina se vuelva a ingresar creando un Loop inestable. Luego limpiamos el Timer1 que es el que
cuneta entre digito y digito, seteamos el flagEntrada en ON o 1, que va a indicar al programa
principal que se recibió un dígito. Se crea una variable local char carácter[2] que almacena
temporalmente el carácter recibido. Luego se crea un índice que va a traer el valor en decimal del
código binario leído en el puerto. Después con ese índice entramos como puntero al vector
Digito[] y se lo asignamos a carácter[]. Armamos la cadena concatenando con dicho carácter ,
función strcat(). Si el contador de caracteres es 3, se resetea a cero y se verifica el estado del
equipo. Si el estado es cero, verificamos contraseña con strcmp(). Si el código = contraseña, la
función devuelve 0 y se pasa a estado =1 y se avisa con el dispositivo de audio, caso contrario, si
no es la contraseña, se limpia todo. Si el estado es 1, se va a verificar las claves de control de
salidas.

Programa principal
1 void main(void) {
2 configIO();
3 configIRQ();
4 configT1();
5 limpiarTodo();
6 //Beeper(1,10,1);
7 //on_T1();
8
9 while(1){
10 if(flagEntrada==1){ // Vino un digito
11 //SLEEP();
12 if(chequeaEstadoT1()==0){ // APAGADO
13 on_T1();
14 }
15 chequeaTiempo();// Chequea los tiempos limites
16
17 }
18
19 //limpiarTodo();
20 }
21 return;
22 }

Al arrancar el PIC ejecuta el main() , lo que hace es llama a los métodos de configuración ya vistos
y luego entra en un loop continuo del que nunca sale, salvo cuando se dispara una interrupción.

Si el flagEntrada está activo es que se produzco una interrupción y si el Timer1 no está activado,
lo activa, luego chequea los tiempos y se queda haciendo esto esperando a que se cumpla en
tiempo entre digito y digito o los 5 minutos después de pasar al estado 1.

PROYECTO COMPLETO

1 /*
2 * File: main_dtmf.c
3 * Author: Gustavo
4 *
5 * Created on 20 de febrero de 2015, 17:54
6 */
7
8 #include<string.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include "delays.h" // header externo incluido,cuando se importa una libreria copiada pero no disponible para PIC16
12 // hay que implementar las funciones en el codigo, yo agregue las funciones en los fuentes.c
13 #include "pic16f873.h"
14 #include <xc.h>
15
16 // CONFIG
17 #pragma config FOSC = XT // Oscillator Selection bits (XT oscillator)
18 #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
19 #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
20 #pragma config CP = OFF // FLASH Program Memory Code Protection bits (Code protection off)
21 #pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled)
22 #pragma config LVP = OFF // Low Voltage In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for
23 programming)
24 #pragma config CPD = OFF // Data EE Memory Code Protection (Code Protection off)
25 #pragma config WRT = ON // FLASH Program Memory Write Enable (Unprotected program memory may be written to by EECON
26 control)
27
28 // Declaraciones
29 #define Sal_1 PORTBbits.RB7 // Salida de control 1 con reles
30 #define Sal_2 PORTBbits.RB6 // Salida de control 2
31 #define Sal_3 PORTBbits.RB5 // Salida de control 3
32 #define Sal_4 PORTBbits.RB4 // Salida de control 4
33 #define Sal_5 PORTBbits.RB3 // Salida de control 5 NO SE PUEDE DIRECTO RA4 ES OPEN DRAIN
34 #define Sal_6 PORTBbits.RB2 // Salida de control 6
35 #define Beep PORTBbits.RB1 // Buzer de indicacion sonora
36 #define FlagPulsoDTMF INTCONbits.INTF // Flag de evento RBO/INT salisa DV del DTMF
37
38 // VARIABLES
39 char Digito[14]={'0','1','2','3','4','5','6','7','8','9','0','*','#',''};
40 // Digito[0], no se usa ya que el Digito[10]='0' caracter , '' fin de cadena String
41 char codigo[4]={'','','',''}; // guarda en formato cadena 3 caracteres de entrada + el null de fin ''
42 const char contrasena [4]="#9*"; // Contraseña para operacion del DTMF
43
44 unsigned char estado=0; // Variable que indica el estado del controlador DTMF
45 // 0 contraseña invalida o no recibida
46 // 1 contraseña valida y se pueden operar las salidas
47 // DETECCION DE CODIGO DE DIGITO CONTRA DTMF
48 // PUERTO B entradas RA3=D3, RA2=D2, RA1=D1, RA0=D0 DEL DTMF
49 unsigned int contador=0; // Contador de degitos que van ingresando
50 unsigned char flagEntrada=0; // Indica que vino un puldo del DTMF 0/1
51 unsigned int ciclosT1=0; // ciclos de cuentas Timer1
52
53 // ciclosT1= 10 SERAN 5 SEG, 10 desbordes de T1 a 65536 * 0.1us * 8
54 // ciclosT1= 20 SERAN 10 SEG, 20 desbordes de T1 a 65536 * 0.1us * 8
55
56 char S1ON [4]="11*"; // Codigos de activacion y desactivacion salidas
57 char S1OFF [4]="10*"; // agrega '' al final de cada cadena
58 char S2ON [4]="21*";
59 char S2OFF [4]="20*";
60 char S3ON [4]="31*";
61 char S3OFF [4]="30*";
62 char S4ON [4]="41*";
63 char S4OFF [4]="40*";
64 char S5ON [4]="51*";
65 char S5OFF [4]="50*";
66 char S6ON [4]="61*";
67 char S6OFF [4]="60*";
68 char SALLON[4]="666" ;// ENCIENDE TODAS
69 char SALLOFF[4]="000";// APAGA TODAS
70
71
72 // Funciones
73 void configIO(void); // Configuración de periféricos
74 void configIRQ(void); // Configuración para la interrupción por Flanco ACS RBO/INT
75 void Beeper(int cant, int tpon, int tpof); // Función de indicación sonora
76 // cant=cantidad de beeps, tpon=HIGH de cada beep , tpof= LOW
77 unsigned char LeerPuerto(void); // Lee el puerto del DTMF
78 void limpiarTodo(void); // Inicializa todo nuevamente
79 void configT1(void); // Configuracion del Timer 1 para tiempos muertos
80 void on_T1(void); // Enciende Timer 1
81 void chequeaTiempo(void);// Chequea si se ha superado el tiempo entre digito y digito
82 bit chequeaEstadoT1(void); // consulta si el timer esta encendido o
83 void apagaT1(void); // apaga el timer1
84 void limpiaT1(void); // limpia el Timer1, lo reseta junto con sus ciclos
85 void controlSalidas(void); // controla las salidas
86 void limpiarCodigo(void); // limpia variable de cadena de codigo
87 void interrupt pulso (void){ // Rutina de Interrupción Flanco ASC RB0/INT
88 /*Beep=1;
89 Delay10KTCYx(50);
90 Beep=0;*/
91 FlagPulsoDTMF=0; // Limpia flag evento Flanco RBO/INT
92 limpiaT1();
93 flagEntrada=1;
94 char caracter[2]={'',''}; // string local que guarda el digito del DTMF recordar ''
95 unsigned char indice; // digito leido de 0 al 12 del puerto
96 contador++;
97 indice=LeerPuerto(); // del 0 al 12
98 caracter[0]=Digito[indice]; // trae el caracter según subindice y lo trnasforma en cadnena
99 strcat(codigo,caracter); // Concatena el caracter leido dentro de la cadena string del codigo[]
100 // para armar la cadena de 3 caracteres
101 if(contador==3){
102 contador=0;
103 if( estado==0){ // contraseña no validada
104 //controlSalidas();
105 if(strcmp(contrasena,codigo)==0){ // Cadenas iguales
106 estado=1;
107 Beeper(1,20,1);
108 limpiarCodigo();
109 }
110 else{ // contraseña invalida
111 Beeper(10,1,1);
112 limpiarTodo();
113 }
114 }
115 else{ // Estado =1
116 controlSalidas();
117 }
118 }
119 }
120
121
122 void main(void) {
123 configIO();
124 configIRQ();
125 configT1();
126 limpiarTodo();
127
128 while(1){
129 if(flagEntrada==1){ // Vino un digito
130 if(chequeaEstadoT1()==0){ // APAGADO
131 on_T1();
132 }
133 chequeaTiempo();// Chequea los tiempos limites
134
135 }
136
137 }
138 return;
139 }
140
141
142 void configIO(void){
143 ADCON1=0x06; // A/D desactivados Y PORT A RA0-RA5 como Digital pag 114 datashhet
144 ADCON0bits.ADON=0; //Modulo interno de conversion apagado
145 TRISA =0xFF; // PUERTO A todo entrada para señales DTMF
146 //TRISC=0x00; // PUERTO C salida
147 TRISB=0b00000001; // PUERTO B todo salida a excepcion de RB0/INT
148 PORTB=0x00;
149 Sal_1=0;
150 NOP();
151 Sal_2=0;
152 NOP();
153 Sal_3=0;
154 NOP();
155 Sal_4=0;
156 NOP();
157 Sal_5=0;
158 NOP();
159 Sal_6=0;
160 NOP();
161 Beep=0;
162
163 }
164
165 void configIRQ(void){
166 OPTION_REGbits.INTEDG=1; // configura RB0(PulsoDigito) para IRQ Flanco Ascendente
167 INTCONbits.PEIE=0; // interrupción perifericos desactivada RBO/INT no necesita permiso perifericos
168 // Me aseguro que solo RBO/INT sea la fuente de las interrupciones
169 INTCONbits.RBIE=0; // interrupción por RB0 desactivada para cualquier cambio puerto B
170 INTCONbits.INTE=1; // Habilita IRQ RBO/INT
171 INTCONbits.INTF=0; // Limpia Flag de IRQ es lo mismo que FlagPulsoDTMF=0;
172 INTCONbits.GIE=1; // Habilita interrupciones globales
173
174 }
175
176 void configT1(void){ // Configura Timer 1 para que cuente 5 seg y 10 seg
177 // Configurarlo con prescaller 1:8 T1CKPS1=T1CKPS0=1
178 // Oscilador timer1 externo desactivado T1OSCEN=0
179 // EN FORMA SINDRONICA T1SYNC#=0
180 // Fuente de cuentas interno xtal/4 TMR1CS=0
181 // TMR1ON =1/0, enciende y apaga
182 T1CON=0x30;
183 //T1CONbits.TMR1ON=0; // apagado
184 apagaT1();
185 TMR1H=0x00; // Puesta a cero
186 TMR1L=0x00;
187 // ciclosT1= 10 SERAN 5 SEG, 10 desbordes de T1 a 65536 * 0.1us * 8
188 // tiempo de validacion entre tecla y tecla para la clave Estado=1
189 // ciclosT1= 600 SERAN 300 SEG=5 minutos, 600 desbordes de T1 a 65536 * 0.1us * 8
190 // Una vez entrada la clave permite 5 minutos a partir del ultimo ingreso de tecla
191 // pasados los 5 min vuelve al Estado= 0
192 }
193 void Beeper(int cant, int tpon, int tpof){
194 // TPO MINIMO BASE =0.1 SEG
195 /*
196 10000* 10 * tpon/tpof
197 HIGH= 0.1* tpon
198 LOW= 0.1 * tpof
199 Contraseña invalida : 10 beeps de 0.1 seg Beeper(10,1,1);
200 Digito valido: 1 beep de 0.5 seg no lo usamos
201 contraseña valida : 1 beep de 2 segundos Beeper(1,20,1);
202 salida activada/desactivada: 3 beep de 0.1 seg Beeper(3,1,1);
203
204 */
205 int i;
206 int j;
207 for(i=1;i<=cant;i++){ // la variable unit compilada en la libreria es el
208 // argumento de 10KTCYx y va de 0 a 255 es unsigned char
209 Beep=1;
210 for (j=1;j<=tpon;j++){
211 Delay10KTCYx(10);
212 }
213 for(j=1;j<=tpof;j++){
214 Beep=0;
215 Delay10KTCYx(10);// Toff es siempre el mismo
216 }
217 }
218
219
220 }
221 unsigned char LeerPuerto(void){ // Lee el puerto y devuelve un decimal del digito DTMF
222 unsigned char puerto; // almacena los bits leidos del DTMF
223 unsigned char puertoFinal=0x00; // arma el byte correcto
224 puerto=PORTA; // SOLO INTERESAN RA0-RA3 Lee el puerto
225 puerto&=0x01; // lee D3 MSB
226 puerto<<=3; // QUEDA 0000 D3 000
227 puertoFinal|=puerto; //0000 D3 000
228 puerto=PORTA;
229 puerto&=0x02; // Lee D2
230 puerto<<=1; // queda 000000 D2 00
231 puertoFinal|=puerto; // QUEDA 0000 D3 D2 0 0
232 puerto=PORTA;
233 puerto&=0x04; // Lee D1
234 puerto>>=1; // queda 000000 D1 0
235 puertoFinal|=puerto; // QUEDA 0000 D3 D2 D1 0
236 puerto=PORTA;
237 puerto&=0x08; // Lee D0
238 puerto>>=3; // queda 0000000 D0
239 puertoFinal|=puerto; // QUEDA 0000 D3 D2 D1 D0
240 puertoFinal&=0x0f;
241 //puerto>>=4; // desplaza 4 lugares a la derecha asi se puede usar como indice
242 return puertoFinal;
243 }
244 void limpiarTodo(void){
245 estado=0;
246 contador=0;
247 limpiarCodigo();
248 flagEntrada=0;
249 ciclosT1=0;
250 apagaT1();
251
252 }
253
254
255 void on_T1(void){
256 T1CONbits.TMR1ON=1; // enciendo Timer
257 limpiaT1();
258
259 }
260
261 void apagaT1(void){
262 T1CONbits.TMR1ON=0; // enciendo Timer
263 }
264
265 void chequeaTiempo(void){
266 if(PIR1bits.TMR1IF==1){ // DESBORDO??
267 ciclosT1++;
268 PIR1bits.TMR1IF=0; // limpia desborde
269 if (estado==0){
270 // validar clave
271 if(ciclosT1==10){ // pasaron los 5 segundos
272 limpiarTodo();
273 }
274 }
275 if(estado==1){ // ya esta validada la clave
276 if(ciclosT1==600){ // 5 MINUTOS
277 limpiarTodo();
278 }
279 }
280 }
281
282 }
283 bit chequeaEstadoT1(void){
284 if(T1CONbits.TMR1ON==1){
285 return 1;
286 }
287 return 0;
288 }
289
290 void limpiaT1 (void){
291 TMR1H=0x00;
292 TMR1L=0x00;
293 PIR1bits.TMR1IF=0; // Limpia desborde
294 ciclosT1=0;
295 }
296
297 void limpiarCodigo(void){
298 int i;
299 for(i=0;i<4;i++){ // Limpia la entrada de codigos todos a NULL
300 codigo[i]='';
301 }
302 }
303
304 void controlSalidas(void){
305 unsigned char resultado=0;
306 if(strcmp(codigo,S1ON)==0){
307 Sal_1=1;
308 Beeper(3,1,1);
309 resultado=1;
310 }
311 if(strcmp(codigo,S1OFF)==0){
312 Sal_1=0;
313 Beeper(3,1,1);
314 resultado=1;
315 }
316 if(strcmp(codigo,S2ON)==0){
317 Sal_2=1;
318 Beeper(3,1,1);
319 resultado=1;
320 }
321 if(strcmp(codigo,S2OFF)==0){
322 Sal_2=0;
323 Beeper(3,1,1);
324 resultado=1;
325 }
326 if(strcmp(codigo,S3ON)==0){
327 Sal_3=1;
328 Beeper(3,1,1);
329 resultado=1;
330 }
331 if(strcmp(codigo,S3OFF)==0){
332 Sal_3=0;
333 Beeper(3,1,1);
334 resultado=1;
335 }
336 if(strcmp(codigo,S4ON)==0){
337 Sal_4=1;
338 Beeper(3,1,1);
339 resultado=1;
340 }
341 if(strcmp(codigo,S4OFF)==0){
342 Sal_4=0;
343 Beeper(3,1,1);
344 resultado=1;
345 }
346 if(strcmp(codigo,S5ON)==0){
347 Sal_5=1;
348 Beeper(3,1,1);
349 resultado=1;
350 }
351 if(strcmp(codigo,S5OFF)==0){
352 Sal_5=0;
353 Beeper(3,1,1);
354 resultado=1;
355 }
356 if(strcmp(codigo,S6ON)==0){
357 Sal_6=1;
358 Beeper(3,1,1);
359 resultado=1;
360 }
361 if(strcmp(codigo,S6OFF)==0){
362 Sal_6=0;
363 Beeper(3,1,1);
364 resultado=1;
365 }
366 if(strcmp(codigo,SALLON)==0){
367 Sal_1=1;
368 Sal_2=1;
369 Sal_3=1;
370 Sal_4=1;
371 Sal_5=1;
372 Sal_6=1;
373 Beeper(3,1,1);
374 resultado=1;
375 }
376 if(strcmp(codigo,SALLOFF)==0){
377 Sal_1=0;
378 Sal_2=0;
379 Sal_3=0;
380 Sal_4=0;
381 Sal_5=0;
382 Sal_6=0;
383 Beeper(3,1,1);
384 resultado=1;
385 }
386 limpiarCodigo();
387 if(resultado==0){ // error de tecleado
388 Beeper(10,1,1);
}
}

MÓDULO DRIVER RELÉ


Para manejar los Reles, en lugar de usar los clásicos transistores en emisor común, se utilizó el
ULN2803 que es un driver colector abierto para manejar, en general, cargas inductivas como en
este caso relés de 12 V o motores PAP.

ULN2803 DTMF
Los diodos son de protección del clásico efecto de sobrepico Lenz al desactival la carga inductiva
o bobina de rele. Se produce un sobrepico de tensión que es recortado por los diodos. Para esto
se debe colocar el COM a 12 V que es la tensión de manejo de los reles.

El esquema de Drivers es el siguiente:

ULN2803 DRIVER DTMF

El puerto PORTB del micro se conecta a las entradas del Driver, y sus salidas de esta manera:
Reles DTMF

Juntamente con los relés, se colocan leds indicadores del estado de cada salida. Cuando la salida
de driver va a 0 Volt, se activa el rele asociado y su led.
El esquema completo no se va a visualizar muy bien, pero pueden enviarme un mail y se los paso.

Esquema completo DTMF

Les dejo un video de muestra del equipo funcionando. Es necesario configurar el celular remoto
para respuesta automática de llamada en modo auriculares y con volumen de medio a alto. Espero
les haya gustado

Video DTMF.

También podría gustarte