AVR to Arduino - VPW

Ecu Hardware Modifications
User avatar
Tazzi
Posts: 3422
Joined: Thu May 17, 2012 8:53 pm
cars: VE SS Ute
Location: WA
Contact:

Re: AVR to Arduino - VPW

Post by Tazzi »

Yeah spot on, I read that in the elm327 diagrams pdf. That 317L adjustable voltage regulator allows the change between 5v and 8v.
Your Local Aussie Reverse Engineer
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Image
tek1229
Posts: 15
Joined: Thu Jun 11, 2015 11:53 am

Re: AVR to Arduino - VPW

Post by tek1229 »

I'll have to try and dig up some files, any interest in this still?? I converted Michael's code years ago for arduino for VPW back when I had my 04 S10.. For transmitting I think I just used 1 transistor and maybe a zener diode, nothing fancy and no other 8 volt regulator.. Used it to control the electric fans I installed.. Would poll for VSS,CTS, and whether a/c was requested and then control the fans..
Let me know if there's any interest, I'll find the code, sounds like you have it figured out though..
User avatar
antus
Site Admin
Posts: 8237
Joined: Sat Feb 28, 2009 8:34 pm
cars: TX Gemini 2L Twincam
TX Gemini SR20 18psi
Datsun 1200 Ute
Subaru Blitzen '06 EZ30 4th gen, 3.0R Spec B
Contact:

Re: AVR to Arduino - VPW

Post by antus »

Please do post it, i would be interested in giving it a try.
Have you read the FAQ? For lots of information and links to significant threads see here: http://pcmhacking.net/forums/viewtopic.php?f=7&t=1396
tek1229
Posts: 15
Joined: Thu Jun 11, 2015 11:53 am

Re: AVR to Arduino - VPW

Post by tek1229 »

Here goes... I did a lot of searching, found some files.. Posting one of them now, can I just post a link to my drop box folder with the files in it? be a lot easier.. I did try the file below, seems to be an issue with the serial communication, might be one of my first tries, or it might be because I was using an atmega 168 and a much older arduino version? anyways, take a look..




Code: Select all

/*************************************************************************  
** Code is modified to run on an Arduino and is shamelessly copied/borrowed/ported over from...
**  AVR J1850 VPW Interface  
**  by Michael Wolf  
**  
**  Released under GNU GENERAL PUBLIC LICENSE  
**  
**  contact: webmaster@mictronics.de  
**  homepage: www.mictronics.de  
**  
**  Revision History  
**  
**  when         what  who          why  
**  31/12/04         v1.00 Michael  Initial release  
**  07/01/05     v1.01 Michael  * changed timeout in j1850_recv_msg() to 4ms  
**                                use an external timeout loop to call the function  
**                                25 times maximum to get the requirment of 100ms  
**  05/05/05     v1.03 Michael  * changed integer types  
**  08/05/05     v1.04 Michael  * changed to use Timer1  
**  11/08/05     v1.05 Michael  * changed EOD to EOF after last databyte send  
**  
**  NOTE:  
**  This file is based on code from Bruce D. Lightner.  
**  (lightner AT lightner DOT net)  
**  This code is part of his project at  
**  http://www.circuitcellar.com/avr2004/first.html  
**  The code is modified and reworked to remove all "GOTO's" and   
**  deprecated macros to be compatible with the latest version of WinAVR.  
**************************************************************************/   
#include <stdint.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <ctype.h>
#define MCU_XTAL 16000000


/*** CONFIG START ***/

#define J1850_PORT_OUT	PORTC	// J1850 output port
#define J1850_DIR_OUT 	DDRC	// J1850 direction register
#define J1850_PIN_OUT		3	// J1850 output pin

#define J1850_PORT_IN		PINC	// J1850 input port
#define J1850_PULLUP_IN	PORTC	// J1850 pull-up register
#define J1850_DIR_IN 		DDRC	// J1850 direction register
#define J1850_PIN_IN		0	// J1850 input pin analog input 0
#define j1850_active() J1850_PORT_OUT |= _BV(J1850_PIN_OUT)
#define j1850_passive() J1850_PORT_OUT &=~ _BV(J1850_PIN_OUT)
#define is_j1850_active() bit_is_clear(J1850_PORT_IN, J1850_PIN_IN)

/* Define Timer1 Prescaler here */
#define c_start_pulse_timer	0x01  // Timer1 runs without Prescaler, 135ns tick @ 7,3728MHz
#define c_stop_pulse_timer	0x00  // Timer1 runs no prescaler, 292ns ticks @ 16,000,000MHz


