////////////////////////////////////////////////////////////////////////////////
// //
// //
// ESTE PROGRAMA DECODIFICA LAS SEÑALES DE UN CONTROL REMOTO SONY //
// ____________________________________________________________________ //
// //
// //
// Sus funciones son: //
// //
// * Apagado y encendido de una lampara en modo on/off //
// * Regular gradualmente la intensidad de la lampara. //
// //
// //
// Se aprende los botones, regula la intensidad en forma automatica, tiene //
// modo sleep y modo de encendido por dia. //
// //
// La entrada del sensor es el pin A0, el sincronismo es A1 y la lampara //
// es conectada al pin ccp1(GP2). //
// //
// En pin a3 es para seleccionar entre foco normal o ahorrador, el pin A5 //
// es conectado un led indicador. //
// //
// El pin a4 es la entrada de la fotoresistencia para controlar automati- //
// -camente la intensidad. //
// //
// Conectar la fotoresistencia con el divisor de voltaje de tal modo que //
// a mas luz menos voltaje. //
// //
// se corrigieron problemas con foco ahorrador //
// //
////////////////////////////////////////////////////////////////////////////////
#include <12f683.h> // Declaramos pic (se ocupa este por ser el unico de 8 pines con pwm).
#fuses NOWDT, NOPROTECT, BROWNOUT, PUT, NOCPD, NOMCLR, INTRC_IO, NOIESO, NOFCMEN
#device ADC=10
#use delay(clock=1000000) //frecuencia de trabajo de 1Mhz
#use fast_io (A)
byte codigo[2]={0,0},code_up[2]={0,0},code_down[2]={0,0},code_onoff[2]={0,0};
////////////////////////////////////////////////////////////////////////////////
///////// variables de codigo leido del control y codigos de función /////////
////////////////////////////////////////////////////////////////////////////////
int16 tstart=440,ton=250,adc; // Tiempo de bit de start 3T=1800 uS y tiempo de bit 1 2T=1200 uS
int cont1=0,cont2=0; // Contadores de bits leidos
unsigned int disparo=128; // Angulo de disparo por default esta entre 28 y 129 (rango dependiendo de config timer2)
short sincro1=0,on_off=0; // Banderas de ciclo completo, apagar (para antirrebote)
unsigned int aprender=0,ciclos=0,ciclos1=0,hold=0;
// Aprender codigo segun alguna condicion(solo para compatibilidad), ya se han leido los 3 codigos y cargalos a la ram
// Banderas: ciclo completo, ciclos transcurridos(usados para retardos),
// Cuenta cuando se deja presionado la tecla encender apagar,
short modo_auto=0,auto_ini=1; // Bandera para indicar si se activo el modo automatico,bandera que inicializa el modo automatico-*-*-*-*
short modo_time1=0,modo_time2=0; // Banderas para activar modo sleep o encendido por tiempo
int16 base_tiempo=0; // Se usa para llevar la cuenta del tiempo en modo sleep y modo ciclico
short ahorrador=0,edo_ahorrador=0; // Bandera que indica si esta usando foco ahorrador,foco ahorrador prendido o apagado
////////////////////////////////////////////////////////////////////////////////
///////////////////// Rutina de sincronismo con la AC //////////////////////////
////////////////////////////////////////////////////////////////////////////////
void sincroniza(void)
{
if(!input(PIN_A1)) sincro1=1; // Ha pasado ciclo positivo? si = activa bandera
if(sincro1 & input(pin_A1)) // Es un ciclo completo? y esta como bombilla
{
if(!ahorrador) // Si no esta como foco ahorrador sincroniza
{
if(disparo!=28) // Si el angulo no es minimo, <<=== aumentar el valor de angulo minimo cambiado a 28 en lugar de 15
{
set_timer2(disparo); // Actualiza el valor del angulo de disparo
setup_ccp1(CCP_PWM); // Ajusta el sincronismo
}
else
setup_ccp1(CCP_OFF); // Si el angulo de disparo es muy pequeño mejor apaga pwm para que no haya fugas de corriente
}
sincro1=0; // Reiniciar otro ciclo
ciclos++; // Cuenta los ciclos que van (para ser usado en retardo o antirrebotes)
// como el programa esta atendiendo mas de una variable a la vez no se pueden usar retardos o ciclos repetitivos
ciclos1++; // Retardo para la correccion de error en modo automatico y cuenta los ciclos para formar segundo
}
} // Termina la función sincroniza
////////////////////////////////////////////////////////////////////////////////
////////////////////////////// escribir eeprom /////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void wr_eeprom(byte address,byte data) // los datos a ser leidos o escritos en eeprom son convertidos
{ // a variables tipo byte para evitar conflictos y como es lento hay que sincronizar
write_eeprom(address,data);
sincroniza();
}
////////////////////////////////////////////////////////////////////////////////
//////////////////////////////// leer eeprom ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
byte rd_eeprom(byte address)
{
return(read_eeprom(address));
sincroniza();
}
//------------------------------------------------------------------
void carga_code(void) // Carga en ram los codigos una vez
{ // que se tienen listos los 3 codigos
code_up[1]=rd_eeprom(0x12); code_up[0]=rd_eeprom (0x11);
code_down[1]=rd_eeprom(0x14); code_down[0]=rd_eeprom (0x13);
code_onoff[1]=rd_eeprom(0x16); code_onoff[0]=rd_eeprom (0x15);
} //Cierra carga_code
//----------------------------------------------------------------
void guarda_code(void) //guarda en eeprom los 3 codigos de funcion y solo los acepta si son diferentes
{
if(aprender!=0) //si se activo la bandera aprender guarda los codigos
{
if(aprender==1) //guarda codigo para aumentar intensidad
{wr_eeprom(0x11,codigo[0]); wr_eeprom(0x12,codigo[1]);}
if(aprender==2) //guarda codigo para disminuir intensidad y comrara que sea diferente al primero
{
if((codigo[0]!=rd_eeprom (0x11)) | (codigo[1]!=rd_eeprom (0x12)))
{ wr_eeprom(0x13,codigo[0]); wr_eeprom(0x14,codigo[1]); }
else
aprender=1;
}
if(aprender==3) //guarda codigo para on-off y compara que sea diferente al primero y segundo y si ya se leyeron los 3 actualizalos en ram
{
if(((codigo[0]!=rd_eeprom (0x11)) | (codigo[1]!=rd_eeprom (0x12))) & ((codigo[0]!=rd_eeprom (0x13)) | (codigo[1]!=rd_eeprom (0x14))))
{ wr_eeprom(0x15,codigo[0]); wr_eeprom(0x16,codigo[1]); carga_code();}
else
aprender=2;
}
if(aprender>=3) // si ya se han leido 3 diferentes sale y ya no vuelve a entrar a la subrutina
aprender=0;
else
aprender++;
}
} // Cierra guarda_code
void readcodigo(void)
{
//----------------------lee codigo------------------------------- <<-- posible version todas marcas
//muestreando a cada cierto tiempo y guarda en un arreglo grande rotando bits
//aplicando reglas generales posibles problemas de memoria
SET_TIMER1(0);//start =3t t=600uS //inicia a contar timer 1
while(!input(PIN_A0)){sincroniza();} //se espera en lo que dura el bit de start y esta sincronizando
if( GET_TIMER1() < tstart ) //checa si el tiempo corresponde al bit de start y si no se sale
goto salir;
codigo[0]=0; //inicializar codigo proveniente del control
codigo[1]=0;
for(cont1=1;cont1<=12;cont1++) //lee los 12 bit de codigo (norma RECS80 de sony)
{
while(input(PIN_A0)){sincroniza();} //se mide el tiempo de cada transicion y verifica si es 1 o 0 segun su duracion
SET_TIMER1(0);
while(!input(PIN_A0)){sincroniza();}//el tiempo que interesa el el tiempo en bajo (si dura t o 2t)
if(GET_TIMER1()>ton)
shift_left(codigo,2,1);//rota 1 posicion e inserta 1 si bit=1 trabaja con 2 bytes
else
shift_left(codigo,2,0);//rota 1 posicion e inserta 0 si bit=0 trabaja con 2 bytes
}
//-------------------compara codigo--------------------------------//ejecuta la accion segun el codigo recibido
if((code_up[0]==codigo[0]) & (code_up[1]==codigo[1])) {if(disparo<129 & input(PIN_A3)) {ahorrador=0; disparo++;}//aumentar intensidad solo si esta en modo lamp incandescente
if(auto_ini==0){modo_auto=0; auto_ini=1; modo_time1=1; output_low(pin_a2); delay_ms(500); if(input(PIN_A3))disparo=128; else{ setup_ccp1(CCP_OFF); ahorrador=1; output_high(PIN_A2); } }
}
//ademas si se estaba en modo auto o modo time se sale y toma en cuenta el tipo de foco para encenderlo (inicia cuenta de tiempo)
if((code_down[0]==codigo[0]) & (code_down[1]==codigo[1])) {if(disparo>28 & input(PIN_A3)) {disparo--; ahorrador=0;}//disminuir intensidad solo si esta en modo lamp incandescente
if(auto_ini==0){modo_auto=0; auto_ini=1; modo_time2=1; output_low(pin_a2); delay_ms(500); if(input(PIN_A3))disparo=128; else{ setup_ccp1(CCP_OFF); ahorrador=1; output_high(PIN_A2); } }
}
//ademas si se estaba en modo auto se sale y entra a modo sleep y toma en cuenta el tipo de foco para encenderlo (inicia cuenta de tiempo)
if((code_onoff[0]==codigo[0]) & (code_onoff[1]==codigo[1])) {on_off=1; ciclos=0;//apagar-encender
if(auto_ini==0){modo_auto=0; auto_ini=1; } modo_time1=0; modo_time2=0; base_tiempo=0; output_high(PIN_A5); }
//ademas si se estaba en modo auto, sleep o ciclico se sale y apaga led
guarda_code();// si no se han grabado en eeprom los codigos se ejecuta la subrutina
//-------------------------borra codigo-----------------------------
salir:
set_timer1(0);
codigo[0]=0;
codigo[1]=0;
}//read codigo
//-------------interrupcion para activar el control automatico---------
#INT_TIMER0
control_auto(void) // <<=== aumentar tiempo (no es posible con timer0 ya tiene maximo prescaler)
{
if(on_off) //si se mantiene presionada la tecla on-off cuenta
{
hold++;
if(hold>12) //si se mantuvo presionada la tecla on-off aprox 3 seg o mas se activa el modo automatico
{modo_auto=1; auto_ini=1;}
}
else
{
hold=0; //al soltar la tecla on-off se limpia la variable hold
}
}
//--------------------------------inicio----------------------------------
main(void)
{
setup_oscillator( OSC_1MHZ | OSC_INTRC);//se configura oscilador rc interno sin salida de señal al exterior a 1Mhz
set_tris_a(0x1B); //gp4 in gp1 in gp0 in gp3 in
output_low(PIN_A5); //encendemos led de espera de encendido(logica invertida)
output_low(PIN_A2); //primeros segundos foco apagado
delay_ms(800);
output_high(PIN_A5);
//-------------inicializaciones--------------------------------------------
setup_adc(ADC_CLOCK_INTERNAL); // usamos adc
setup_adc_ports(sAN3); //a4 al adc para leer la fotoresistencia(pin a4 pero canal analogo 3)
set_adc_channel(3); //a4 seleccionado
setup_timer_0 (RTCC_DIV_256|RTCC_INTERNAL); //timer 0 se desborda aprox cada .2 seg para usarlo en hold (interrupcion)
setup_timer_1 ( T1_INTERNAL | T1_DIV_BY_1); //timer 1 se incrementa cada 4 uS
set_rtcc(0);
disable_interrupts(INT_EXT); // \
disable_interrupts(INT_TIMER1); // |
disable_interrupts(INT_TIMER2); // |
disable_interrupts(INT_RA); // > no utilizamos estas interrupciones-*-*-
disable_interrupts(INT_CCP1); // |
disable_interrupts(INT_COMP); // /
enable_interrupts(INT_TIMER0);// usamos estas interrupciones
enable_interrupts(GLOBAL);// habilitamos en general las interrupciones
setup_comparator(NC_NC_NC_NC); //comparador analogico apagado
setup_timer_2(T2_DIV_BY_16, 130, 1); //timer 2 configurado para pwm a una frecuencia de 120 Hz
//-------------------comprobacion para ver si se queren cambiar los codigos de funcion-----------------
if(rd_eeprom(0x17)==0x01) //si apago y encendio dos veces el modulo aprende codigos y enciende a media intensidad
{
aprender=1; //activamos bandera de guardar codigos de funcion
if(input(PIN_A3)) //si esta como bombilla
disparo=65; //angulo de disparo por default (variable que se carga a timer 2 al sincronizar y es el
//numero desde que empieza a contar timer2 y de acuerdo a la configuracion de setup_timer2 se
//incrementa cada cierto tiempo y a desbordarse es cuando pwm genera un pulso en alto o sea duty(1) )
else //si esta como foco ahorrador
{
output_high(PIN_A2); //enciende y apaga 2 veces
delay_ms(200);
output_low(PIN_A2);
delay_ms(200);
output_high(PIN_A2);
delay_ms(200);
output_low(PIN_A2);
}
}
else //si no aprendes codigos entonces prende a maxima intensidad segun tipo de foco
disparo=128;
wr_eeprom(0x17,0x01);//activamos bandera en eeprom de encendido del modulo
delay_ms(1200); //si pasa 1 segundo y no apagamos el modulo quiere decir que no queremos que se aprenda codigos nuevos
wr_eeprom(0x17,0x00);//desactivamos bandera en eeprom de encendido del modulo
set_pwm1_duty(1); //ancho del pulso en alto para disparo, solo un pequeño pulso en alto
if(!input(PIN_A3))
{
setup_ccp1(CCP_OFF);
output_high(PIN_A2);
ahorrador=1;
}
carga_code(); //cargamos codigos de funcion de eeprom
//----------------------------------------------------------------------------------
while(TRUE) //ciclo principal
{
sincroniza(); //la mayor parte de tiempo esta sincronizando
if(modo_time1)//si fue activado el modo sleep permanece activado 1 o 5 min y se apaga
if(ciclos>=60)
{
ciclos=0; base_tiempo++; output_toggle(PIN_A5); if(base_tiempo==60){disparo=28; base_tiempo=0; modo_time1=0; if(input(PIN_A3)) ahorrador=0; else{ ahorrador=1; setup_ccp1(CCP_OFF); output_low(PIN_A2); } }
}
//verifica que se ha cumplido la base de tiempo (1 seg) y al cumplirse 60 seg se apaga y ya no entra a la rutina ya sea como foco ahorrador o normal
if(modo_time2)//si fue activado el modo de encendido temporal permanece activado 3 hrs y se apaga y al dia sig otras 3 horas etc
if(ciclos>=120)
{
ciclos=0; base_tiempo++; output_toggle(PIN_A5); if(base_tiempo<5){ if(input(PIN_A3)){disparo=128; ahorrador=0;}else{ahorrador=1; setup_ccp1(CCP_OFF); output_high(PIN_A2); ahorrador=1;} } else{ if(input(PIN_A3)){disparo=28;ahorrador=0;}else{setup_ccp1(CCP_OFF);output_low(PIN_A2);ahorrador=1;} }if(base_tiempo==60) base_tiempo=0;
}
//verifica que se ha cumplido la base de tiempo (2 seg) y al cmplirse 3 hrs se apaga y al cumplirse 24 vuelve a iniciar ya sea como foco ahorrador o normal
if(!input(PIN_A0)) readcodigo(); //si hay señal de mando ir, decodificalo
if(on_off) //retardo para evitar rebotes en la funcion on off (la comparacion on_off fue cambiada en esta posicion para ocupar menos ciclos maquina)
if(ciclos==45)
{
if(input(PIN_A3))
{
ahorrador=0; if(disparo==28) disparo=128; else disparo=28;
}
else
{
ahorrador=1; setup_ccp1(CCP_OFF); if(edo_ahorrador){output_low(PIN_A2); edo_ahorrador=0;} else{output_high(PIN_A2); edo_ahorrador=1;}//<<--problemas al usar output_toggle();
}
codigo[0]=0; codigo[1]=0; on_off=0; ciclos=0; hold=0;
}
//si bandera on_off fue activada apaga o prende respetando el tipo de foco
//on_off
sincroniza();//se agrega otro sincroniza para aumentar la prioridad de la tarea
if(modo_auto) //si fue activado el modo control automatico de iluminacion-*-*-*-**
{
if(auto_ini)// si es apenas activado el modo automatico-*-*-*-*
{
setup_ccp1(CCP_OFF);//apagamos temporalmente el pwm
output_high(PIN_A2); //enciende y apaga
delay_ms(300);
output_low(PIN_A2);
delay_ms(300);
output_high(PIN_A2);
delay_ms(300);
output_low(PIN_A2);
delay_ms(300);
output_high(PIN_A2);
delay_ms(300);
output_low(PIN_A2);
delay_ms(300);
on_off=0;
auto_ini=0;//ya no entres a esta condicion hasta que se vuelva a activar modo auto--*-*-*-*-*
disparo=28;
}
if(input(PIN_A3)) //si esta como foco normal
{
if(ciclos1>10)//esperamos hasta que se cumpla un tiempo <<=== aumentar tiempo (fue cambiado de 8 a 10)
{
ahorrador=0;
adc=read_adc();//leemos la intensidad luminosa por medio de fotorresistencia
sincroniza();
if(adc<600 & disparo>28) disparo--; //si hay mucha luz disminuyela y espera cierto tiempo <====simplificar (ya se simplifico)
if(adc>610 & disparo<128) disparo++; //si hay poca luz aumentala y espera cierto tiempo (se usa histeresis para evitar inestabilidad)
//en estas dos condiciones anteriores se limita el valor al mismo tiempo y el set point esta en ese rango
ciclos1=0;
}
}
else //si esta como foco ahorrador
{
adc=read_adc();//leemos la intensidad luminosa
sincroniza();
if(read_adc()>980) {setup_ccp1(CCP_OFF); output_high(PIN_A2); ahorrador=1; }
if(read_adc()<200) {setup_ccp1(CCP_OFF); output_low(PIN_A2); ahorrador=1; } // Prende la lampara al 100% cuando obscurece
} // a cierto nivel (on-off con histeresis).
} // Modo auto.
} // Cierra while true.
} // Cierra main.