/****************************************************************

Project        : On-board computer for VW Golf Mk2 1.8RP
Initial Date   : 31-1-2004
Author         : Erik_G2RP                            


Chip type           : ATmega8
On-chip flash       : 8192 bytes (4096 words)
On-chip SRAM        : 1024 bytes
Program type        : Application
Clock frequency     : 8.000000 MHz
Memory model        : Small
External SRAM size  : 0


Compiler            : CodevisionAVR 1.24.6 Standard
Data Stack size     : 256
Optimizer strategy  : Optimize for size


Last revision date  : 11 May 2005
Software revision   : 1.2

/*******************************************************************

This software is copyright, no part of it may be redistributed
or modified without the explicit approval of the author.

/*******************************************************************

1-Wire ADC Connection table:

Channel #       Description
   0		   Battery Voltage
   1               Load current
   2               Battery charging current
   3               Fuel indicator

/******************************************************************/



#include <mega8.h>
#include <delay.h>
#include <stdio.h>
#include <bcd.h>
#include <math.h>
#include <spi.h>
#include <sleep.h>

// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x12
#endasm
#include <lcd.h>

// 1-wire bus functions
#asm
    .equ __w1_port=0x15
    .equ __w1_bit=3
#endasm
#include <1wire.h>

#define ADC_VREF_TYPE 0x40
#define outside  0
#define inside   1
#define oil      2
#define water    3
#define charge   1
#define total    2

#define MAX_DEVICES 4
// Declare your global variables here
// unsigned char rom_codes[MAX_DEVICES,9], devices;

typedef unsigned char rom_code;
flash rom_code               //T1_rom[8]  = { 0x28,0x3e,0x2f,0x66,0x00,0x00,0x00,0xee },
			       T2_rom[8]  = { 0x28,0x3a,0x4c,0x66,0x00,0x00,0x00,0x6d },
			       T3_rom[8]  = { 0x10,0xd3,0x45,0x83,0x00,0x08,0x00,0x7a },
			     //ADC_rom[8] = { 0x20,0x72,0x92,0x04,0x00,0x00,0x00,0x23 };
			       ADC_rom[8] = { 0x20,0x10,0x97,0x04,0x00,0x00,0x00,0x5c };

unsigned int avg[6], avg_cur[6];
unsigned char avg_idx,avg_cur_idx;

unsigned int trip_hour, trip_min;

char minute, hour, day, mon;
char tekst[20];

volatile bit stdby = 1;
volatile bit ovflw;

typedef struct { char name[4]; char days;} month_def;

month_def month[12] = { {"JAN",31},{"FEB",29},{"MAR",31},{"APR",30},{"MAY",31},{"JUN",30},
                        {"JUL",31},{"AUG",31},{"SEP",30},{"OCT",31},{"NOV",30},{"DEC",31} };

typedef unsigned char byte;
/* table for the user defined character
   arrow that points to the top right corner */
flash byte char0[8]={
0b0000010,
0b0000101,
0b0000010,
0b0000000,
0b0000000,
0b0000000,
0b0000000,
0b0000000};

// Define your functions here

interrupt [EXT_INT1] void EXTINT1Handler(void)
{
  PORTB.2=1;
  spi(0x07);
  spi(0x00);
  PORTB.2=0;
  stdby=PINB.1;
}

interrupt [TIM1_OVF] void TIM1OVFHandler(void)
{
  ovflw=1;
}  

unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | ADC_VREF_TYPE;
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}

void w1_activate(rom_code flash *rc)
{
  unsigned char i;
  
  #asm("cli")
  w1_init();
  w1_write(0x55);
  for (i=0;i<8;i++) w1_write(*rc++);
  #asm("sei")
}


unsigned int get_1wire_adc(unsigned char mux_in)
{
  unsigned char adc_lo ,adc_hi;

  #asm("cli")
  
  w1_activate(ADC_rom);
  
  w1_write(0x55);
  
  w1_write(0x08+ (mux_in<<1));
  w1_write(0x00);
  
  w1_write(0x0a);
  w1_read(); w1_read(); w1_read();
  w1_write(0x01);
  w1_read(); w1_read(); w1_read();

  w1_activate(ADC_rom);
 
  w1_write(0x3c);
  w1_write(1<<mux_in);  
  w1_write(0x00);

  w1_read();
  w1_read();
  
  delay_ms(33);
  
  w1_activate(ADC_rom);
 
  w1_write(0xaa);
  w1_write(mux_in<<1);
  w1_write(0x00);
  
  adc_lo = w1_read();
  adc_hi = w1_read();
  
  #asm("sei")  

  return((adc_lo+ ( ((unsigned int)(adc_hi))<<8)>>6));
}