// define error return codes
#define J1850_RETURN_CODE_UNKNOWN    0
#define J1850_RETURN_CODE_OK         1
#define J1850_RETURN_CODE_BUS_BUSY   2
#define J1850_RETURN_CODE_BUS_ERROR  3
#define J1850_RETURN_CODE_DATA_ERROR 4
#define J1850_RETURN_CODE_NO_DATA    5
#define J1850_RETURN_CODE_DATA       6
// convert microseconds to counter values
#define us2cnt(us) ((unsigned int)((unsigned long)(us) / (1000000L / (float)((unsigned long)MCU_XTAL / 1L))))
#define WAIT_100us	us2cnt(100)		// 100us, used to count 100ms
// define J1850 VPW timing requirements in accordance with SAE J1850 standard
// all pulse width times in us
// transmitting pulse width
#define TX_SHORT	us2cnt(64)		// Short pulse nominal time
#define TX_LONG		us2cnt(128)		// Long pulse nominal time
#define TX_SOF		us2cnt(200)		// Start Of Frame nominal time
#define TX_EOD		us2cnt(200)		// End Of Data nominal time
#define TX_EOF		us2cnt(280)		// End Of Frame nominal time
#define TX_BRK		us2cnt(300)		// Break nominal time
#define TX_IFS		us2cnt(300)		// Inter Frame Separation nominal time
// see SAE J1850 chapter 6.6.2.5 for preferred use of In Frame Respond/Normalization pulse
#define TX_IFR_SHORT_CRC	us2cnt(64)	// short In Frame Respond, IFR contain CRC
#define TX_IFR_LONG_NOCRC us2cnt(128)	// long In Frame Respond, IFR contain no CRC
// receiving pulse width
#define RX_SHORT_MIN	us2cnt(34)	// minimum short pulse time
#define RX_SHORT_MAX	us2cnt(96)	// maximum short pulse time
#define RX_LONG_MIN		us2cnt(96)	// minimum long pulse time
#define RX_LONG_MAX		us2cnt(163)	// maximum long pulse time
#define RX_SOF_MIN		us2cnt(163)	// minimum start of frame time
#define RX_SOF_MAX		us2cnt(239)	// maximum start of frame time
#define RX_EOD_MIN		us2cnt(163)	// minimum end of data time
#define RX_EOD_MAX		us2cnt(239)	// maximum end of data time
#define RX_EOF_MIN		us2cnt(239)	// minimum end of frame time, ends at minimum IFS
#define RX_BRK_MIN		us2cnt(239)	// minimum break time
#define RX_IFS_MIN		us2cnt(280)	// minimum inter frame separation time, ends at next SOF
// see chapter 6.6.2.5 for preferred use of In Frame Respond/Normalization pulse
#define RX_IFR_SHORT_MIN	us2cnt(34)	// minimum short in frame respond pulse time
#define RX_IFR_SHORT_MAX	us2cnt(96)	// maximum short in frame respond pulse time
#define RX_IFR_LONG_MIN		us2cnt(96)	// minimum long in frame respond pulse time
#define RX_IFR_LONG_MAX		us2cnt(163)	// maximum long in frame respond pulse time
#define BAUD_RATE    38400

// J1850 message (max 12 byte - 3 byte header - 1 CRC byte) x 2
// because of 2 ASCII chars/byte + 1 terminator
// or 10 bytes for AT command
#define SERIAL_MSG_BUF_SIZE	18
const char ident_txt[]      PROGMEM = "ELM322 v2.0\r\n";
const char bus_busy_txt[]   PROGMEM = "BUSBUSY\r\n";
const char bus_error_txt[]  PROGMEM = "BUSERROR\r\n";
const char data_error_txt[] PROGMEM = "<DATAERROR\r\n";
const char no_data_txt[]    PROGMEM = "NO DATA\r\n";
// define bit macros
#define SETBIT(x,y) (x |= (y)) 		// Set bit y in byte x
#define CLEARBIT(x,y) (x &= (~y)) // Clear bit y in byte x
#define CHECKBIT(x,y) (x & (y)) 	// Check bit y in byte x
// define parameter bit mask constants 
#define ECHO 		  0x0001 // bit 0 : Echo on/off
#define HEADER	  0x0002 // bit 1 : Headers on/off
#define LINEFEED  0x0004 // bit 2 : Linefeeds on/off
#define RESPONSE  0x0008 // bit 3 : Responses on/off
#define PACKED    0x0010 // bit 4 : use packed data
#define AUTO_RECV 0x0020 // bit 5 : auto receive on/off
#define MON_TX	  0x0040 // bit 6 : monitor transmitter
#define MON_RX	  0x0080 // bit 7 : monitor receiver
#define MON_OBH   0x0100 // bit 8 : monitor one byte header
#define USE_OBH   0x0200 // bit 9 : use one byte header in Tx message
// use of bit-mask for parameters init to default values
volatile uint16_t parameter_bits = ECHO|LINEFEED|RESPONSE|AUTO_RECV;
uint8_t j1850_req_header[3] = {0x68, 0x6A, 0xF1};  // default request header
uint8_t j1850_ecm_header[3] = {0x6c, 0x10, 0xf1}; // mode 22 header
uint8_t j1850_bcm_header[3] = {0x6c, 0x40, 0xf1}; // BCM header
uint8_t auto_recv_addr = 0x6B;  // physical or functional address in receive mode
uint8_t mon_receiver;  // monitor receiver only addr
uint8_t mon_transmitter;  // monitor transmitter only addr
uint8_t serial_msg_buf[SERIAL_MSG_BUF_SIZE];	 // serial Rx buffer
uint8_t *serial_msg_pntr;
int16_t serial_putc(int8_t data);	// send one databyte to USART
#define DEFAULT_BAUD   ((unsigned int)((unsigned long)MCU_XTAL/((unsigned long)BAUD_RATE*16)-1))	// calculate baud rate value for UBBR
uint8_t timeout_multiplier;  // default 4ms timeout multiplier

void setup()
{
  wdt_disable();	// make sure the watchdog is not running
	UBRR0H = DEFAULT_BAUD>>8;		// set baud rate
	UBRR0L = DEFAULT_BAUD;
	UCSR0B =((1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0));	// enable Rx & Tx, enable Rx interrupt
	serial_msg_pntr = &serial_msg_buf[0];  // init serial msg pointer
	
	j1850_init();	// init J1850 bus

	ident();	// send identification to terminal

	serial_putc('>');  // send initial command prompt

	sei();	// enable global interrupts
TCCR1A=0x0000; // configure timer1
TCCR1C=0x0000; // configure timer1
}

