AVR to Arduino - VPW
Re: AVR to Arduino - VPW
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
Contact for Software/Hardware development and Reverse Engineering
Site:https://www.envyouscustoms.com
Mob:+61406 140 726
Re: AVR to Arduino - VPW
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..
Let me know if there's any interest, I'll find the code, sounds like you have it figured out though..
- antus
- Site Admin
- Posts: 8251
- 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
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
Re: AVR to Arduino - VPW
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.
Re: AVR to Arduino - VPW
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..
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..
Re: AVR to Arduino - VPW
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.
-
- 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
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...
- antus
- Site Admin
- Posts: 8251
- 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
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.
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
Re: AVR to Arduino - VPW
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
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
- antus
- Site Admin
- Posts: 8251
- 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
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