unsigned int read_1wire_adc(unsigned char adc_x)
{
  unsigned int adc_val;
   
  adc_val  = get_1wire_adc(adc_x);
  adc_val += get_1wire_adc(adc_x);
  
  return (adc_val >>1);  
}


void ds18x20_set_9bit(rom_code flash *rc)
{
  w1_activate(rc);
  w1_write(0x4E);
  w1_write(0x00);
  w1_write(0x00);
  w1_write(0x1F);
  w1_init();
}


signed int ds18x20_temp(rom_code flash *rc, char wait_long)
{
 char i=0; 
 char t_lo,t_hi,j;
 
 w1_activate(rc);
 
 w1_write(0x44);       // Convert T  
 
 if (wait_long==1) j=85; else j=10;

 while (++i <= j)
 {
   delay_ms(10);
   if ((PINC.4==1) || (PINC.5==1) || (PINB.1==0)) 
   {
     w1_init();
     return;
   }
 };
 
 w1_activate(rc);
 w1_write(0xBE);       // Read scratch-pad

 t_lo=w1_read();
 t_hi=w1_read();
 w1_init();

 return(t_lo + ((unsigned int)(t_hi)<<8));
}


void show_time_date(void) 
{
  #asm("cli")
  PORTB.2=1;
  spi(0x01);
  minute=spi(0x00);
  hour=bcd2bin(spi(0x00) & 0x3F);
  spi(0x00);
  day=bcd2bin(spi(0x00));
  mon=bcd2bin(spi(0x00));
  PORTB.2=0;

  sprintf(tekst," %2d:%d%d   %2d %s ",hour, minute>>4, minute & 0x0F, day, month[mon-1]);
  lcd_gotoxy(0,0);
  lcd_puts(tekst);
  #asm("sei")
}

void update_time_date(void) 
{
      char t,c;

      lcd_clear();
      lcd_putsf("  SET NEW TIME");
      delay_ms(1000);
      lcd_clear();

      sprintf(tekst," %2d:%d%d   %2d %s",hour, minute>>4, minute & 0x0F, day, month[mon-1]);
      lcd_puts(tekst);

      // set hours
      while (PINC.4==0) {
        t=0; while ((++t<100) && (PINC.5==0) && (PINC.4==0)) delay_ms(4);
        if (PINC.5==1) 
        { if (++hour>23) hour=0;  delay_ms(200); }
        else 
        {
          lcd_gotoxy(0,0);lcd_putsf("   ");
          t=0; while ((++t<100) && (PINC.5==0) && (PINC.4==0)) delay_ms(4);
        };      
        sprintf(tekst," %2d",hour);
        lcd_gotoxy(0,0);lcd_puts(tekst);
      }; 

      //set minutes
      while (PINC.4==1); delay_ms(100);
      while (PINC.4==0) {
        t=0; while ((++t<100) && (PINC.5==0) && (PINC.4==0)) delay_ms(4);
        if (PINC.5==1) 
          { c=bcd2bin(minute); if (++c>59) c=0; minute=bin2bcd(c); delay_ms(200); }
        else 
        {
          lcd_gotoxy(4,0);lcd_putsf("  ");
          t=0; while ((++t<100) && (PINC.5==0) && (PINC.4==0)) delay_ms(4);
        };      
        sprintf(tekst,"%d%d",minute>>4, minute & 0x0F);
        lcd_gotoxy(4,0);lcd_puts(tekst);

      }; 

      //set day
      while (PINC.4==1); delay_ms(100);
      while (PINC.4==0) {
        t=0; while ((++t<100) && (PINC.5==0) && (PINC.4==0)) delay_ms(4);
        if (PINC.5==1) 
          { if (++day>month[mon-1].days) day=1; delay_ms(200); }
        else 
        {
          lcd_gotoxy(1,1);lcd_putsf("  ");
          t=0; while ((++t<100) && (PINC.5==0) && (PINC.4==0)) delay_ms(4);
        };      
        sprintf(tekst,"%2d", day);
        lcd_gotoxy(1,1);lcd_puts(tekst);
      }; 

      //set month
      while (PINC.4==1); delay_ms(100);
      while (PINC.4==0) {
        t=0; while ((++t<100) && (PINC.5==0) && (PINC.4==0)) delay_ms(4);
        if (PINC.5==1) 
          { if (++mon>12) mon=1; delay_ms(200); }
        else 
        {
          lcd_gotoxy(4,1);lcd_putsf("   ");
          t=0; while ((++t<100) && (PINC.5==0) && (PINC.4==0)) delay_ms(4);
        };      
        sprintf(tekst,"%s", month[mon-1]);
        lcd_gotoxy(4,1);lcd_puts(tekst);
      }; 


      while (PINC.4==1);
      lcd_clear();
      delay_ms(1000);
      lcd_putsf("  TIME UPDATED");

      #asm("cli")
      PORTB.2=1;
      spi(0x80);
      spi(0);
      spi(minute);
      spi(bin2bcd(hour));
      PORTB.2=0; delay_ms(1);
      PORTB.2=1;
      spi(0x84);
      spi(bin2bcd(day));
      spi(bin2bcd(mon));
      PORTB.2=0;
      delay_ms(1000);
      lcd_clear();
      delay_ms(1000);
      #asm("sei")
}

