//************************************************************
// Auther: Luke Skaff
// EET 480 - Senior Project
// Spring 2007
// Automotive engine computer (ECU) diagnostic interface
// Compiled in WinAVR
//************************************************************


//***********************************************************
//	                    Program discription
// Sends Mode 1 command set to ECU
// Stores Mode 1 data
// Cauclates disired values from stored data
// Outputs calculated data to LCD
//***********************************************************

//***********************************************************
// For more information on project visit:
// http://www.lukeskaff.com
//***********************************************************

// Dependent librarys
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <util/delay.h>

#define F_CPU 19660800UL   // CPU crystal speed 19.6608Mhz
#define FOSC 19660800UL    // CPU crystal speed 19.6608Mhz
#define BAUD 8192UL        // 8192 Baud rate



// ------ PORT & PIN Definitions -------
#define   LCD_PORT    PORTC    //8-BIT LCD data lines
#define   RS_PORT     PORTD    //Port RS line in on
#define   RS          7        //Pin # of RS line
#define   E_PORT      PORTD    //Port Enable line in on
#define   E           6        //Pin # of Enable line

#define   RXcontrol   5        //Pin # RX enable transistor

#define   DataM      0xFF 


#define Bit_Set(port,bit_num) (port = port | (0x01 << bit_num))
#define Bit_Clear(port,bit_num) (port = port & ~(0x01 << bit_num))

#define DataStreamSize  63    //Size of receiving data stream
#define CarDataSize     22    //Size of ASCII car data


unsigned char DataStream[DataStreamSize];
#define  Def_Offset     3
#define  DEF_ERROR1     3 + Def_Offset
#define  DEF_ERROR2     4 + Def_Offset
#define  DEF_ERROR3     5 + Def_Offset
#define  DEF_ERROR4     6 + Def_Offset
#define  DEF_ERROR5     7 + Def_Offset
#define  DEF_CTS        8 + Def_Offset
#define  DEF_RPM        11 + Def_Offset
#define  DEF_MPH        14 + Def_Offset
#define  DEF_BLM        20 + Def_Offset
#define  DEF_BLM_CELL   21 + Def_Offset
#define  DEF_STPS       24 + Def_Offset   // Scaled Throttle Position Sensor
#define  DEF_MAP        26 + Def_Offset   // volts = N x (5 / 255)
                                          // KPa = N * .369 + 10.354
#define  DEF_BATT       34 + Def_Offset   // volts = N / 10


unsigned char CarData_ASCII[CarDataSize];
#define  DATA_CTS_2     0  //COOLANT TEMPERATURE celcius 
#define  DATA_CTS_1     1
#define  DATA_TPS_v     2  //TPS volts

#define  DATA_RPM_4     3
#define  DATA_RPM_3     4
#define  DATA_RPM_2     5
#define  DATA_RPM_1     6

#define  DATA_MPH_3     7
#define  DATA_MPH_2     8
#define  DATA_MPH_1     9

#define  DATA_BLM_3     10
#define  DATA_BLM_2     20
#define  DATA_BLM_1     21

#define  DATA_BLM_CELL  11

#define  DATA_TPS_3     12
#define  DATA_TPS_2     13
#define  DATA_TPS_1     14

#define  DATA_MAP_3     15
#define  DATA_MAP_2     16
#define  DATA_MAP_1     17

#define  DATA_BATT_2    18
#define  DATA_BATT_1    19





//ECU trouble codes lookup table
const unsigned char PROGMEM ErrorCode_Table[]=
{
   23,22,21,16,15,14,13,12,
   35,34,33,32,31,26,25,24,
   51,46,45,44,43,42,41,36,
   63,62,61,56,55,54,53,52,
   00,00,00,00,00,66,65,64
};


//Function prototypes
void LCD_Initialize(void);
void LCD_D_Write(unsigned char);
void LCD_I_Write(unsigned char);
void LCD_Delay(void);
void USART_init(void);
void Binary_Out(unsigned char);
void Get_Car_Data(void);
//void HEX_to_ASCII(unsigned int, unsigned char &, unsigned char &, unsigned char &, unsigned char &);


// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
// ------------------------ Program Code Starts Here ----------------------------
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------