void loop()
{
		while( CHECKBIT(parameter_bits, MON_RX) ||
            CHECKBIT(parameter_bits, MON_TX) ||
            CHECKBIT(parameter_bits, MON_OBH)
          )
		{
			uint8_t j1850_msg_buf[12];  // J1850 message buffer
			uint8_t *j1850_msg_pntr = &j1850_msg_buf[0];  //  msg pointer
			int8_t recv_nbytes;  // byte counter		
      
			recv_nbytes = j1850_recv_msg(j1850_msg_buf);	// get J1850 frame
		
			if( !(recv_nbytes & 0x80) ) // proceed only with no errors
			{
				j1850_msg_pntr = &j1850_msg_buf[0];
			
				// check for respond from correct addr or monitor all mode
				if( (CHECKBIT(parameter_bits, MON_RX) && CHECKBIT(parameter_bits, MON_TX))
				    ||
						((mon_receiver == *(j1850_msg_pntr+1)) && CHECKBIT(parameter_bits, MON_RX) )
						||
						((mon_transmitter == *(j1850_msg_pntr+2)) && CHECKBIT(parameter_bits, MON_TX) )
            ||
            ((mon_transmitter == *(j1850_msg_pntr)) && CHECKBIT(parameter_bits, MON_OBH) )
					)
				{
          // surpess CRC and header bytes output
					if( !CHECKBIT(parameter_bits, HEADER) )
					{ 
            if( CHECKBIT(parameter_bits, MON_OBH) ||  // check if one byte header frames are used
                CHECKBIT(parameter_bits, USE_OBH)
               )
            {
              recv_nbytes -= 2;  // discard 1st header byte and CRC
              j1850_msg_pntr += 1;  // skip header byte
            }
            else
            {
              recv_nbytes -= 4;  // discard 3 header bytes and CRC
              j1850_msg_pntr += 3;  // skip 3 header bytes
            }
					}

					if(CHECKBIT(parameter_bits, PACKED))
					{ // check respond CRC
						if( *(j1850_msg_pntr+(recv_nbytes-1)) == j1850_crc(j1850_msg_buf, recv_nbytes-1) )
							serial_putc(recv_nbytes);  // length byte
						else
							serial_putc(recv_nbytes&0x80);  // length byte with error indicator set
					}
     
					// output response data
					for(;recv_nbytes > 0; recv_nbytes--)
					{
						if(CHECKBIT(parameter_bits, PACKED))
							serial_putc(*j1850_msg_pntr++);  // data byte
						else
						{
							serial_put_byte2ascii(*j1850_msg_pntr++);
							serial_putc(' ');
						}
					}
     			
					if(!CHECKBIT(parameter_bits, PACKED))
					{// formated output with CR and optional LF
						serial_putc('\r');
						if(CHECKBIT(parameter_bits, LINEFEED)) serial_putc('\n');
					}					
					
				}  // end if valid monitoring addr
			} // end if message recv
		} // end while monitoring active
	}	// endless loop

uint8_t j1850_recv_msg(uint8_t *msg_buf )
{
	uint8_t nbits;			// bit position counter within a byte
	uint8_t nbytes;		// number of received bytes
	uint8_t bit_state;// used to compare bit state, active or passive
	timer1_start();	
	while(!is_j1850_active())	// wait for response from j1850
	{
		if(TCNT1 >= WAIT_100us)	// check for 100us
		{
			timer1_stop(); 
                        return J1850_RETURN_CODE_NO_DATA | 0x80;	// error, no responds within 100us
                }
	}
	// wait for SOF
	timer1_start();	// restart timer1
	while(is_j1850_active())	// run as long bus is active (SOF is an active symbol)
	{
		if(TCNT1 >=  RX_SOF_MAX)
                    {
                       return J1850_RETURN_CODE_BUS_ERROR | 0x80;	// error on SOF timeout
                    }
	}
	timer1_stop();  // end of 1st active pulse
	if(TCNT1 < RX_SOF_MIN)
                    {
                      return J1850_RETURN_CODE_BUS_ERROR | 0x80;	// error, not SOF
                    }
	// SOF succesfully read
	bit_state = is_j1850_active();	// store actual bus state
	timer1_start();
	for(nbytes = 0; nbytes < 12; ++nbytes)
	{
		nbits = 8;
		do
		{
			*msg_buf <<= 1;
			while(is_j1850_active() == bit_state) // compare last with actual bus state, wait for change
			{
				if(TCNT1 >= RX_EOD_MIN	)	// check for EOD symbol
				{
					timer1_stop();
					return nbytes;	// return number of received bytes
				}
			}
			bit_state = is_j1850_active();	// store actual bus state
			uint16_t tcnt1_buf = TCNT1;
			timer1_start();
			if( tcnt1_buf < RX_SHORT_MIN) return J1850_RETURN_CODE_BUS_ERROR | 0x80;	// error, pulse was to short

			// check for short active pulse = "1" bit
			if( (tcnt1_buf < RX_SHORT_MAX) && !is_j1850_active() )
				*msg_buf |= 1;

			// check for long passive pulse = "1" bit
			if( (tcnt1_buf > RX_LONG_MIN) && (tcnt1_buf < RX_LONG_MAX) && is_j1850_active() )
				*msg_buf |= 1;

		} while(--nbits);// end 8 bit while loop
		
		++msg_buf;	// store next byte
		
	}	// end 12 byte for loop

	// return after a maximum of 12 bytes
	timer1_stop();	
	return nbytes;
}