void show_temp(char t_sel) 
{
  double T;
  
  lcd_gotoxy(0,0);
  
  if (t_sel==inside) 
  {
    lcd_putsf("TEMP IN: ");
    T=((double)(ds18x20_temp(T3_rom,1)))/2;
  }
  else
  {  
    lcd_putsf("TEMP OUT:");
    T=((double)(ds18x20_temp(T2_rom,0)))/16;
  };
    
  sprintf(tekst,"  %3d C",(signed int)(T));
  lcd_gotoxy(1,1); lcd_puts(tekst);
  lcd_gotoxy(6,1); lcd_putchar(0);
}


void show_temp_2(char t_sel)
{
  double tt,tt2,t_in,vu;
  double adc;
  double R_ntc;
  
  lcd_gotoxy(0,0);
  
  if (t_sel==oil)
  {
    lcd_putsf("OIL TEMP:");
    adc=read_adc(2);
  
    R_ntc = 220.0*(adc/(1024.0-adc));
  }
  else
  { 
    lcd_putsf("WATER:   ");  
    adc=read_adc(1);
    
    //    vu=5.0*(adc/1024.0);
    //    R_ntc = 53.0*(20.0*vu/(91.35-20.0*vu));

    vu=100.0*(adc/1024.0);
    R_ntc = 53.0*(vu/(91.35-vu));
  }
  
  tt=log(R_ntc);
  tt2=tt*tt*tt;
  t_in = (1/( 1.586e-3 + 2.393e-4*tt + 3.539e-7*tt2 ))-273.15;

  sprintf(tekst,"  %3d C",(signed int)(t_in));
  lcd_gotoxy(1,1); lcd_puts(tekst);
  lcd_gotoxy(6,1); lcd_putchar(0);  
}


void show_oil_pressure(void) 
{
  sprintf(tekst,"OIL PRES:  --- B");
  lcd_gotoxy(0,0); lcd_puts(tekst);
}


void show_voltage(void) 
{
  char vl,vr;
  float vbat;

  vbat=((float)(read_1wire_adc(0)))*0.02851;
  
  vl=vbat;
  vr=(float)((vbat-vl)*10);
    
  sprintf(tekst,"VOLTAGE:  %2u.%1u V",vl,vr);
  lcd_gotoxy(0,0);lcd_puts(tekst);
}


void show_current(char cur_mode) 
{
  unsigned int adc16=0;
  char i;
  float cur;
 
  lcd_gotoxy(0,0);

  if (++avg_cur_idx>5) avg_cur_idx=0;
  avg_cur[avg_cur_idx]=read_1wire_adc(cur_mode);
  for (i=0;i<6;i++) adc16+=avg_cur[i];
//  adc16=avg_cur[0]+avg_cur[1]+avg_cur[2];
//  adc16+=avg_cur[3]+avg_cur[4]+avg_cur[5];
  adc16=adc16/6;

  cur=(((float)(adc16)) * 0.24927)-125;

  if (cur_mode==total) {
    lcd_putsf("CURRENT:");
    cur= -cur;
  }
  else
    lcd_putsf("CHARGE: ");
  
  
  sprintf(tekst, "   %3d A", (signed int)(cur));
  
  lcd_gotoxy(0,1); lcd_puts(tekst);  
}


