Re: AVR to Arduino - VPW
Posted: Sun Aug 17, 2014 12:33 am
Yeah spot on, I read that in the elm327 diagrams pdf. That 317L adjustable voltage regulator allows the change between 5v and 8v.
Electronic Fuel Injection - Developement & Tuning
https://pcmhacking.net/forums/
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
}