void j1850_init(void)
{
	j1850_passive();	// set VPW pin in passive state
	J1850_DIR_OUT |= _BV(J1850_PIN_OUT);	// make VPW output pin an output
	J1850_PULLUP_IN |= _BV(J1850_PIN_IN);	// enable pull-up on VPW pin
	J1850_DIR_IN	&=~ _BV(J1850_PIN_IN);	// make VPW input pin an input
  
}

static void j1850_wait_idle(void)
{
	timer1_start();
	while(TCNT1 < RX_IFS_MIN)	// wait for minimum IFS symbol
	{
		if(is_j1850_active()) timer1_start();	// restart timer1 when bus not idle
	}
}
 void timer1_ctrl(uint8_t val)
{
    TCCR1B = val;
}
 void timer1_start(void)
{
    TCCR1B = c_start_pulse_timer;
    TCNT1 = 0;
}
 void timer1_stop(void)
{
    TCCR1B = c_stop_pulse_timer;
}
 void timer1_set(uint16_t val)
{
    TCNT1 = val;
}
uint8_t j1850_send_msg(uint8_t *msg_buf, int8_t nbytes)
{
	if(nbytes > 12)	return J1850_RETURN_CODE_DATA_ERROR;	// error, message to long, see SAE J1850

	j1850_wait_idle();	// wait for idle bus

	timer1_start();	
	j1850_active();	// set bus active
	
	while(TCNT1 < TX_SOF);	// transmit SOF symbol
          j1850_passive();
	uint8_t temp_byte,	// temporary byte store
					nbits;		// bit position counter within a byte	
	  uint16_t delay;		// bit delay time
  	do
	{
		temp_byte = *msg_buf;	// store byte temporary
		nbits = 8;
//while(TCNT1 < TX_SOF);	// transmit SOF symbol
    while (nbits--)		// send 8 bits
		{
			if(nbits & 1) // start allways with passive symbol
			{
				j1850_passive();	// set bus passive
                              	timer1_start();
				delay = (temp_byte & 0x80) ? TX_LONG : TX_SHORT;	// send correct pulse lenght
                while (TCNT1 <= delay)	// wait
				{
                                       
				  	if(!J1850_PORT_IN & _BV(J1850_PIN_IN))	// check for bus error
                                      	{
						timer1_stop();
                                              	return J1850_RETURN_CODE_BUS_ERROR;	// error, bus collision!
                                	}
				}
			}
			else	// send active symbol
			{
				j1850_active();	// set bus active
				timer1_start();
				delay = (temp_byte & 0x80) ? TX_SHORT : TX_LONG;	// send correct pulse lenght
               while (TCNT1 <= delay);	// wait
			}
      temp_byte <<= 1;	// next bit
		}// end nbits while loop
		++msg_buf;	// next byte from buffer
	} while(--nbytes);// end nbytes do loop
  j1850_passive();	// send EOF symbol
  timer1_start();
  while (TCNT1 <= TX_EOF); // wait for EOF complete
	timer1_stop();
  return J1850_RETURN_CODE_OK;	// no error
}

// calculate J1850 message CRC
uint8_t j1850_crc(uint8_t *msg_buf, int8_t nbytes)
{
	uint8_t crc_reg=0xff,poly,byte_count,bit_count;
	uint8_t *byte_point;
	uint8_t bit_point;

	for (byte_count=0, byte_point=msg_buf; byte_count<nbytes; ++byte_count, ++byte_point)
	{
		for (bit_count=0, bit_point=0x80 ; bit_count<8; ++bit_count, bit_point>>=1)
		{
			if (bit_point & *byte_point)	// case for new bit = 1
			{
				if (crc_reg & 0x80)
					poly=1;	// define the polynomial
				else
					poly=0x1c;
				crc_reg= ( (crc_reg << 1) | 1) ^ poly;
			}
			else		// case for new bit = 0
			{
				poly=0;
				if (crc_reg & 0x80)
					poly=0x1d;
				crc_reg= (crc_reg << 1) ^ poly;
			}
		}
	}
	return ~crc_reg;	// Return CRC
}
void ident(void)
{
	serial_puts_P(ident_txt);  // show code description
}



/*
**---------------------------------------------------------------------------
**
** Abstract: Convert 2 byte ASCII hex to 1 byte decimal
**
**
** Parameters: Pointer to first ASCII char
**
**
** Returns: decimal value
**
**
**---------------------------------------------------------------------------
*/
static uint8_t ascii2byte(char *val)
{
	uint8_t temp = *val;
	
	if(temp > 0x60) temp -= 39;  // convert chars a-f
	temp -= 48;  // convert chars 0-9
	temp *= 16;
	
	temp += *(val+1);
	if(*(val+1) > 0x60) temp -= 39;  // convert chars a-f
	temp -= 48;  // convert chars 0-9	

	return temp;

}