void show_fuel(void)
{
  char fuel,i;
  
  unsigned int adc16=0;

  if (++avg_idx>5) avg_idx=0;
  avg[avg_idx]=read_1wire_adc(3);
  for (i=0;i<6;i++) adc16=avg[i];
//  adc16=avg[0]+avg[1]+avg[2];
//  adc16+=avg[3]+avg[4]+avg[5];
  adc16=adc16/6;
  
  fuel=((float)(940-adc16))/9.3478;

  sprintf(tekst,"FUEL:     %2u LTR",fuel);
  lcd_gotoxy(0,0); lcd_puts(tekst);
}



void show_rpm(void) 
{
  unsigned int t_new, rpm;
  unsigned int rpm_avg[5];
  unsigned char rpm_idx,i;
      
  GICR=0x00;
  MCUCR=0x00;
  GIFR=0x00;
  
  #asm("sei")
  
  lcd_gotoxy(0,0);
  lcd_putsf("ENGINE RPM: ----");

  #define no_break ((PINC.4==0) && (PINC.5==0) && (PINB.1==1))
  
  //while ((PINC.4==0) && (PINC.5==0) && (PINB.1==1))
  while (no_break)
      {
        while ((PINB.0==0) && no_break);
        while ((PINB.0==1) && no_break);         

        TCNT1=54000;
        ovflw=0;      

        while ((PINB.0==0) && no_break);
        while ((PINB.0==1) && no_break);         
        t_new=TCNT1-54000;

        if (!no_break) 
        {
          #asm("cli")
          GICR|=0x80;
          MCUCR=0x02;
          GIFR=0x80;
          #asm("sei")
          return;
        };
         
        while (ovflw==0);
 
        if (++rpm_idx > 4) rpm_idx=0;

        rpm_avg[rpm_idx] = ((long)(125000*30))/(t_new);
        rpm=0;
        for (i=0;i<5;i++) rpm+=rpm_avg[i];
//        rpm=rpm_avg[0]+rpm_avg[1]+rpm_avg[2]+rpm_avg[3]+rpm_avg[4];
        rpm=(rpm/250)*50;

        sprintf(tekst,"%4u",rpm);
        lcd_gotoxy(4,1);lcd_puts(tekst);
      };

  #asm("cli")
  GICR|=0x80;
  MCUCR=0x02;
  GIFR=0x80;
  #asm("sei")
}


void show_trip_time(void)
{
  unsigned int t1, t2;
  char th, tm;
  
  t1=((unsigned int)(bcd2bin(trip_hour & 0x3F))*60)+ (unsigned int)(bcd2bin(trip_min));
  
  #asm("cli")
  PORTB.2=1;
  spi(0x01);
  t2 = (unsigned int)(bcd2bin(spi(0x00)));
  t2 += (unsigned int)(bcd2bin(spi(0x00) &0x3F)) * 60;
  PORTB.2=0;
  #asm("sei")
  
  if (t2<t1) t2+=1440;
  
  th=(char)((t2-t1)/60);
  tm=(char)((t2-t1)%60);
  
  sprintf(tekst,"ON-TIME:   %2d:%d%d", th,(char)(tm/10), tm%10);
  lcd_gotoxy(0,0);lcd_puts(tekst);
}



void define_char(byte flash *pc,byte char_code)
{
  byte i,a;
  a=(char_code<<3) | 0x40;
  for (i=0; i<8; i++) lcd_write_byte(a++,*pc++);
}


/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////


// Place the main function here

