AVR to Arduino - VPW

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

AVR to Arduino - VPW

Post by Tazzi »

Not sure whether this should be here.. or off topic, but any who...

After having 4 elm327's connected up to the Tech2 to keep up with communication and reverse engineer bis and pieces.. Im really seeing the limitations of the device. If I see another "Buffer full" message, I think Im going to spontaneously combust.
It seems a smart chap, Michael, has developed a AVR device which can communicate over VPW.
Can be found here: http://www.mictronics.de/projects/j1850-vpw-interface/

To me, it looks like a perfect opportunity to develop a cheap device to communicate over VPW without such limitations.
After briefly going over the schematics, looks like we can easily port it over to an arduino mega comfortably.. just might require changing the crystal to a 4Mhz instead of the 16Mhz one to minimize the required changes in coding.

A nice little diagram that depicts which pins correspond to what from ATmega8 to Arduino..
Image


Now porting the code to Arduino. Since I dont know the AVR laguage very well.. just a few inconsistencies, makes it hard to tell what certain things are doing such as setting up the serial ports ect. I know I can import AVR libraries, but this doenst solve the serial port setup delemas and its still nice to be able to understand each part of the code incase changes are ever required.

Hoping to give this a real crack and see if we cant get a device up and running, Just go through each section of the code, understand what its doing and port it to an arduino appropriate format. My end goal here, is a VPW device, which can have custom "controls" embedded into it. For me this would be getting it to respond back as a fake PCM or TCM ect while reversing the Tech2 instead displaying everything over serial port which leads to our buffer full issues and missing vital frames.


And heres the schematic of it (K1 = OBD2 port and K2 equal serial port.. but we will simply use the onboard usb port on Arduino)
schematic.PNG
schematic.PNG (57.18 KiB) Viewed 15115 times
Your Local Aussie Reverse Engineer
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Image
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 »

Howdy, I moved your topic to hardware modification and then renamed the area to modification and development. Now it suits your post :)

I think that this is a good idea. I would really like to see a cheap VPW hardware device become available.

What you are looking at would most certainly work. I am an atmel man myself, and prefer it over pic tools and architecture, but have you considered staying on pic, and porting the code to run on the elm clones? There is a 5 pin pic programming header on the side of the board, and if you do that, then after the firmware is developed (and of course thats the hard bit) your talking $10 worth of hardware and a quick flash to have a working device.
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
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 »

antus wrote:Howdy, I moved your topic to hardware modification and then renamed the area to modification and development. Now it suits your post :)

I think that this is a good idea. I would really like to see a cheap VPW hardware device become available.

What you are looking at would most certainly work. I am an atmel man myself, and prefer it over pic tools and architecture, but have you considered staying on pic, and porting the code to run on the elm clones? There is a 5 pin pic programming header on the side of the board, and if you do that, then after the firmware is developed (and of course thats the hard bit) your talking $10 worth of hardware and a quick flash to have a working device.
Yeah thats also a possibility. I guess if we can understand this coding well enough since its a a working example, it will be much easier to adapt to any device we want. Will need to do some pcb pin tracking to see what pin goes where. Only really need 4.. VPW TX and RX, and usb TX and RX. The rest should be hooked up accordingly I guess for power,gnd and the LED's.

Cheers for sorting out the thread location :thumbup:
Your Local Aussie Reverse Engineer
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Image
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 »

I preferable dont want to have to use header files.. so compiling into one file will be what Ill be looking to do.
So theres 4 main files to look at here:
J1850.h
J1850.c
Main.h
Main.c

Might as well start with the VPW protocol controlling files, so opening up J1850.h file: (All credits to Michael wolf!)

Code: Select all

#ifndef __J1850_H__    
#define __J1850_H__

/*** 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

#define	J1850_PIN_OUT_NEG			// define output level inverted by hardware
#define	J1850_PIN_IN_NEG			// define input level inverted by hardware

/*** CONFIG END ***/

#ifdef J1850_PIN_OUT_NEG
	#define j1850_active() J1850_PORT_OUT &=~ _BV(J1850_PIN_OUT)
	#define j1850_passive() J1850_PORT_OUT |= _BV(J1850_PIN_OUT)
#else
	#define j1850_active() J1850_PORT_OUT |= _BV(J1850_PIN_OUT)
	#define j1850_passive() J1850_PORT_OUT &=~ _BV(J1850_PIN_OUT)
#endif

#ifdef J1850_PIN_IN_NEG
#define is_j1850_active() bit_is_clear(J1850_PORT_IN, J1850_PIN_IN)
#else
#define is_j1850_active() bit_is_set(J1850_PORT_IN, J1850_PIN_IN)
#endif

/* Define Timer1 Prescaler here */
#define c_start_pulse_timer	0x01  // Timer1 runs without Prescaler, 135ns tick @ 7,3728MHz
#define c_stop_pulse_timer	0x00