/*
**---------------------------------------------------------------------------
**
** Abstract: Processing of received serial string
**
** Parameters: none
**
** Returns: 0 = unknown
**          1 = OK
**          2 = bus busy
**          3 = bus error
**          4 = data error
**          5 = no data
**          6 = data ( also to surpress any other output )
**
**---------------------------------------------------------------------------
*/
int8_t serial_processing(void)
{
	char *serial_msg_pntr = strlwr((char *)serial_msg_buf);  // convert string to lower
	uint8_t serial_msg_len = strlen((char *)serial_msg_buf);  // get string length
  	uint8_t *var_pntr = 0;  // point to different variables

	if( (*(serial_msg_pntr)=='a') && (*(serial_msg_pntr+1)=='t'))  // check for "at" or hex
	{  // is AT command
		// AT command found
		switch( *(serial_msg_pntr+2) )  // switch on "at" command
		{
			case 'a':  // auto receive address on
				if(*(serial_msg_pntr+3) == 'r')	SETBIT(parameter_bits, AUTO_RECV);
				if( j1850_req_header[0] & 0x04)  // check for functional or physical addr
					auto_recv_addr = j1850_req_header[2]; // use physical recv addr
				else
					auto_recv_addr = j1850_req_header[1]+1;  // use funct recv addr
				return J1850_RETURN_CODE_OK ;

     
			case 'd':  // set defaults
				parameter_bits = ECHO|LINEFEED|RESPONSE|AUTO_RECV;
				timeout_multiplier = 0x19;	// set default timeout to 4ms * 25 = 100ms
				j1850_req_header[0] = 0x68;  // Prio 3, Functional Adressing
				j1850_req_header[1] = 0x6A;  // Target legislated diagnostic
				j1850_req_header[2] = 0xF1;  // Frame source = Diagnostic Tool
				return J1850_RETURN_CODE_OK ;
		
			case 'e':  // echo on/off
				if(*(serial_msg_pntr+3) == '0')
					CLEARBIT(parameter_bits, ECHO);
				else
					SETBIT(parameter_bits, ECHO);
				return J1850_RETURN_CODE_OK ;
			
			case 'i':  // send ident string
				ident();
				return J1850_RETURN_CODE_OK ;

			case 'l': // linefeed on/off (only for data strings)
				if(*(serial_msg_pntr+3) == '0')
					CLEARBIT(parameter_bits, LINEFEED);
				else
					SETBIT(parameter_bits, LINEFEED);
				return J1850_RETURN_CODE_OK ;			

			case 'h': // show headers on/off
				if(*(serial_msg_pntr+3) == '0')
					CLEARBIT(parameter_bits, HEADER);
				else
					SETBIT(parameter_bits, HEADER);
				return J1850_RETURN_CODE_OK ;

			case 'r': // show response on/off
				if(*(serial_msg_pntr+3) == '0')
					CLEARBIT(parameter_bits, RESPONSE);
				else
					SETBIT(parameter_bits, RESPONSE);
				return J1850_RETURN_CODE_OK ;

			case 'f': // send formated
				if(*(serial_msg_pntr+3) == 'd')
					CLEARBIT(parameter_bits, PACKED);
				return J1850_RETURN_CODE_OK ;

			case 'o': // one byte header on/off
				if(*(serial_msg_pntr+3) == '0')
					CLEARBIT(parameter_bits, USE_OBH);
				else
					SETBIT(parameter_bits, USE_OBH);
				return J1850_RETURN_CODE_OK ;

			case 'p': // send packed data
				if(*(serial_msg_pntr+3) == 'd')
					SETBIT(parameter_bits, PACKED);
				return J1850_RETURN_CODE_OK ;

			case 'm':  // switch into monitoring mode
				switch(*(serial_msg_pntr+3))
				{
					case 'a':
						SETBIT(parameter_bits, MON_RX);  // monitor all
						SETBIT(parameter_bits, MON_TX);
						return J1850_RETURN_CODE_DATA; // return, no following parameter
            
          case 'i':
						CLEARBIT(parameter_bits, MON_TX);
 						CLEARBIT(parameter_bits, MON_RX);
            SETBIT(parameter_bits, MON_OBH);  // monitor one byte header
 						var_pntr = &mon_transmitter;
            break;  // get folowing parameter
            
					case 'r':  // monitor only receiver addr
						SETBIT(parameter_bits, MON_RX);  // monitor receiver only
						CLEARBIT(parameter_bits, MON_TX);
						var_pntr = &mon_receiver;
						break;  // get following parameter					

					case 't':  // monitor only transmitter addr
						CLEARBIT(parameter_bits, MON_RX);
						SETBIT(parameter_bits, MON_TX);  // monitor transmitter only
						var_pntr = &mon_transmitter;
						break;  // get following parameter
          
          default:
            return J1850_RETURN_CODE_UNKNOWN;
				}
				if(
						isxdigit(*(serial_msg_pntr+4)) && isxdigit(*(serial_msg_pntr+5))
						&&	( serial_msg_len == 6)
					)  // proceed when next two chars are hex
					{
					  // make 1 byte hex from 2 chars ASCII and save
						*var_pntr = ascii2byte(serial_msg_pntr+4);
						return J1850_RETURN_CODE_DATA;
					}

			case 's': // commands SH,SR or ST

				if(	isxdigit(*(serial_msg_pntr+4)) && isxdigit(*(serial_msg_pntr+5)) )
				{  // proceed when next two chars are hex
					switch(*(serial_msg_pntr+3))
					{
						case 'h':  // set header bytes
							if(
									isxdigit(*(serial_msg_pntr+6)) && isxdigit(*(serial_msg_pntr+7))
									&&	isxdigit(*(serial_msg_pntr+8)) && isxdigit(*(serial_msg_pntr+9))
									&&	( serial_msg_len == 10)
								)  // proceed when next four chars are hex
								{
								  // make 3 byte hex from 6 chars ASCII and save
									j1850_req_header[0]=ascii2byte(serial_msg_pntr+4);
									j1850_req_header[1]=ascii2byte(serial_msg_pntr+6);
									j1850_req_header[2]=ascii2byte(serial_msg_pntr+8);
									if( CHECKBIT(parameter_bits, AUTO_RECV) )
									{
										if( j1850_req_header[0] & 0x04)  // check for functional or physical addr
											auto_recv_addr = j1850_req_header[2]; // use physical recv addr
										else
											auto_recv_addr = j1850_req_header[1]+1;  // use funct recv addr
									}
									return J1850_RETURN_CODE_OK ;
								}
                                                                  
								break;
					
						case 't':  // set response timeout multipler
								var_pntr = &timeout_multiplier;
								break;
								
						case 'r':  // set receive address and manual receive mode
								var_pntr = &auto_recv_addr;
								CLEARBIT(parameter_bits, AUTO_RECV);
								break;
            
            default:
              return J1850_RETURN_CODE_UNKNOWN;
					} // end switch char 3
					if(serial_msg_len == 6)
					{
						*var_pntr =ascii2byte(serial_msg_pntr+4);
						if (timeout_multiplier < 8)  timeout_multiplier = 8;  // set multiplier for minimum of 32ms timout
						return J1850_RETURN_CODE_OK ;
					}
				} // end if char 4 and 5 isxdigit
                                     else if (*(serial_msg_pntr+4)=='b')
                                       {
                                        j1850_req_header[0]=j1850_bcm_header[0];
					j1850_req_header[1]=j1850_bcm_header[1];
					j1850_req_header[2]=j1850_bcm_header[2];
                                        return J1850_RETURN_CODE_OK ;                                
                                       }
                                        return J1850_RETURN_CODE_UNKNOWN;

			case 'z':  // reset all and restart device
				wdt_enable(WDTO_15MS);	// enable watdog timeout 15ms
				for(;;);	// wait for watchdog reset
		
			default:  // return error, unknown command
				return J1850_RETURN_CODE_UNKNOWN;		
		}

	}
          
        
        {
			if( (serial_msg_len & 1) || (serial_msg_len > 16) )  // check for "even" message lenght
			return J1850_RETURN_CODE_UNKNOWN;                                           // and maximum of 8 data bytes

		serial_msg_len /= 2;  // use half the string lenght for byte count

		while( *serial_msg_pntr )  // check all chars are valid hex chars
		{
			if(!isxdigit(*serial_msg_pntr))
				return J1850_RETURN_CODE_UNKNOWN;
			++serial_msg_pntr;
		}
		serial_msg_pntr = (char *)&serial_msg_buf[0];  // reset pointer
	
	  uint8_t j1850_msg_buf[12];  // J1850 message to be send
		uint8_t *j1850_msg_pntr = &j1850_msg_buf[0];  //  msg pointer
		uint8_t cnt;  // byte counter
		
		// store header bytes 1, use at least one header byte
		*j1850_msg_pntr = j1850_req_header[0];
    
    // store header 2-3 when three byte header is in use
    if( !CHECKBIT(parameter_bits, USE_OBH) )
    {
      *(++j1850_msg_pntr) = j1850_req_header[1];
      *(++j1850_msg_pntr) = j1850_req_header[2];
    }
    
		// convert serial message from 2 byte ASCII to 1 byte binary and store
		for(cnt = 0; cnt < serial_msg_len; ++cnt)
		{
			*(++j1850_msg_pntr) = ascii2byte(serial_msg_pntr);
			serial_msg_pntr += 2;		
		}
		
		// generate CRC for J1850 message and store, use 1 or 3 byte header
    if(CHECKBIT(parameter_bits, USE_OBH))
      *(++j1850_msg_pntr) = j1850_crc( j1850_msg_buf,serial_msg_len+1 );  // use one header bytes
    else
      *(++j1850_msg_pntr) = j1850_crc( j1850_msg_buf,serial_msg_len+3 );  // use three header byte
      
		// send J1850 message and save return code, use 1 or 3 byte header
    uint8_t return_code;
    if(CHECKBIT(parameter_bits, USE_OBH))
      return_code = j1850_send_msg(j1850_msg_buf, serial_msg_len+2);
    else
      return_code = j1850_send_msg(j1850_msg_buf, serial_msg_len+4);
      
		// skip receive in case of transmit error or RESPONSE disabled
		if( (return_code == J1850_RETURN_CODE_OK) && CHECKBIT(parameter_bits, RESPONSE) )
		{
			uint16_t time_count = 0;		

			do
			{
				/*
					Run this loop until we received a valid response frame, or response timed out,
					or the bus was idle for 100ms or an bus error occured.
				*/
			
				cnt = j1850_recv_msg(j1850_msg_buf);  // receive J1850 respond

				/*
					the j1850_recv_msg() has a timeout of 100us
					so the loop will run 100ms by default before timeout
					100ms is recommended by SAE J1850 spec
				*/
				++time_count;	
				if(time_count >= 1000)
					return J1850_RETURN_CODE_NO_DATA;
					
				/*
					Check for bus error. End the loop then.
				*/
				if( cnt == (J1850_RETURN_CODE_BUS_ERROR & 0x80) )  // check if we got an error code or just number of recv bytes
				{
					if(CHECKBIT(parameter_bits, PACKED))
					{
						serial_putc(0x80);  // lenght byte with error indicator set
						return J1850_RETURN_CODE_DATA;  // surpress any other output
					}
					else
						return cnt & 0x7F;  // return "receive message" error code
				}

				j1850_msg_pntr = &j1850_msg_buf[0];

			} while( auto_recv_addr != *(j1850_msg_pntr+1) );	

			// check respond CRC
			if( *(j1850_msg_pntr+(cnt-1)) != j1850_crc(j1850_msg_buf, cnt-1) )
			{
				if(CHECKBIT(parameter_bits, PACKED))
				{
					serial_putc(0x80);  // length byte with error indicator set
					return J1850_RETURN_CODE_DATA;  // surpress any other output
				}
				else
					return J1850_RETURN_CODE_DATA_ERROR;
			}

			// check for respond from correct addr in auto or man recv mode
			if( auto_recv_addr != *(j1850_msg_pntr+1) )
			{
				if(CHECKBIT(parameter_bits, PACKED))
				{
					serial_putc(0x00);  // length byte
					return J1850_RETURN_CODE_DATA;  // surpress any other output
				}
				else
					return J1850_RETURN_CODE_NO_DATA;
			}
			
			if( !CHECKBIT(parameter_bits, HEADER) )
			{ 
         if(CHECKBIT(parameter_bits, USE_OBH) )  // check if one byte header frames are used
         {
            cnt -= 2;  // discard 1st header byte and CRC
            j1850_msg_pntr += 1;  // skip header byte
          }
          else
          {
            cnt -= 4;  // discard 3 header bytes and CRC
            j1850_msg_pntr += 3;  // skip 3 header bytes
          }
			}

			if(CHECKBIT(parameter_bits, PACKED))
				serial_putc(cnt);  // length byte
			
			// output response data
			for(;cnt > 0; --cnt)
			{
				if(CHECKBIT(parameter_bits, PACKED))
					serial_putc(*j1850_msg_pntr++);  // length byte
				else
				{
					serial_put_byte2ascii(*j1850_msg_pntr++);
					serial_putc(' ');
				}
			}
			
			if(!CHECKBIT(parameter_bits, PACKED))
			{// formated output with CR and optional LF
				serial_putc('\r');
				if(CHECKBIT(parameter_bits, LINEFEED)) serial_putc('\n');
			}
			return J1850_RETURN_CODE_DATA;  // surpress any other output

		}  // end if J1850 OK && RESPONSE
		else  // transmit error or show RESPONSE OFF, return error code
			return return_code;
		
	} // end if !AT
	
	// we should never reach this return
	return J1850_RETURN_CODE_UNKNOWN;
} // end serial_processing