//******************************************************************
//* Function: LCD_Initialize                                       *
//* Initialize LCD                                                 *
//******************************************************************
void LCD_Initialize(void)
{
   Bit_Clear(E_PORT,E); // Bring enable pin low

   LCD_I_Write(0x38);   // 8-Bit data transfer mode
   LCD_I_Write(0x0C);   // Turn display on, No cursor, No blinking
   LCD_I_Write(0x01);   // Display clear, Restore to upper left position
   LCD_I_Write(0x06);   // Address counter increment after each display
   LCD_I_Write(0x02);   // Set address counter to zero, move cursor to home
}



//******************************************************************
//* Function: LCD_Write                                            *
//* Output data passed in, in data var. to LCD                     *
//* - Commented out, not used anymore -                            *
//******************************************************************
/*void LCD_Write(unsigned char data, unsigned char select)
{
   Bit_Set(E_PORT,E);

   if(select==0xFF)
      Bit_Set(RS_PORT,RS);   //LCD Data mode
   else
      Bit_Clear(RS_PORT,RS); //LCD Instruction mode

   LCD_PORT=data;

   _delay_ms(1);
   Bit_Clear(E_PORT,E);
   _delay_ms(1);

}
*/

//******************************************************************
//* Function: LCD_D_Write                                          *
//* Output data passed in, in data var. to LCD                     *
//******************************************************************
void LCD_D_Write(unsigned char data)
{
   Bit_Set(E_PORT,E);
   Bit_Set(RS_PORT,RS);   //LCD Data mode
 

   LCD_PORT=data;

   LCD_Delay();
   Bit_Clear(E_PORT,E);
   LCD_Delay();

}

//******************************************************************
//* Function: LCD_I_Write                                          *
//* Output data passed in, in data var. to LCD                     *
//******************************************************************
void LCD_I_Write(unsigned char data)
{
   Bit_Set(E_PORT,E);
   Bit_Clear(RS_PORT,RS); //LCD Instruction mode

   LCD_PORT=data;

   LCD_Delay();
   Bit_Clear(E_PORT,E);
   LCD_Delay();

}


//******************************************************************
//* Function: LCD_Delay                                            *
//* Relay function used for LCD communication                      *
//******************************************************************
void LCD_Delay(void)
{
   _delay_ms(1);
}


//******************************************************************
//* Function: USART_init                                           *
//* Enable and Initialize UART                                     *
//******************************************************************
void USART_init(void)
{
    // Set the USART baudrate registers for 8192, UBRR=149
    UBRR0H = 0;
    UBRR0L = 149; // Set for 4800 for PC testing
                  //255 = 4800 baud
                  //149 = 8192 buad
				  //127 = 9600 baud


	//UCSRnA - USART Control and Status Register A
    // Enable 2x speed change, 0=16 divider 1=8 divider
    /*
	  Bit 7 MSB
	R RXCn  -> USART Receive Complete
	0 TXCn  -> TXCn: USART Transmit Complete
	R UDREn -> USART Data Register Empty
	R FEn   -> Frame Error
	R DORn  -> Data OverRun
	R UPEn  -> USART Parity Error
	0 U2Xn  -> Double the USART Transmission Speed, 0=16 divider 1=8 divider
	0 MPCMn -> Multi-processor Communication Mode
	  Bit 0 LSB
	*/
	UCSR0A = (0<<U2X0);


    // UCSRnB - USART Control and Status Register n B
    // Enable receiver
    /*
	 Bit 7 MSB
	RXCIE0 = 0 Receive Interrupt disabled
	TXCIE0 = 0 Transmit Interrupt disabled
	UDRIE0 = 0 Interupt disabled
	RXEN0  = 1 Receive enabled
	TXEN0  = 1 Transmit enabled
	UCSZ02 = 0 8-bit mode
	RXB80  = 0 9'th RX data bit when using 9-bit UART
	TXB80  = 0 9'th TX data bit when using 9-bit UART
	 Bit 0 LSB
	*/
	UCSR0B =(1<<RXEN0)|(1<<TXEN0);

    
	// UCSRnC - USART Control and Status Register n C
	// Set the USART to asynchronous at 8 bits no parity and 1 stop bits
    /*
	  Bit 7 MSB
	0 UMSEL01 |
	0 UMSEL00 |-> Async
	0 UPM01  |
	0 UPM00  |-> No parity
	0 USBS0 -> 1 stop bits;
	1 UCSZ01  |
	1 UCSZ00  |-> 8-bit
	0 UCPOL0 -> Reciving data sampled on fallign edge
	  Bit 0 LSB
	*/
	UCSR0C =(0<<UMSEL01)|(0<<UMSEL00)|(0<<UPM01)|(0<<UPM00)|(0<<USBS0)|(1<<UCSZ01)|(1<<UCSZ00)|(0<<UCPOL0);
         
}