// 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

uint8_t timeout_multiplier;  // default 4ms timeout multiplier

extern void j1850_init(void);
extern uint8_t j1850_recv_msg(uint8_t *msg_buf );
extern uint8_t j1850_send_msg(uint8_t *msg_buf, int8_t nbytes);
extern uint8_t j1850_crc(uint8_t *msg_buf, int8_t nbytes);

static inline void timer1_ctrl(uint8_t val)
{
    TCCR1B = val;
}

static inline void timer1_start(void)
{
    TCCR1B = c_start_pulse_timer;
    TCNT1 = 0;
}

static inline void timer1_stop(void)
{
    TCCR1B = c_stop_pulse_timer;
}

static inline void timer1_set(uint16_t val)
{
    TCNT1 = val;
}

#endif // __J1850_H__

Starting with the top: (PORTx = if it should be high or low, DDRx = if input or output, PINx = pin on MCU)

#define J1850_PORT_OUT PORTC // J1850 output port - var PORT_OUT is PORTC, used to set pin as high or low
#define J1850_DIR_OUT DDRC // J1850 direction register - var DIR_OUT is DDRC, used to set direction.. eg input or Output
#define J1850_PIN_OUT 3 // J1850 output pin - var PIN_OUT is set to pin 3...is connected to OBD2 Connector same as Analogue input 3 on Arduino

#define J1850_PORT_IN PINC // J1850 input port - var PORT_IN is PINc, used to set the pin on MCU (bit confused)
#define J1850_PULLUP_IN PORTC // J1850 pull-up register - var PULLUP_IN is PORTC, used to set pin as high or low
#define J1850_DIR_IN DDRC // J1850 direction register - var DIR_IN is DDRC, used to set direction eg Input or Output
#define J1850_PIN_IN 0 // J1850 input pin - var PIN_IN is set to 0..is connected to OBD2 Connector same as Analogue input 0 on Arduino

#define J1850_PIN_OUT_NEG // define output level inverted by hardware - Wont need to be inverted since not serial port
#define J1850_PIN_IN_NEG // define input level inverted by hardware - Wont need to b inverted since no serial port


I think thats the setup for the vpw pins. So its equivalent to setting the pins as High/low, Out/In and setting the actual pin. The defined variables will be called and set throughout the program to the required values. (If anyone has any comments, now wold be great!)
Your Local Aussie Reverse Engineer
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Image
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 »

Next section: (Note, BV = bitwise operation.. can be used to turn pins high or low)

#ifdef J1850_PIN_OUT_NEG //If it has been defined as J1850_PIN_OUT_NEG, then do this
#define j1850_active() J1850_PORT_OUT &=~ _BV(J1850_PIN_OUT) //var j1850_Active means J1850_PORT_OUT = Low (off) (where PIN_OUT = mcu VPW pin)
#define j1850_passive() J1850_PORT_OUT |= _BV(J1850_PIN_OUT) //var j1850_passive means J1850_PORT_OUT = High (on) (Where PIN_OUT = mcu VPW pin)
#else //if not NEG, do this
#define j1850_active() J1850_PORT_OUT |= _BV(J1850_PIN_OUT) //var j1850_Active means J1850_PORT_OUT = High (on) (where PIN_OUT = mcu VPW pin)
#define j1850_passive() J1850_PORT_OUT &=~ _BV(J1850_PIN_OUT) //var j1850_passive means J1850_PORT_OUT = Low (off) (Where PIN_OUT = mcu VPW pin)
#endif

#ifdef J1850_PIN_IN_NEG //If it has been defined as J1850_PIN_OUT_NEG, then do this
#define is_j1850_active() bit_is_clear(J1850_PORT_IN, J1850_PIN_IN) //var is_j1850_active means bit_is_clear(PIN Low??, RX MCU PIN)
#else
#define is_j1850_active() bit_is_set(J1850_PORT_IN, J1850_PIN_IN) //no change do same as above
#endif
Your Local Aussie Reverse Engineer
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Image
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 »

/* Define Timer1 Prescaler here */
#define c_start_pulse_timer 0x01 // Timer1 runs without Prescaler, 135ns tick @ 7,3728MHz - Pulse timer ON (Note, need to change 16Mhz crystal to 4Mhz)
#define c_stop_pulse_timer 0x00 //Pulse Timer OFF


// define error return codes
#define J1850_RETURN_CODE_UNKNOWN 0 //Simple error code definitions
#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 - Prescaler calculation here
#define us2cnt(us) ((unsigned int)((unsigned long)(us) / (1000000L / (float)((unsigned long)MCU_XTAL / 1L)))) //So, converting microseconds.. into.. ??
// So it is: (int)(long)microsecond/ (1000000L/ (float)(long 4000000 / 1L)) - Im not sure what the L stands for? hmm...


