Hola:
Ante todo, aviso, y pido disculpas por ello, de que este mensaje es muy largo, para que se entienda el proyecto.
Tengo poca experiencia con los PIC y he intentado diseñar un circuito que identifique el número del teléfono que nos llama de forma que pueda usar teléfono móvil para llamar al teléfono de mi casa y se encendiera una luz, pero quisiera que la luz solo se encendiera si el que llama soy yo y que NO se encienda si llama cualquier otro.
Tengo contratado el servicio de identificación de llamadas y utilizo un par de pantallas par ello, concretamente el modelo IDI de Telefónica que regalaba hace un tiempo.
El problema es que con estas pantallas se ve el número del que llama,
pero no me sirven para encender una luz al recibir un número concreto.
He estado buscando por Internet y he encontrado varias páginas que
hablan sobre los circuitos para identificar las llamadas (lo que en
inglés se llama “caller id”) y he encontrado varios esquemas, pero están pensando para mostrar el número en la pantalla, pero no para hacer lo que yo quiero.
Aquí hay varios enlaces sobre esto:
http://www.shout.net/~wildixon/callerid/callerid.htmlhttp://matthieu.benoit.free.fr/cidfr.htmhttp://www.ainslie.org.uk/callerid/cli_faq.htmhttp://www.pablin.com.ar/electron/circuito/telefon/callerid/Viendo alguno de estos circuitos, he pensado en usar uno de los
integrados que hay que se conectan a la línea telefónica y entregan las
señales de identificación en forma TTL en serie y después tratarlas con
un PIC para extraer el número del que llama y compararlo con el de mi
móvil y encender la luz en caso de que coincidan.
Mirando por Internet he encontrado otra página interesante sobre este tema:
http://members.kolp.at/clix/Han montado un identificador completo usando un CMX602B, el mismo chip
que he usado yo, pero como micro usan un Atmel AT90S8515 en vez de un
PIC como he utilizado yo.
He adaptado el programa que se usa en ese proyecto para un PIC con el compilador de C de CCS, pero he llegado a un punto en que no avanzo, pues el micro solo captura algunos caracteres (solo dos o tres) y pierde los demás.
Para descartar problemas con el CMX602B, lo configuré para que no usara el “data retiming” y “escupiera” los caracteres según los decodificara, viéndolos en el PC previo paso por un MAX232.
El resultado es que se recibía perfectamente todo el mensaje, con los
bytes de control, fecha, hora y número de teléfono completos, aunque
antes y después del mensaje se colaban algunos caracteres aleatorios, de ruido, pero fácilmente eliminables.
Mirando el montaje aparecido en “Todo electrónica”, me di cuenta de que algunos componentes que rodean al CMX eran diferentes respecto a los que aparecen en el data sheet del fabricante, concretamente los del amplificador operacional que incluye, y que es el que amplifica la señal FSK antes de demodularla. Están ajustados de forma que tenga menos ganancia. Lo probé así y se reciben menos caracteres de “ruido” al principio y final del mensaje, pero los datos útiles se reciben
perfectamente de las dos formas. Así descarté que el problema estuviera aquí. Después he dejado los componentes del montaje de “Todo
electrónica” para que capte menos ruido.
Después vuelvo a montarlo con el PIC y el CMX configurado en modo “data retiming” y pensé que el problema pudiera estar en que el programa del PIC fuera demasiado lento y no fuera capaz de seguir el ritmo que le imponía el CMX, por lo que cambié el PIC por uno más rápido, pues estaba usando un 16F84A con la misma señal de reloj del CMX, concretamente a 3,579545 Mhz. Lo he cambiado por un 16F876A con un cristal de 20 Mhz, pues el 16F84A que tenía es de 4 Mhz.
El resultado es exactamente el mismo, captura los mismos pocos caracteres.
Sospeché que pudiera ser problema de falta de velocidad porque
originalmente el programa era para un Atmel AVR, que como sabréis, son
unas cuatro veces más rápidos que un PIC a la misma frecuencia de reloj, pues ejecutan una instrucción por cada pulso de reloj, mientras que los PIC necesitan cuatro.
Así pues, como mis escasos conocimientos de los PIC no dan para más,
incluyo el listado a continuación por si alguno quiere perder un rato en estudiarlo, por si detecta algún error o se le ocurre alguna idea para mejorarlo.
El montaje es el siguiente:
La pata 10 (MODE) del CMX la he unido a la RB3 del PIC. La pata 11 del
CMX (ZP) la he puesto a masa La pata12 (IRQN) a la RB0, la de interrupción externa. La pata 13 (DET) del CMX no la he usado. La pata
14 (RXCK) va a RB2 y la pata 15 (RXD) a RB1.
En cuanto al programa, en la función main() he hecho un bucle sin fin
que solo hace que cambie de color un LED bicolor conectado a las patas
RB6 y RB7, que puse para asegurarme de que el programa estaba
funcionando. En la pata RB4 he puesto un pulsador para parar el programa y grabar en la EEPROM una serie de variables que he usado para saber hasta donde llega la decodificación.
Después de recibir la llamada, la variable CMX_State me suele alcanzar
el valor 2 y cont alcanza 5, aunque si cambio el retardo al principio de la rutina de interrupción (delay_cycles (

) a un valor muy bajo (0 o 1) o muy alto (mayor de 16) CMX_State solo alcanza 1, o cont solo llega a 2 o 3. En el mejor de los casos, la matriz x solo me captura unas dos “U”, a veces tres, y después caracteres sin sentido, pues sabéis que antes del mensaje principal, se envía una ráfaga de caracteres U (unas 15 o 20).
Como veréis, la mayor parte del trabajo se hace en la rutina de interrupción.
El programa es tal vez demasiado complicado y confuso, pero como ya dije, no lo he hecho yo, sino que lo he “fusilado” del que hay en
http://members.kolp.at/clix/ , limitándome a “traducirlo” de un AVR a
PIC. Es el siguiente:
---------------------------------------------------------------
// Based on Reinhard Kapeller (
http://members.kolp.at/clix/)
#include <16F876A.h>
#fuses HS, NOPROTECT,NOWDT,NOPUT,NOLVP,NOBROWNOUT,NOCPD,NODEBUG,WRT
#use delay (clock=20000000)
#BIT INTEDG = 0x81.6
//Signals of CMX602B - pins of port B of PIC16F876A
#DEFINE IRQN PIN_B0
#DEFINE RXD PIN_B1
#DEFINE RXCK PIN_B2
#DEFINE MODE PIN_B3
#define DTMAX 8 // buffer length for datetime
#define NBMAX 20 // buffer length for number
// CMX602B CMX_States
#define IDLE 0
#define RCV 1
#define RCV_U 2
#define RCV_MSGLEN 3
#define RCV_MSG 4
#define RCV_DTLEN 5
#define RCV_DT 6
#define RCV_NBLEN 7
#define RCV_NB 8
#define RCV_ARLEN 9
#define RCV_AR 10
#define RCV_XLEN 11
#define RCV_DONE 12
#define RCV_ERROR 13
char DateTime[DTMAX];
char Number[NBMAX];
byte CMX_State = IDLE;
byte NBLen;
byte DTLen;
byte MsgLen;
byte MsgPos;
byte BufPos;
byte Checksum;
byte AbsenceR;
byte cont = 0;
byte i, RX_Byte;
char x[8];
void Init()
{
set_tris_a (0);
set_tris_b (0b00010011);
set_tris_c (0);
output_low (MODE);
output_low (RXCK);
ext_int_edge (H_TO_L); //IRQ on falling edge
enable_interrupts (INT_EXT); //enable interrupt (RB0)
enable_interrupts (global);
// CMX_State = IDLE;
}
/* interrupt service routine */
#int_ext
void ext_int_handler(void)
{
if ((CMX_State >= RCV) && (CMX_State < RCV_DONE))
{
/* receive bits */
RX_Byte = 0;
for (i = 8; i > 0; i--)
{
delay_cycles (

;
output_high (RXCK);
RX_Byte >>= 1;
if (input (RXD)) /* get bit */
RX_Byte |= 0x80;
output_low (RXCK);
}
x[cont]=RX_Byte;
cont++;
if (CMX_State >= RCV_MSGLEN)
Checksum += RX_Byte; /* calculate checksum */
if (CMX_State >= RCV_MSG)
{
MsgPos++; /* current message position */
if (MsgPos > MsgLen) /* last byte: checksum */
{
if (Checksum == 0)
CMX_State = RCV_DONE;
else
CMX_State = RCV_ERROR;
output_bit (MODE, 0); /* FSK receive mode off */
}
}
switch (CMX_State) /* process RX_Byte */
{
case RCV:
if (RX_Byte == "U")
{
MsgPos = 0;
DTLen = 0;
NBLen = 0;
AbsenceR = 0;
CMX_State = RCV_U; /* -> RCV_U state */
}
break;
case RCV_U:
if (RX_Byte == 0x80) /* "call setup" message */
{
Checksum = RX_Byte; /* start calculating checksum */
CMX_State = RCV_MSGLEN; /* -> RCV_MSGLEN state */
}
break;
case RCV_MSGLEN:
MsgLen = RX_Byte; /* message length */
CMX_State = RCV_MSG; /* -> RCV_MST state */
break;
case RCV_MSG:
switch (RX_Byte)
{
case 0x01: /* "date/time" parameter */
CMX_State = RCV_DTLEN; /* -> RCV_DTLEN state */
break;
case 0x02: /* number parameter */
CMX_State = RCV_NBLEN; /* -> RCV_NBLEN state */
break;
case 0x04: /* "absence reason" parameter */
CMX_State = RCV_ARLEN; /* -> RCV_ARLEN state */
break;
default: /* unused/unknown parameter */
CMX_State = RCV_XLEN; /* -> RCV_XLEN state */
}
break;
case RCV_DTLEN:
if (RX_Byte == DTMAX) /* valid "date/time" length */
{
DTLen = RX_Byte;
BufPos = 0;
CMX_State = RCV_DT; /* -> RCV_DT state */
}
else
CMX_State = RCV_MSG; /* -> RCV_MSG state */
break;
case RCV_DT:
DateTime[BufPos] = RX_Byte; /* store "date/time" byte */
BufPos++;
if (BufPos >= DTMAX) /* end of "date/time" parameter */
CMX_State = RCV_MSG; /* -> RCV_MSG state */
break;
case RCV_NBLEN:
NBLen = RX_Byte; /* store "RX" length */
BufPos = 0;
CMX_State = RCV_NB; /* -> RCV_NB state */
break;
case RCV_NB:
Number[BufPos] = RX_Byte; /* store "RX" byte */
BufPos++;
if ((BufPos >= NBLen) || (BufPos >= NBMAX))
CMX_State = RCV_MSG; /* end of "RX" parameter -> RCV_MSG state */
break;
case RCV_ARLEN:
if (RX_Byte == 1) /* valid "absence reason" length */
CMX_State = RCV_AR; /* -> RCV_AR state */
else
CMX_State = RCV_MSG; /* -> RCV_MSG state */
break;
case RCV_AR:
AbsenceR = RX_Byte; /* absence reason */
CMX_State = RCV_MSG; /* -> RCV_MSG state */
break;
case RCV_XLEN:
CMX_State = RCV_MSG; /* -> RCV_MSG state */
break;
}
} /* if ((CMX_State >= RCV) && (CMX_State < RCV_DONE)) */
else if (INTEDG) /* rising edge => end of ring indication */
{
delay_ms (2);
output_high (MODE); /* FSK receive mode */
CMX_State = RCV;
disable_interrupts (INT_EXT);
ext_int_edge (H_TO_L); /* IRQ on falling edge */
enable_interrupts (INT_EXT);
}
else /* falling edge => ring indication */
{
disable_interrupts (INT_EXT);
ext_int_edge (L_TO_H); /* IRQ on rising edge */
enable_interrupts (INT_EXT);
}
}
void main()
{
// int i;
Init();
while (input(PIN_B4))
{
output_high (PIN_B7);
output_low (PIN_B6);
delay_ms (250);
output_low (PIN_B7);
output_high (PIN_B6);
delay_ms (250);
}
for (i=0; i<DTMAX; i++)
write_eeprom (i, DateTime
);
write_eeprom (DTMAX, "#");
for (i=0; i<NBMAX; i++)
write_eeprom (DTMAX + 1 + i, Number);
write_eeprom (0x30, CMX_State);
write_eeprom (0x32, cont);
write_eeprom (0x34, RX_Byte);
for (i=0; i<8; i++)
write_eeprom (0x20 + i, x);
}
---------------------------------------------------------------
Salu2 y gracias.