//******************************************************************
//* Function: Binary_Out                                           *
//* Outputs any passed in variable to LCD in binary                *
//* - Used for debugging -                                         *
//* - Commented out, not used in final working code -              *
//******************************************************************
/*
void Out_Binary(unsigned char data)
{
	for(unsigned char bitn = 8 ; bitn > 0 ; bitn--)
	{
		if( data & ( 1 << (bitn-1) ) )
			LCD_Write(0x31,0xFF); // Output 1 to LCD
		else
			LCD_Write(0x30,0xFF); // Output 0 to LCD
	}
}
*/


//*******************************************************************
//* Function: USART_Receive                                         *
//* Waits for incoming data on USART then returns it to the calling *
//* function                                                        *
//* - Commented out, not used in final working code -               *
//*******************************************************************
/*
unsigned char USART_Receive(void)
{
	// Wait for data to be received 
	while ( !(UCSR0A & (1<<RXC0)) );

	// Get and return received data from buffer
	return UDR0;
}
*/


//******************************************************************
//* Function: USART_Transmit                                       *
//* Transmit data                                                  *
//* datastream array until array is full                           *
//******************************************************************
void USART_Transmit(unsigned char data)
{
	// Wait for empty transmit buffer
	while ( !( UCSR0A & (1<<UDRE0)) );

	// Put data into buffer, sends the data
	UDR0 = data;

	// Wait for empty transmit buffer
	while ( !( UCSR0A & (1<<UDRE0)) );
}


//******************************************************************
//* Function: Get_Car_Data                                         *
//* Waits for incoming data on USART then puts received char into  *
//* datastream array until array is full                           *
//******************************************************************
void Get_Car_Data(void)
{
   unsigned int timeout;

   Bit_Set(PORTD,RXcontrol);  //Lockout recieve pin

   //Mode 1 command set
   USART_Transmit(0xF4);
	USART_Transmit(0x56);
	USART_Transmit(0x01);
	USART_Transmit(0xB5);

   Bit_Clear(PORTD,RXcontrol);  //Open up recieve pin

	for(unsigned char i = 0 ; i < DataStreamSize ; i++)
	{
      timeout=0;  // Reset timeout to zero

		// Wait for data to be received if data not recived in timout period
      // then exit loop
		while( !(UCSR0A & (1<<RXC0) )  && (timeout < 12000) )
      {
         timeout++;
      }
       
		// Get and return received data from buffer
      if(timeout == 12000)
         DataStream[i]=0;     // If timeout load 0 into datastream value
      else
		   DataStream[i]=UDR0;  // Otherwise load recieved value
	}

   Bit_Set(PORTD,RXcontrol);  //Lockout recieve pin
}


//******************************************************************
//* Function: Flash_LEDs                                           *
//* - Used for debugging -                                         *
//* - Commented out, not used in final working code -              *
//******************************************************************
/*
void Flash_LEDs()
{
   for(int i=0 ; i<20 ; i++ )
   {   
      PORTA=0x00;
      _delay_ms(1000);
      PORTA=0xFF;
      _delay_ms(1000);
   }
}
*/

//******************************************************************
//* Function: HEX_to_ASCII                                         *
//* Convert inputed 8-bit HEX data to 4 ANSII digits for LCD       *
//******************************************************************
void HEXtoASCII(unsigned int Hex_Input,unsigned char *digit4, unsigned char *digit3, unsigned char *digit2, unsigned char *digit1)
{   

   // Clear varaibles
    *digit4 = 0x00;
    *digit3 = 0x00;
    *digit2 = 0x00;
    *digit1 = 0x00;

    while(Hex_Input >= 1000)
    {
        Hex_Input=Hex_Input - 1000;
        *digit4 = *digit4 + 0x01; 
    }

    while(Hex_Input >= 100)
    {
        Hex_Input=Hex_Input - 100;
        *digit3 = *digit3 + 0x01; 
    }
    
    while(Hex_Input >= 10)
    {
        Hex_Input=Hex_Input - 10;
        *digit2 = *digit2 + 0x01;
    }
    
    *digit1 = Hex_Input; //remainder 
    
    // Convert to ASCII, OR with 0x30 produces ASCII
    *digit4 = *digit4 | 0x30;
    *digit3 = *digit3 | 0x30;
    *digit2 = *digit2 | 0x30;
    *digit1 = *digit1 | 0x30;
}