/*
**---------------------------------------------------------------------------
**
** Abstract: Send one byte via USART
**
** Parameters: data byte
**
** Returns: NULL
**
**---------------------------------------------------------------------------
*/
int16_t serial_putc(int8_t data)
{
	// wait for USART to become available
		while ( (UCSR0A & _BV(UDRE0)) != _BV(UDRE0));
		UDR0 = data; 									// send character
		return 0;
}; //end usart_putc


/*
**---------------------------------------------------------------------------
**
** Abstract: Make 2 byte ASCII from one byte binary and send to terminal
**
** Parameters: input byte
**
** Returns: none
**
**---------------------------------------------------------------------------
*/
void serial_put_byte2ascii(uint8_t val)
{
	uint8_t ascii1=val;
	serial_putc( ((ascii1>>4) < 10) ? (ascii1>>4) + 48 : (ascii1>>4) + 55 );  // upper nibble
	serial_putc( ((val&0x0f) < 10) ? (val&0x0f) + 48 : (val&0x0f) + 55 );  // lower nibble
}


/*
**---------------------------------------------------------------------------
**
** Abstract: Print string in program memory to terminal
**
** Parameters: Pointer to string
**
** Returns: none
**
**---------------------------------------------------------------------------
*/
void serial_puts_P(const char *s)
{
	while( pgm_read_byte(&*s)) serial_putc( pgm_read_byte(&*s++));// send string char by char
}

