//***************************************************************************
//******
//****** Digital Transistorized Ignition (TCI version). SPORTDEVICES SEP/29/2007
//******
//****** NOTE: use compiler: PCW v2.619
//******
//****** Digital parts: PIC16F84a, XTAL 4MHz
//****** Complete schematic: www.sportdevices.com/ignition/ignition_TCI_84.htm
//****** To ask any question: jandani@sportdevices.com
//******
//******
//****************************************************************************

#include <16F84a.H>
#fuses XT,NOWDT,PUT
#use fast_io(a)
#use delay(clock=4000000)
#define pickup    PIN_B0  //pin 6
#define coil      PIN_A2  //pin 1
#define LED       PIN_A4  //pin 1
#use rs232(baud=19200, xmit=PIN_A3, rcv=PIN_A1)


//setup values
#define prescaler        7     //2^7=128
#define dwell_time       4000  //us
#define delay_multiple   8     //8 us, min 3 us
#define low_rpm_pulse    true  //does it generate pulses at low rpm?
#define low_rpm_multiple 40    //us, to calculate degrees at low rpm (8 * 2.5 = 40)
#define silent_time      1000  //silent time after spark to avoid false triggers
#define pulse_polarity   0     //0-activates low
#define min_rpm          200   //min rpm value, if lower the program starts again
#define min_period       31
//********
#define max_period       (60000000/128/min_rpm) //(2^prescaler)

const byte ignition0[64]={
15,15,16,16,17,17,18,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,49,75,101,106,108,110,112,
114,116,118,119,121,123,125,127,129,131,133,135,137,138,140,142,144,146,148,150,152,154,156,157,159,161,163,165,167,169,
171,173,174,176,182};


#byte TMR0=0x01
#byte STATUS=0x03
#byte PORTA=0x05
#byte PORTB=0x06
#byte INTCON=0x0b
#byte OPTION_REG=0x81

#define perl make8(per,0)
#define perh make8(per,1)

int tmr0h,del;
int16 per,per_ant,dwell,per_tmp;
boolean first=true,second=true;

#byte W_TEMP=0x7f
int STATUS_TEMP;

#int_global
void int_tmr0()
{
//PUSH
   #asm
	movwf	W_TEMP		//store W and STATUS
	swapf	STATUS,w
	movwf	STATUS_TEMP
   #endasm

   if(tmr0h<0xff) tmr0h++;

   bit_clear(INTCON,2);
   if(tmr0h>(max_period/256+1))
   {
     //timeout is needed to prevent coil to burnout
     first=true;
     output_low(coil); //turn off the coil (residual spark will result)
   }

//POP ****************************
  #asm
  swapf	STATUS_TEMP,W
  MOVWF	STATUS
  SWAPF	W_TEMP,F
  SWAPF	W_TEMP,W
  #endasm
}

void check_dwelltime()
{
  if (!input(coil)) //is activated yet, if so-> don't waste time
  {
    if(tmr0h<max_period/256)
    {
      per=make16(tmr0h,TMR0);
      if(per>dwell) output_high(coil); //start dwell time now
    }
  }
}

void wait_pulse()
{

  #if pulse_polarity==0
    while (!input(pickup));  //wait line goes high
    while (input(pickup)) check_dwelltime();   //wait line goes low
  #else
    while (input(pickup));   //wait line goes low
    while (!input(pickup)) check_dwelltime();  //wait line goes high
  #endif

  per=make16(tmr0h,TMR0);
  TMR0=0; tmr0h=0;
}

void main(){

   INTCON=0xa0; //T0IE=1
   OPTION_REG=prescaler-1;  //PULLUP on, 2^prescaler=128
   set_tris_a(0x01);
   set_tris_b(0x01); //PIN B0 input (curve selector)

#ifdef LED
   output_low(LED);
   delay_ms(50);
   output_high(LED);
   delay_ms(100);
#endif

   dwell=0xffff;  //first time

   while (true)
   {

      //first pulse is not processed because the period can not be calculated until the second pulse
      if(first)  { wait_pulse(); first=false; second=true;}

      wait_pulse();
      if(second) {per_ant=per; second=false;}

      output_high(coil); //start dwell time (if not started yet)

      if ((per>=min_period)&&(per<min_period+sizeof(ignition0)))
      {
        del=perl-min_period;
        #ifdef curve_sel
          if(input(curve_sel))
            del=ignition1[del];
          else
        #endif
          del=ignition0[del];

        del=del-8; //delay adjust (substract 8*4=32 us)
        //do delay
        #asm
 bucle: #endasm
        delay_cycles(delay_multiple-3);
        #asm
        decfsz &del,f
        goto bucle
        #endasm
      }

    else
    {
      if (per>=max_period) first=true;  //if rpm is too slow, engine is stoped

#if low_rpm_pulse
      else
      if (per>=min_period+sizeof(ignition0))
      {
        per_tmp=per;
        #asm
        bcf    03,0
        incf    &per_tmp,F
        incf    &per_tmp+1,F
loop2:  #endasm
        delay_cycles(delay_multiple-3);
        #asm
        decfsz &per_tmp,f
        goto loop2
        decfsz &per_tmp+1,f
        goto loop2
        #endasm

      }
#endif
   }

   output_low(coil); //fire or turn off the coil

   dwell=per-(per_ant-per)-(dwell_time/128); //per - increment - dwell time
   per_ant=per;

   per=make16(tmr0h,TMR0);
   dwell=dwell+per; //consider the delay from ref

   delay_us(silent_time); //time after spark to avoid false triggers
   }  //while
} //main