//******************************************************************
//* Function: Calculate_CarData                                    *
//* Convert raw data from data stream to real numbers and put in   *
//* ASCII format                                                   *
//******************************************************************
void Calculate_CarData()
{
   unsigned char ignore;
   unsigned int temp;

   // Calculate coolant tempature
   temp = DataStream[DEF_CTS] * 0.75 - 40;
   HEXtoASCII(temp,&ignore, &ignore, &CarData_ASCII[DATA_CTS_2], &CarData_ASCII[DATA_CTS_1]);

   // Calculate RPM
   temp = DataStream[DEF_RPM]*25;
   HEXtoASCII(temp,&CarData_ASCII[DATA_RPM_4], &CarData_ASCII[DATA_RPM_3], &CarData_ASCII[DATA_RPM_2], &CarData_ASCII[DATA_RPM_1]);

   // Calculate MPH
   temp = DataStream[DEF_MPH] / 1;
   HEXtoASCII(temp,&ignore, &CarData_ASCII[DATA_MPH_3], &CarData_ASCII[DATA_MPH_2], &CarData_ASCII[DATA_MPH_1]);

   // Calculate Block Learn Multiplier (BLM)
   temp = DataStream[DEF_BLM];
   HEXtoASCII(temp,&ignore, &CarData_ASCII[DATA_BLM_3], &CarData_ASCII[DATA_BLM_2], &CarData_ASCII[DATA_BLM_1]);

   // Calculate Throttle Position Sensor (TPS) percent
   temp = DataStream[DEF_STPS] / 2.56;
   HEXtoASCII(temp,&ignore, &CarData_ASCII[DATA_TPS_3], &CarData_ASCII[DATA_TPS_2], &CarData_ASCII[DATA_TPS_1]);

   // Calculate Manifold Air Pressure (MAP)
   temp = (DataStream[DEF_MAP] * 0.369) + 10.354;
   HEXtoASCII(temp,&ignore, &CarData_ASCII[DATA_MAP_3], &CarData_ASCII[DATA_MAP_2], &CarData_ASCII[DATA_MAP_1]);

   // Calculate Battery voltage
   temp = DataStream[DEF_BATT] / 10;
   HEXtoASCII(temp,&ignore, &ignore, &CarData_ASCII[DATA_BATT_2], &CarData_ASCII[DATA_BATT_1]);


}