/*
**---------------------------------------------------------------------------
**
** Abstract: USART Receive Interrupt
**
** Parameters: none
**
** Returns: none
**
**---------------------------------------------------------------------------
*/
SIGNAL(SIG_USART_RECV)
{
	uint8_t *hlp_pntr = &serial_msg_buf[sizeof(serial_msg_buf)-1];  // get end of serial buffer

	// check for buffer end, prevent buffer overflow
	if ( serial_msg_pntr > hlp_pntr )
	{
		serial_msg_pntr = &serial_msg_buf[sizeof(serial_msg_buf)-1];
	}
	
	uint8_t in_char = UDR0;  // get received char

  // end monitor modes on any received char
	if( CHECKBIT(parameter_bits,MON_RX) ||
      CHECKBIT(parameter_bits,MON_TX) ||
      CHECKBIT(parameter_bits,MON_OBH)
    )
	{
		CLEARBIT(parameter_bits,MON_RX);
		CLEARBIT(parameter_bits,MON_TX);
    CLEARBIT(parameter_bits,MON_OBH);
		print_prompt();  // command prompt to terminal
	}
	else  // no active monitor mode
	{
		if( CHECKBIT(parameter_bits,ECHO) )  // return char when echo is on
			serial_putc(in_char);		

		// check for terminating char
		if(in_char == 0x0D)
		{
      //if(CHECKBIT(parameter_bits, LINEFEED)) serial_putc('\n');    
			*(serial_msg_pntr) = 0x00;	// terminate received message
			switch ( serial_processing() )  // process serial message
			{
				case J1850_RETURN_CODE_OK:  // success
					serial_puts_P(PSTR("\n\rOK"));
					print_prompt();  // command prompt to terminal
					break;
				case J1850_RETURN_CODE_BUS_BUSY:  // bus was busy
					serial_puts_P(bus_busy_txt);
					print_prompt();  // command prompt to terminal
					break;
				case J1850_RETURN_CODE_BUS_ERROR:  // bus error detected
					serial_puts_P(bus_error_txt);
					print_prompt();  // command prompt to terminal
					break;
				case J1850_RETURN_CODE_DATA_ERROR:  // data error detected
					serial_puts_P(data_error_txt);
			    print_prompt();  // command prompt to terminal						
					break;
				case J1850_RETURN_CODE_NO_DATA:  // no data response (response timeout)
					serial_puts_P(no_data_txt);
			    print_prompt();  // command prompt to terminal	
					break;
				case J1850_RETURN_CODE_DATA:     // data response
          print_prompt();  // command prompt to terminal
					break;			
				default: // unknown error
					serial_puts_P(PSTR("\n\r?"));
			    print_prompt();  // command prompt to terminal	
			}
			serial_msg_pntr = &serial_msg_buf[0];  // start new message
		}
	
		// received char was no termination
		if(isalnum((int16_t)in_char))
		{  // check for valid alphanumeric char and save in buffer
			*serial_msg_pntr = in_char;
			++serial_msg_pntr;	
		}
  }
};// end of UART receive interrupt


