/* Para utilizar con compilador CCS y el pic16F873A */
#include <16f876.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,PUT
#use delay(clock=20000000,restart_wdt)
#define pedalfreno PIN_C7 //entrada pedal freno,
// a través de una resistencia para limtar la corriente, viene de 12V
// mejor si también se añade un zener de 5V1
#define PWM3    PIN_B5        // controla la pata del transistor MOSFET,
// puede ser necesario una resistencia a +12V si es canal N
// o algo más si es canal P
unsigned long duty3, dutyPWM3; // como la interrupción puede venir en culquier momento, 
                            // y el temporizador son 2 bytes, quizá pille a medias el cambio, 
                            // por eso es necesario usar dos variables para lo mismo, una solo se usa para la interrupción
                            // la otra es la que gastamos "normal"
#define valorposicion 0x200    
// de este valor dependerá la luminosidad cuando no esté pisado el freno
// 0x200 es la mitad de 0x3FF, si el led fuera lineal, se iluminaría la mitad.
// este valor habrá que calcularlo a ojimetro, para que ilumine como "posición"
// teniendo en cuenta que, por ejemplo
// 25% = 0x100
// 50% = 0x200
// 75% = 0x300
// 100% = 0x3FF ó más
 
#INT_TIMER0                    // esta directiva le dice al compilador que se trata de la función que se debe ejecutar cuando 
void overflowTM0(void)        // interrumpa el timer0, es el que marca la frecuencia PWM
{
    if (duty3!= 0)            // si está a cero, nada
    {
        output_high(PWM3);        // si tiene algún valor, activo la salida y empiezo a contar el duty
        set_timer1(dutyPWM3);    // el duty lo fijo aquí
        ArrancaTimer1();        // y el timer (empiezo a contar) aquí
        BorraIRQT1();            // por si se ha metido una interrupción mientras estamos aquí, la borro
        enable_interrupts(INT_TIMER1);    // para que no interrumpa y gaste tiempo, la paramos cada vez, 
    }                            // hay que ponerla en marcha otra ves, rendundante, pues al parar el timer no interrumpirá
} // al salir de la interrupción, el propio compilador borra el flag correspondiente.
    
#INT_TIMER1                // lo mismo que antes, en esta ocasión para el timer1, que marca el duty
void overflowTM1(void)
{
    if (duty3<=0x3FF)    // en realidad no es necesario, por un truco que explico después, 
    {                    // pero si está a tope, no hay que tocar nada
        output_low(PWM3);    // lo pongo a cero, se acabo el duty
        disable_interrupts(INT_TIMER1);    // desactivo la interrupcion del timer, a la espera de un nuevo valor.
        ParaTimer1();        // y lo paro, redundante.
    }
}    // al salir de la interrupción, el flag correspondiente se borra solo.
// estas funciones se pueden hacer también con ordenes en C, pero para que se vea que el ensamblador se puede usar también.
    
void BorraIRQT1(void)    // es una función muy simple, que borra el bit correspondiente al timer 1, en ensamblador
{
#asm
     BCF 0xc, 0        ; Borro la interrupción del timer1
#endasm
}
void ParaTimer1(void)     // en este caso, paro el timer1
{
#asm
    BCF 0x10, 0        ; Paro el Timer1
#endasm
}
void ArrancaTimer1(void)    // y aquí lo arranco
{
#asm
    BSF 0x10, 0        ; Arranco el Timer1
#endasm
}
void set_pwm3_duty(unsigned long duty)
{
    disable_interrupts(GLOBAL);    // esto debe hacerse para que no cambie a medias, al ser dos bytes, no se puede hacer en una instrucción.
    dutyPWM3= (0xFFF0)-(duty3);    // como el timer cuenta hacía arriba, a más valor de duty3, mayor será la resta, y más tarde interrumpirá
    enable_interrupts(GLOBAL);    // esto es para que coincida que a más valor, más luz. 
    //Minimo Valor, 0xFFE0, que asegura que podrá volver antes de que se acabe la cuenta
}
void main (void)
{
    output_low(PWM3);    
    setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);    // Este controla el corte del PWM, 16 bits, con reloj interno
    ParaTimer1();    // Para no activar la salida, será el timer 0 el que la active, si toca
    duty3= 0x00;    // empezamos a 0.
    setup_timer_0(RTCC_INTERNAL | RTCC_DIV_32);    // Este controla el inicio de la cuenta, salen unos 1000 hercios, aunque sea de 8 bits.
                                                // nunca se para.
       enable_interrupts(INT_TIMER1);                
       enable_interrupts(INT_TIMER0);
       enable_interrupts(GLOBAL);
    while (1)
    {
        if (!pedalfreno)
        {
            duty3= 0x3FF; // esto hace que se ilumine a tope,
            //porque no alcanza nunca el valor asignado a valor posición
        }
        else
        {
            duty3= valorposicion; // valor calculado para que se encienda más o menos a medias.
        }        
        set_pwm3_duty(duty3);
    }
//    este bucle se puede mejorar bastante, no es necesario cambiar cada vez el valor de duty, se puede dejar "dormido" 
// con los valores de "posición" y que se despierte cuando se pise el pedal de freno. 
// he aprovechado mucho código de la otra aplicación, por eso está en otro compilador, y tiene nombres diferentes.
}