//******************************************************************
//* Function: Output_CarData()                                     *
//* Output calcualted car data and trouble codes to LCD            *
//******************************************************************
void Output_CarData()
{
   unsigned char ignore;
   unsigned int  ErrorCode;
   unsigned char bitn=0;
   unsigned char code_2, code_1;
   unsigned char error_bit, error_byte;

   LCD_I_Write(0x02);   // Set address counter to zero, move cursor to home

   LCD_D_Write('R');
   LCD_D_Write('P');
   LCD_D_Write('M');
   LCD_D_Write(' ');
   LCD_D_Write(CarData_ASCII[DATA_RPM_4]);
   LCD_D_Write(CarData_ASCII[DATA_RPM_3]);
   LCD_D_Write(CarData_ASCII[DATA_RPM_2]);
   LCD_D_Write(CarData_ASCII[DATA_RPM_1]);

   LCD_D_Write(' '); //Space
   LCD_D_Write(' '); //Space
   LCD_D_Write(' '); //Space

   LCD_D_Write('M');
   LCD_D_Write('P');
   LCD_D_Write('H');
   LCD_D_Write(' ');
   LCD_D_Write(CarData_ASCII[DATA_MPH_3]);
   LCD_D_Write(CarData_ASCII[DATA_MPH_2]);
   LCD_D_Write(CarData_ASCII[DATA_MPH_1]);

   LCD_D_Write(' '); //Space
   LCD_D_Write(' '); //Space


   LCD_D_Write('B');
   LCD_D_Write('L');
   LCD_D_Write('M');
   LCD_D_Write(' ');
   LCD_D_Write(CarData_ASCII[DATA_BLM_3]);
   LCD_D_Write(CarData_ASCII[DATA_BLM_2]);
   LCD_D_Write(CarData_ASCII[DATA_BLM_1]);

   LCD_D_Write(' '); //Space
   LCD_D_Write(' '); //Space
   LCD_D_Write(' '); //Space
   LCD_D_Write(' '); //Space

   LCD_D_Write('C');
   LCD_D_Write('T');
   LCD_D_Write('S');
   LCD_D_Write(' ');
   LCD_D_Write(CarData_ASCII[DATA_CTS_2]);
   LCD_D_Write(CarData_ASCII[DATA_CTS_1]);
   LCD_D_Write('C');
   LCD_D_Write(' ');


   LCD_D_Write(' '); //Space

   LCD_D_Write('M');
   LCD_D_Write('A');
   LCD_D_Write('P');
   LCD_D_Write(' ');

   LCD_D_Write(CarData_ASCII[DATA_MAP_3]);
   LCD_D_Write(CarData_ASCII[DATA_MAP_2]);
   LCD_D_Write(CarData_ASCII[DATA_MAP_1]);
   LCD_D_Write(' '); //Space
   LCD_D_Write(' '); //Space
   LCD_D_Write(' '); //Space
   LCD_D_Write(' '); //Space

   LCD_D_Write('T');
   LCD_D_Write('P');
   LCD_D_Write('S');
   LCD_D_Write(' ');
   LCD_D_Write(CarData_ASCII[DATA_TPS_3]);
   LCD_D_Write(CarData_ASCII[DATA_TPS_2]);
   LCD_D_Write(CarData_ASCII[DATA_TPS_1]);
   LCD_D_Write('%');

   LCD_D_Write(' '); //Space

   LCD_D_Write('B');
   LCD_D_Write('A');
   LCD_D_Write('T');
   LCD_D_Write('T');
   LCD_D_Write(' ');

   
   LCD_D_Write(CarData_ASCII[DATA_BATT_2]);
   LCD_D_Write(CarData_ASCII[DATA_BATT_1]);
   LCD_D_Write('V');
   LCD_D_Write(' ');

   LCD_D_Write(' ');
   LCD_D_Write(' ');
   
   error_bit=0;  //Reset error bit to zero


   //*************************
   // Check for Trouble Codes
   //*************************
	for(error_byte = DEF_ERROR1 ; error_byte < DEF_ERROR5 ; error_byte++)
	{
   	for(bitn = 0 ; bitn < 7 ; bitn++)
   	{
         //If error bit in datastream is 1 then ouput trouble code
   		if(DataStream[error_byte] & ( 1 << (bitn) ) )
         {
            ErrorCode = (unsigned char)pgm_read_byte(&ErrorCode_Table[error_bit]);
    
            if(ErrorCode != 0) //If error code is 0 ignore trouble code
            {
               ErrorCode = (unsigned char)pgm_read_byte(&ErrorCode_Table[error_bit]);
               
               HEXtoASCII(ErrorCode,&ignore, &ignore, &code_2, &code_1);
      			LCD_D_Write(code_2);
               LCD_D_Write(code_1);
               LCD_D_Write(' ');
            }
         }

         error_bit = error_bit + 1; //Increment error bit number

   	}
    }

}


//******************************************************************
//* Function: main                                                 *
//* Main control function                                          *
//******************************************************************
int main (void)
{
   DDRA = 0xFF;                                 // Configure PORTA as output
   DDRC = 0xFF;                                 // Configure PORTC as output
   DDRD = (1<<PD1)|(1<<PD5)|(1<<PD6)|(1<<PD7);  // Configure PORTD 

   USART_init();        //Initialize USART
   LCD_Initialize();    //Initialize LCD


   // Loop forever
   while(1)
   {
      Get_Car_Data();
      Calculate_CarData();
      Output_CarData();
   }
  
  while(1)
  ;

}// End of main