/*
**---------------------------------------------------------------------------
**
** Abstract: Print command prompt to terminal
**
**
** Parameters: none
**
**
** Returns: none
**
**
**---------------------------------------------------------------------------
*/
void print_prompt(void)
{
	serial_puts_P(PSTR("\r\n>"));	// send new command prompt
}
Last edited by tek1229 on Sat Jan 16, 2016 12:14 pm, edited 1 time in total.
tek1229
Posts: 15
Joined: Thu Jun 11, 2015 11:53 am

Re: AVR to Arduino - VPW

Post by tek1229 »

By the way, I found the problem for the serial receive issue.. the issue is because I originally did this on an Atmega168 and I'm checking it with an Atmega328.. The interupt handler is different.. Needs to be changed to (USART_RX_vect) from (SIG_USART_RECV) .. Once you do that it'll compile and load ok.. Haven't checked out the electric end of it, but I remember I hacked it.. Think I used a resistor divider for incoming and a transistor fed 12v for outgoing.. May have used a zener diode to limit voltage, or simply a resistor ladder as well, don't remember.. I'm looking for any research I can find but that was probably 5 computers and 2 houses ago.. lol.. The timing was fun to figure out, For the version I used he said I needed a crystal running at 7.3728MHz for it to work.. The makefile had a define of MCU_XTAL = 7372800.. Since I wasn't going to burn any fuses to make it run on an arduino I needed to come up with something.. What I did this I simply put #define MCU_XTAL 16000000 on the top of the arduino program so the timing calculations would work out.. Seems to work fine for what I needed..

I haven't found my actual work files but I did find a backup, have a few more revisions where I had it stand alone request data like vss and cts and a/c request.. Had another revision where I used the arduino serial library and converted all of michael's serial code to make it simpler for me to understand, looking for that one still..
HOYS
Posts: 7
Joined: Sat Dec 01, 2012 6:55 pm
cars: 2003 Pontiac Grand Am

Re: AVR to Arduino - VPW

Post by HOYS »

Interested if you could post a schematic of how you connected everything in your arduino implementation. Trying to get this to work right now myself.
redheadedrod
Posts: 4
Joined: Tue Apr 18, 2017 1:46 am
cars: '03 GM truck, '09 GM car, '94 GM car

Re: AVR to Arduino - VPW

Post by redheadedrod »

So what is the current status of this project? I am in process of researching j1850VPW protocol to add the code to a pre-release arduino Due based interface. Since this is my first Arduino Project I have lots of questions...
User avatar
antus
Site Admin
Posts: 8237
Joined: Sat Feb 28, 2009 8:34 pm
cars: TX Gemini 2L Twincam
TX Gemini SR20 18psi
Datsun 1200 Ute
Subaru Blitzen '06 EZ30 4th gen, 3.0R Spec B
Contact:

Re: AVR to Arduino - VPW

Post by antus »

If you've got a logic analyser that is supported by sigrok (or willing to get one, they can be very low cost) you'll probably find this protocol decoder I wrote very helpful in your development viewtopic.php?f=3&t=4761

Also check out the allpro adaptor from http://www.obddiag.net/allpro.html as its open source hardware and software. Its cost is low and its a quite powerful platform.
Have you read the FAQ? For lots of information and links to significant threads see here: http://pcmhacking.net/forums/viewtopic.php?f=7&t=1396
shorts
Posts: 1
Joined: Sat Aug 05, 2017 11:03 am
cars: WB Panel Van - transplanting LS1

Re: AVR to Arduino - VPW

Post by shorts »

Folks..

New to the forum and I have pretty much confused myself already. Project is an LS1 transplant into a WB van. So far motor is almost rebuilt and trans is done (4l60). I am also grafting a VY dash / instrument cluster / AC / wiring loom into the car. Ideally I want to run antitheft etc and basically i want it to act totally as a VY.

Also I was planning on an odb2 driven additional display (tft soft dash) and recently saw obd2 arduino shields available. I will chase up the details later but i believe it acts like an ELM 327. As I want to be able to eventually edit dash, bcm and pcm data is this a good interface to start with?

Shield details to follow

Regards,
Craig
User avatar
antus
Site Admin
Posts: 8237
Joined: Sat Feb 28, 2009 8:34 pm
cars: TX Gemini 2L Twincam
TX Gemini SR20 18psi
Datsun 1200 Ute
Subaru Blitzen '06 EZ30 4th gen, 3.0R Spec B
Contact:

Re: AVR to Arduino - VPW

Post by antus »

Sure thas fine. You need VPW protocol, just 1x speed for logging. Any off the shelf elm will be enough but the ascii protocol it talks isnt elegant (wastes 2 or 2.5x the serial bandwidth on the pc side) depending if you put spaces between the ascii hex representation. If you want to go further a binary protocil is better.
Have you read the FAQ? For lots of information and links to significant threads see here: http://pcmhacking.net/forums/viewtopic.php?f=7&t=1396
Post Reply