void main(void)
{
// Declare your local variables here

unsigned int v;
unsigned char old_s, new_s, menu;
// unsigned char n,m;

// Input/Output Ports initialization
// Port B initialization
// Func0=In Func1=In Func2=In Func3=In Func4=Out Func5=In Func6=In Func7=In 
// State0=T State1=T State2=T State3=T State4=0 State5=T State6=T State7=T 
PORTB=0x00;
DDRB=0b00101100;

// Port C initialization
// Func0=In Func1=In Func2=In Func3=In Func4=In Func5=In Func6=In 
// State0=T State1=T State2=T State3=T State4=T State5=T State6=T 
PORTC=0x00;
DDRC=0b00000000;

// Port D initialization
// Func0=In Func1=In Func2=In Func3=In Func4=In Func5=In Func6=In Func7=In 
// State0=T State1=T State2=T State3=T State4=T State5=T State6=T State7=T 
PORTD=0x00;
DDRD=0x0b00000000;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
TCCR0=0x00;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR1A=0x00;
TCCR1B=0x83;
TCNT1H=0x00;
TCNT1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: On
// INT1 Mode: Low level
GICR|=0x80;
MCUCR=0x02;
GIFR=0x80;

TIMSK=0x04;

#asm("cli")

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
// Analog Comparator Output: Off
ACSR=0x80;
SFIOR=0x00;

// ADC initialization
// ADC Clock frequency: 156.250 kHz
// ADC Voltage Reference: AVCC pin
// ADC High Speed Mode: Off
// ADC Auto Trigger Source: None
ADMUX=ADC_VREF_TYPE;
ADCSRA=0x87;
SFIOR&=0xEF;

// SPI initialization
// SPI Type: Slave
// SPI Clock Rate: 1250.000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: MSB First
SPCR=0b01010111;
SPSR=0x00;

// LCD module initialization
avg_idx=0;

lcd_init(8);
define_char(char0,0x00);
 
// initialize DS1305 Real Time Clock
PORTB.2=1;
spi(0x87);
spi(0x80);
spi(0x80);
spi(0x80);
spi(0x80);
PORTB.2=0;
delay_ms(1);

PORTB.2=1;
spi(0x8F);
spi(0x05);
PORTB.2=0;
delay_ms(10);

PORTB.2=1;
spi(0x01);
trip_min = spi(0x00);
trip_hour = spi(0x00);
PORTB.2=0;

lcd_clear();


/*

devices=w1_search(0xf0,rom_codes);
sprintf(tekst,"%u 1-wire devices",devices);
lcd_gotoxy(0,0);
lcd_puts(tekst);
delay_ms(2000);
lcd_clear();
delay_ms(1000);

for (n=0;n<devices;n++) 
  {
  lcd_gotoxy(0,0);
  for (m=0;m<8;m++)
    {
    if (m==4) lcd_gotoxy(0,1);
    sprintf(tekst,"%2x",rom_codes[n,7-m]);
    lcd_puts(tekst);
    }
  delay_ms(8000);
  lcd_clear();
  delay_ms(200);
};  
lcd_clear();
delay_ms(100);

*/

//ds18x20_set_9bit(T1_rom);
ds18x20_set_9bit(T2_rom);
//ds18x20_set_9bit(T3_rom);

menu=1;


while (1) {
  
 #asm("sei")

 if (PINB.1==0)       
 {
   #asm("cli")
   PORTB.2=1;
   spi(0x00);
   new_s=spi(0x00);
   trip_min=spi(0x00);
   trip_hour=spi(0x00);
   PORTB.2=0;
   #asm("sei")
   
   if (new_s!=old_s) 
   {
     show_time_date();
     old_s=new_s;
   }    
   
   sleep_enable();
   powerdown();          // reduces total power consumption to 1 mA
 }  
 else
 {  
  switch (menu) {
    case  1: show_time_date();      break;
    case  2: show_temp(inside);     break;
    case  3: show_temp(outside);    break;
    case  4: show_temp_2(water);    break;
    case  5: show_temp_2(oil);      break;
    case  6: show_oil_pressure();   break;
    case  7: show_voltage();        break;
    case  8: show_current(total);   break;
    case  9: show_current(charge);  break;
    case 10: show_fuel();           break;
    case 11: show_rpm();            break;
    case 12: show_trip_time();      break;
  };  
  
  #asm("sei")
  
  v=0;
  while ((PINC.4==0) && (PINC.5==0) && (v<25)) 
  {
    v++; 
    delay_ms(20);
  };  
  
  if (PINC.4==1)        // button 2 pressed
  {
    v=0;
    while ((PINC.4==1) && (v<300)) 
    {
      delay_ms(10);
      v++;
    };
    if (v>=300)          // button 2 pressed > 3 seconds
      update_time_date();
    else                // button 2 pressed < 3 seconds 
    {   
      if (++menu>12) menu=1; 
      lcd_clear(); delay_ms(100);
    };
  };       


  if (PINC.5==1)       // button 1 pressed
  {
    while (PINC.5==1);
    if (--menu==0) menu=12;
    lcd_clear(); delay_ms(100);
  
  };  
  
  }
 
 }

} // main