#define WAIT_100us us2cnt(100) // 100us, used to count 100ms - so all that us2count is converting 100microseconds to 100milli seconds? Is that right?
Your Local Aussie Reverse Engineer
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Image
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 »

// 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 - count 64 microsec
#define TX_LONG us2cnt(128) // Long pulse nominal time - count 128 microsec
#define TX_SOF us2cnt(200) // Start Of Frame nominal time -count 200 microsec
#define TX_EOD us2cnt(200) // End Of Data nominal time - count 200 microsec
#define TX_EOF us2cnt(280) // End Of Frame nominal time - count 280 microsec
#define TX_BRK us2cnt(300) // Break nominal time -count 300 microsec
#define TX_IFS us2cnt(300) // Inter Frame Separation nominal time -count 300 microsec

// 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 -count 64 microsec
#define TX_IFR_LONG_NOCRC us2cnt(128) // long In Frame Respond, IFR contain no CRC -count 128 microsec

// 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


So looks like all the "us2cnt" stuff is converting the microseconds to a specific value utilizing the crystal value. I think. Which is then used to define the RX an TX requirements of the VPW frames. Might have to ask Michael about that one
Your Local Aussie Reverse Engineer
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Image
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 »

uint8_t timeout_multiplier; // default 4ms timeout multiplier - Byte timeour_multipler

extern void j1850_init(void); //function initialise j1850
extern uint8_t j1850_recv_msg(uint8_t *msg_buf ); //function byte j1850_recv_msg(byte *msg_buf)
extern uint8_t j1850_send_msg(uint8_t *msg_buf, int8_t nbytes); //function byte j1850_send_msg(byte *msg_buf, byte nbytes)
extern uint8_t j1850_crc(uint8_t *msg_buf, int8_t nbytes); //function byte j1850_crc(byte *msg_buf, byte nbytes)

static inline void timer1_ctrl(uint8_t val) // function timer1_ctrl(byte val) - this essential manually controls timer ON/OFF
{
TCCR1B = val; //controls timer.. can set timer on/off or set the prescaler
}

static inline void timer1_start(void) //function start timer
{
TCCR1B = c_start_pulse_timer; //sets TCCR1B = 1.. timer starts
TCNT1 = 0; //process value reset (set to 0)
}

static inline void timer1_stop(void) //stop timer
{
TCCR1B = c_stop_pulse_timer; //Sets TCCR1B = 0..stop timer
}

static inline void timer1_set(uint16_t val) //Set custom process value
{
TCNT1 = val; //Set process value
}

#endif // __J1850_H__



So thats the end of that file! Only issues is reading up on equivalent timer controls on the arduino. And also totally confirm that timer/microsecond equation up top.
Your Local Aussie Reverse Engineer
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Image
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 »

Now moving onto the next file, j1850.c
This file does not return or have any parameters, it is used to initialize the vpw connection.

I wont paste the entire code this time.. will go section by section again.


#include <avr/io.h> //for io pins control
#include "j1850.h" //import the header file previous defined
/*
**---------------------------------------------------------------------------
**
** Abstract: Init J1850 bus driver
**
** Parameters: none
**
** Returns: none
**
**---------------------------------------------------------------------------
*/
void j1850_init(void) //function as seen in header file (returns nothing, passes nothing)
{
j1850_passive(); // set VPW pin in passive state - set to passive (self explanatory)
J1850_DIR_OUT |= _BV(J1850_PIN_OUT); // make VPW output pin an output -Set TX pin (pin 3) to output

J1850_PULLUP_IN |= _BV(J1850_PIN_IN); // enable pull-up on VPW pin - Set RX pin pullup pin High on pin 0
J1850_DIR_IN &=~ _BV(J1850_PIN_IN); // make VPW input pin an input - Set RX pin (pin 0) to input

}


/*
**---------------------------------------------------------------------------
**
** Abstract: Wait for J1850 bus idle
**
** Parameters: none
**
** Returns: none
**
**---------------------------------------------------------------------------
*/
static void j1850_wait_idle(void) //function to wait for bus idle
{
timer1_start(); //start timer
while(TCNT1 < RX_IFS_MIN) // wait for minimum IFS symbol - while process value (timer value) is less thant the RX_IFS_MIN.. do below
{
if(is_j1850_active()) timer1_start(); // restart timer1 when bus not idle - if j1850 is active then restart timer1
}
}


/*
**---------------------------------------------------------------------------


Thats enough for today.. will have a look at some more tomorrow.
Your Local Aussie Reverse Engineer
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Image
ejukated
Posts: 443
Joined: Wed Mar 04, 2009 8:52 pm

Re: AVR to Arduino - VPW

Post by ejukated »

Great project! I actually etched a PCB from that design and built the interface years ago to reset an code in a LS1 I still have it here somewhere but I think I blew the PIC mucking around with something
Post Reply