pcan_cantact_another_one/Src/pcan_protocol.c

830 lines
18 KiB
C

#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "pcan_can.h"
#include "pcan_usb.h"
#include "pcan_packet.h"
#include "pcan_led.h"
#include "pcan_timestamp.h"
#include "punker.h"
#define SJA1000_BASICCAN 0
#define SJA1000_PELICAN 1
static struct
{
uint8_t bus_active;
uint8_t ext_vcc_state;
uint8_t led_mode;
uint16_t last_timestamp_sync;
uint16_t last_flush;
struct
{
/* error handling related */
uint8_t err;
uint8_t ecc;
uint8_t rx_err;
uint8_t tx_err;
/* config */
uint8_t btr0;
uint8_t btr1;
uint8_t silient;
uint8_t loopback;
uint8_t err_mask;
uint32_t quartz_freq;
}
can;
uint8_t sja1000_shadow[6];
uint8_t sja1000_ic_mode;
uint8_t device_id;
uint32_t device_serial;
}
pcan_device =
{
.device_id = 0xFF,
.device_serial = 0xFFFFFFFF,
.can =
{
.quartz_freq = PCAN_USB_CRYSTAL_HZ,
.loopback = 0,
},
};
#define BLOCK_SIZE (64)
#define HEADER_SIZE (2)
#define PCAN_MAX_RECORD_SIZE (BLOCK_SIZE*14)
typedef struct
{
/* raw data array */
uint8_t buffer[PCAN_MAX_RECORD_SIZE];
/* ts counter, need to provide correct timestamp correction ( 2 or 1 byte ) */
uint16_t ts_counter;
uint16_t pos;
}
PCAN_RECORD_BUFFER_EX;
static uint8_t temp_data_buffer[PCAN_MAX_RECORD_SIZE] = { 0 };
static struct t_m2h_fsm data_fsm =
{
.state = 0,
.ep_addr = PCAN_USB_EP_MSGIN,
.pdbuf = temp_data_buffer,
.dbsize = sizeof( temp_data_buffer ),
};
static PCAN_RECORD_BUFFER_EX pcan_records;
void pcan_sja1000_write( uint8_t reg_addr, uint8_t value )
{
if( pcan_device.sja1000_ic_mode == SJA1000_PELICAN )
{
reg_addr &= 0x7F;
switch( reg_addr )
{
case 6:
pcan_device.sja1000_shadow[2] = value;
break;
case 7:
pcan_device.sja1000_shadow[3] = value;
break;
case 14:
pcan_device.sja1000_shadow[4] = value;
break;
case 15:
pcan_device.sja1000_shadow[5] = ( value == 0xFF ) ? 0x7F: value;
break;
case 16:
pcan_device.sja1000_shadow[0] = value;
break;
case 20:
pcan_device.sja1000_shadow[1] = value;
break;
case 31:
if( (value & 0x80 ) == 0 )
{
pcan_device.sja1000_ic_mode = SJA1000_BASICCAN;
}
break;
}
}
else if( pcan_device.sja1000_ic_mode == SJA1000_BASICCAN )
{
reg_addr &= 0x1F;
switch( reg_addr )
{
case 4:
pcan_device.sja1000_shadow[0] = value;
break;
case 5:
pcan_device.sja1000_shadow[1] = value;
break;
case 6:
pcan_device.sja1000_shadow[2] = value;
break;
case 7:
pcan_device.sja1000_shadow[3] = value;
break;
case 31:
if( (value & 0x80 ) != 0 )
{
pcan_device.sja1000_ic_mode = SJA1000_PELICAN;
}
break;
}
}
}
uint8_t pcan_sja1000_read( uint8_t reg_addr )
{
if( pcan_device.sja1000_ic_mode == SJA1000_PELICAN )
{
reg_addr &= 0x7F;
switch( reg_addr )
{
case 0:
return 0x01;
case 6:
return pcan_device.sja1000_shadow[2];
case 7:
return pcan_device.sja1000_shadow[3];
case 14:
return pcan_device.sja1000_shadow[4];
case 15:
return pcan_device.sja1000_shadow[5];
case 16:
return pcan_device.sja1000_shadow[0];
case 20:
return pcan_device.sja1000_shadow[1];
case 31:
return 0x80; /* PeliCAN mode */
default:
return 0;
}
}
else if( pcan_device.sja1000_ic_mode == SJA1000_BASICCAN )
{
reg_addr &= 0x1F;
switch( reg_addr )
{
case 0:
return 0x01;
case 4:
return pcan_device.sja1000_shadow[0];
case 5:
return pcan_device.sja1000_shadow[1];
case 6:
return pcan_device.sja1000_shadow[2];
case 7:
return pcan_device.sja1000_shadow[3];
case 31:
return 0x00; /* BasicCAN mode */
default:
return 0;
}
}
return 0;
}
void pcan_record_write_header( uint8_t *p )
{
p[0] = 0x02; /* record header */
p[1] = 0x00; /* record count */
}
void pcan_record_buffer_reset( PCAN_RECORD_BUFFER_EX *prec )
{
pcan_record_write_header( prec->buffer );
prec->ts_counter = 0;
prec->pos = HEADER_SIZE;
}
/* request free slot for specific amount of data */
uint8_t *pcan_record_buffer_request( PCAN_RECORD_BUFFER_EX *prec, uint16_t size )
{
if( size > (BLOCK_SIZE-HEADER_SIZE) )
return 0;
uint16_t unused = BLOCK_SIZE - ( prec->pos & (BLOCK_SIZE-1) );
/* allocate new slot ? */
if( unused < size )
{
/* align data to next slot bound */
uint8_t next = ( prec->pos + (BLOCK_SIZE-1) ) & (~((BLOCK_SIZE-1)));
if( (next+HEADER_SIZE) >= PCAN_MAX_RECORD_SIZE )
return 0;
memset( &prec->buffer[next], 0x00, BLOCK_SIZE );
pcan_record_write_header( &prec->buffer[next] );
next += 2;
prec->pos = next;
/* reset ts_counter for next buffer */
prec->ts_counter = 0;
}
if( ( prec->pos + size ) > PCAN_MAX_RECORD_SIZE )
return (void*)0;
return &prec->buffer[prec->pos];
}
void pcan_record_buffer_commit( PCAN_RECORD_BUFFER_EX *prec, uint16_t size )
{
uint16_t pos = prec->pos&(~(BLOCK_SIZE-1));
/* increse record count */
++prec->buffer[pos+1];
prec->pos += size;
}
uint16_t pcan_record_buffer_flush( PCAN_RECORD_BUFFER_EX *prec )
{
uint16_t ts_ticks = pcan_timestamp_ticks();
int res, flush_size;
if( prec->pos <= HEADER_SIZE )
{
/* try to send ZLP, original 800 */
if( pcan_device.last_flush && (((uint16_t)(ts_ticks - pcan_device.last_flush)) >= PCAN_TICKS_FROM_US( 800 )) )
{
res = pcan_flush_data( &data_fsm, 0, 0 );
if( res )
{
pcan_device.last_flush = 0;
}
}
return 0;
}
flush_size = ( prec->pos + (BLOCK_SIZE-1) ) & (~((BLOCK_SIZE-1)));
res = pcan_flush_data( &data_fsm, prec->buffer, flush_size );
if( res )
{
pcan_record_buffer_reset( prec );
pcan_device.last_flush = ts_ticks;
return 1;
}
return 0;
}
#if 0
static void pcan_bus_event( PCAN_RECORD_BUFFER_EX *prec, uint8_t flags )
{
uint8_t *ptr, sl, pos = 0;
ptr = pcan_record_buffer_request( prec, 6 );
if( !ptr )
return;
/* sl */
sl = PCAN_USB_STATUSLEN_INTERNAL | 0x03;
pack_u8( ptr, sl );
ptr += 1;
pos += 1;
/* function */
pack_u8( ptr, PCAN_USB_REC_BUSEVT );
ptr += 1;
pos += 1;
/* number 0x80, 0x00 */
pack_u8( ptr, flags );
ptr += 1;
pos += 1;
/* ecc */
pack_u8( ptr, pcan_device.can.ecc );
ptr += 1;
pos += 1;
/* rx error counter */
pack_u8( ptr, pcan_device.can.rx_err );
ptr += 1;
pos += 1;
/* tx error counter */
pack_u8( ptr, pcan_device.can.tx_err );
ptr += 1;
pos += 1;
/* add new record */
pcan_record_buffer_commit( prec, pos );
}
#endif
#define BUFFER_MINIMAL_GAP_SIZE (16u)
static void pcan_rx_can_frame( PCAN_RECORD_BUFFER_EX *prec, const can_message_t *msg )
{
uint8_t *ptr, pos = 0;
ptr = pcan_record_buffer_request( prec, BUFFER_MINIMAL_GAP_SIZE );
/* wow ! too fast too furious !!! */
if( !ptr )
{
if( !pcan_record_buffer_flush( prec ) )
{
pcan_device.can.err |= PCAN_USB_ERROR_RXQOVR;
return;
}
ptr = pcan_record_buffer_request( prec, BUFFER_MINIMAL_GAP_SIZE );
/* o_0 */
if( !ptr )
return;
}
/* recheck dlc for abnormal frames... */
uint8_t sl = msg->dlc > 8 ? 8: msg->dlc;
if( msg->flags & CAN_FLAG_EXTID )
{
sl |= PCAN_USB_STATUSLEN_EXT_ID;
}
if( msg->flags & CAN_FLAG_RTR )
{
sl |= PCAN_USB_STATUSLEN_RTR;
}
pack_u8( ptr, sl );
ptr += 1;
pos += 1;
if( sl & PCAN_USB_STATUSLEN_EXT_ID )
{
uint32_t id = ( msg->id & 0x1FFFFFFF ) << 3;
if( msg->flags & CAN_FLAG_ECHO )
{
id |= 0x01;
}
pack_u32( ptr, id );
ptr += 4;
pos += 4;
}
else
{
uint16_t id = ( msg->id & 0x7FF ) << 5;
if( msg->flags & CAN_FLAG_ECHO )
{
id |= 0x01;
}
pack_u16( ptr, id );
ptr += 2;
pos += 2;
}
/* timestamp to ticks, 1 tick is 42.666us */
uint16_t timestamp16 = msg->timestamp;
if( prec->ts_counter == 0 )
{
pack_u16( ptr, timestamp16 );
ptr += 2;
pos += 2;
}
else
{
pack_u8( ptr, timestamp16&0xFF );
ptr += 1;
pos += 1;
}
++prec->ts_counter;
if( !( sl & PCAN_USB_STATUSLEN_RTR ) )
{
memcpy( ptr, msg->data, msg->dlc );
ptr += msg->dlc;
pos += msg->dlc;
}
if( msg->flags & CAN_FLAG_ECHO )
{
pack_u8( ptr, msg->dummy );
ptr += 1;
pos += 1;
}
/* add new record */
pcan_record_buffer_commit( prec, pos );
}
static void pcan_rx_message( can_message_t *msg )
{
if( !pcan_device.bus_active )
return;
if( ( msg->flags & CAN_FLAG_ECHO ) == 0 )
{
pcan_led_set_mode( LED_CH0_RX, LED_MODE_BLINK_FAST, 237 );
}
pcan_rx_can_frame( &pcan_records, msg );
}
static void pcan_can_error( uint8_t err, uint8_t rx_err, uint8_t tx_err )
{
pcan_device.can.rx_err = rx_err;
pcan_device.can.tx_err = tx_err;
if( err & CAN_ERROR_FLAG_BUSOFF )
{
pcan_device.can.err |= PCAN_USB_ERROR_BUS_OFF;
}
}
#define WAIT_FOR_TXSLOTS 1
static uint8_t pcan_decode_data_frame( uint8_t *ptr, uint16_t size, uint8_t flags )
{
can_message_t msg = { 0 };
uint8_t rec_len = flags & PCAN_USB_STATUSLEN_DLC;
/* return back frame to PC */
uint8_t srr_flag = 0;
if( !pcan_device.bus_active )
return 0;
if( flags & PCAN_USB_STATUSLEN_EXT_ID )
{
uint32_t id;
if( size < sizeof( uint32_t ) )
return 0;
id = unpack_u32( ptr );
srr_flag = id & 0x01;
id >>= 3;
msg.flags = CAN_FLAG_EXTID;
msg.id = id;
ptr += sizeof( uint32_t );
size -= sizeof( uint32_t );
}
else
{
uint16_t id;
if( size < sizeof( uint16_t ) )
return 0;
id = unpack_u16( ptr );
srr_flag = id & 0x01;
id >>= 5;
msg.id = id;
ptr += sizeof( uint16_t );
size -= sizeof( uint16_t );
}
/* check for MAX DLC */
msg.dlc = rec_len < 8 ? rec_len: 8;
if( size < rec_len )
return 0;
if( !( flags & PCAN_USB_STATUSLEN_RTR ) )
{
memcpy( msg.data, ptr, msg.dlc );
ptr += rec_len;
size -= rec_len;
}
else
{
msg.flags |= CAN_FLAG_RTR;
}
/* self receive flag ? */
if( srr_flag )
{
if( size < sizeof( uint8_t ) )
return 0;
msg.dummy = unpack_u8( ptr );
ptr += sizeof( uint8_t );
size -= sizeof( uint8_t );
}
msg.timestamp = pcan_timestamp_ticks();
pcan_led_set_mode( LED_CH0_TX, LED_MODE_BLINK_FAST, 237 );
#if WAIT_FOR_TXSLOTS
const uint16_t ts_poll = pcan_timestamp_ticks();
/* need more time to send data... ? */
while( pcan_can_send_message( &msg ) < 0 )
{
/* USB will get NACK and we will not miss other data */
pcan_can_poll();
uint16_t ts_diff = pcan_timestamp_ticks() - ts_poll;
/* we can't tramsit couse bus off or timeout ? */
if( ( pcan_device.can.err & PCAN_USB_ERROR_BUS_OFF ) ||
( ts_diff >= PCAN_TICKS_FROM_US( 1000000u ) ) )
{
/* tx buffer overflow, drop all data */
pcan_device.can.err |= PCAN_USB_ERROR_TXFULL;
return size;
}
}
#else
if( pcan_can_send_message( &msg ) < 0 )
{
/* tx buffer overflow, drop all data */
pcan_device.can.err |= PCAN_USB_ERROR_TXFULL;
return size;
}
#endif
/* return back to PC */
if( srr_flag )
{
msg.flags |= CAN_FLAG_ECHO;
pcan_rx_message( &msg );
}
return size;
}
static void pcan_set_bitrate( uint8_t *ptr )
{
/* decode BTR1 */
volatile uint8_t tseg1 = (ptr[0] & 0xF) + 1;
volatile uint8_t tseg2 = ((ptr[0]>>4) & 0x7) + 1;
/* decode BTR0 */
volatile uint16_t brp = (ptr[1] & 0x3f) + 1;
volatile uint8_t sjw = ((ptr[1]>>6) & 0x3) + 1;
uint32_t bitrate = (((PCAN_USB_CRYSTAL_HZ/2)/brp)/(1/*tq*/ + tseg1 + tseg2 ));
(void)bitrate;
pcan_can_set_bitrate( brp, tseg1, tseg2, sjw );
}
/* create */
void pcan_timesync_event( PCAN_RECORD_BUFFER_EX *prec )
{
uint8_t sl = PCAN_USB_STATUSLEN_INTERNAL | 2 /* record data size */;
uint8_t *ptr, pos = 0;
ptr = pcan_record_buffer_request( prec, 5 );
if( !ptr )
return;
uint16_t timestamp16 = pcan_timestamp_ticks();
pack_u8( ptr, sl );
ptr += 1;
pos += 1;
/* function */
pack_u8( ptr, PCAN_USB_REC_TS );
ptr += 1;
pos += 1;
/* number */
pack_u8( ptr, 1 );
ptr += 1;
pos += 1;
/* timestamp */
pack_u16( ptr, timestamp16 );
ptr += 2;
pos += 2;
/* add new record */
pcan_record_buffer_commit( prec, pos );
/* error record */
pos = 0;
ptr = pcan_record_buffer_request( prec, 3 );
if( !ptr )
return;
pcan_device.can.err &= pcan_device.can.err_mask;
pack_u8( ptr, PCAN_USB_STATUSLEN_INTERNAL );
ptr += 1;
pos += 1;
pack_u8( ptr, PCAN_USB_REC_ERROR ); /* f */
ptr += 1;
pos += 1;
pack_u8( ptr, pcan_device.can.err ); /* n */
ptr += 1;
pos += 1;
/* add new record */
pcan_record_buffer_commit( prec, pos );
/* clean errors */
pcan_device.can.err = 0x00;
}
void pcan_protocol_process_command( uint8_t *ptr, uint16_t size )
{
PCAN_USB_PARAM *cmd = (void*)ptr;
if( size < PCAN_USB_MSG_HEADER_LEN )
return;
switch( cmd->number )
{
case PCAN_USB_EX0:
switch( cmd->function )
{
/* set mass storage mode */
case PCAN_USB_SETCAN2FLASH:
break;
}
break;
case PCAN_USB_EX3:
switch( cmd->function )
{
/* CAN silient control*/
case PCAN_USB_SET_SILENT_MODE:
pcan_device.can.silient = cmd->param[0];
pcan_can_set_silent( pcan_device.can.silient );
break;
}
break;
case PCAN_USB_SET:
switch( cmd->function )
{
/* BTR0 BTR1 */
case PCAN_USB_CMD_BITRATE:
pcan_device.can.btr0 = cmd->param[1];
pcan_device.can.btr1 = cmd->param[0];
pcan_set_bitrate( cmd->param );
break;
/* set CAN on/off*/
case PCAN_USB_CMD_BUS:
pcan_device.bus_active = cmd->param[0];
pcan_led_set_mode( LED_STAT, pcan_device.bus_active ? LED_MODE_BLINK_SLOW:LED_MODE_OFF, 0 );
pcan_can_set_bus_active( pcan_device.bus_active );
if( pcan_device.bus_active )
{
/* provide first timesync event */
pcan_device.last_timestamp_sync = pcan_timestamp_ticks();
pcan_timesync_event( &pcan_records );
}
/* led state */
pcan_led_set_mode( LED_CH0_TX, pcan_device.bus_active ? LED_MODE_OFF:LED_MODE_ON, 0 );
pcan_led_set_mode( LED_CH0_RX, pcan_device.bus_active ? LED_MODE_OFF:LED_MODE_ON, 0 );
break;
/* set device id 0-255 */
case PCAN_USB_CMD_DEVID:
pcan_device.device_id = cmd->param[0];
break;
/* unknown windows driver call */
case PCAN_USB_CMD_CFG:
break;
/* sja1000 reg write */
case PCAN_USB_CMD_REGISTER:
pcan_sja1000_write( cmd->param[0], cmd->param[1] );
break;
/* ext VCC on/off */
case PCAN_USB_CMD_EXT_VCC:
pcan_device.ext_vcc_state = cmd->param[0];
break;
/* error frame mask */
case PCAN_USB_CMD_ERR_FR:
pcan_device.can.err_mask = cmd->param[0];
break;
/* set led mode */
case PCAN_USB_CMD_LED:
pcan_device.led_mode = cmd->param[0];
break;
}
break;
case PCAN_USB_GET:
switch( cmd->function )
{
/* BTR0 BTR1 */
case PCAN_USB_CMD_BITRATE:
cmd->param[1] = pcan_device.can.btr0;
cmd->param[0] = pcan_device.can.btr1;
pcan_usb_send_command_buffer( cmd, sizeof( PCAN_USB_PARAM ) );
break;
/* quarts freq in Mhz */
case PCAN_USB_CMD_CLOCK:
{
cmd->param[0] = pcan_device.can.quartz_freq/1000000u;
pcan_usb_send_command_buffer( cmd, sizeof( PCAN_USB_PARAM ) );
}
break;
/* get device id */
case PCAN_USB_CMD_DEVID:
{
/* default: 255 */
cmd->param[0] = pcan_device.device_id;
pcan_usb_send_command_buffer( cmd, sizeof( PCAN_USB_PARAM ) );
}
break;
/* serial */
case PCAN_USB_CMD_SN:
{
/* default: 4 bytes FFFFFFFF */
cmd->param[0] = (pcan_device.device_serial>>0x18)&0xFF;
cmd->param[1] = (pcan_device.device_serial>>0x10)&0xFF;
cmd->param[2] = (pcan_device.device_serial>>0x08)&0xFF;
cmd->param[3] = (pcan_device.device_serial>>0x00)&0xFF;
pcan_usb_send_command_buffer( cmd, sizeof( PCAN_USB_PARAM ) );
}
break;
/* sja1000 reg read */
case PCAN_USB_CMD_REGISTER:
cmd->param[1] = pcan_sja1000_read( cmd->param[0] );
pcan_usb_send_command_buffer( cmd, sizeof( PCAN_USB_PARAM ) );
break;
/* get led */
case PCAN_USB_CMD_LED:
cmd->param[0] = pcan_device.led_mode;
pcan_usb_send_command_buffer( cmd, sizeof( PCAN_USB_PARAM ) );
break;
}
break;
default:
assert( 0 );
break;
}
}
void pcan_protocol_process_data( uint8_t *ptr, uint16_t size )
{
uint8_t msg_prefix, msg_rec_count;
/* invalid frame header len ? */
if( size < PCAN_USB_MSG_HEADER_LEN )
return;
msg_prefix = *ptr++;
msg_rec_count = *ptr++;
(void)msg_prefix; /* avoid warning */
size -= PCAN_USB_MSG_HEADER_LEN;
for( uint8_t i = 0; i < msg_rec_count; i++ )
{
uint16_t eated = 0, sl;
if( size < 1 )
return;
sl = unpack_u8( ptr );
ptr += sizeof( uint8_t );
size -= sizeof( uint8_t );;
/* status, error frame */
if( sl & PCAN_USB_STATUSLEN_INTERNAL )
{
/* ??? abnormal, device support only CAN data frame */
eated = 0;
}
/* regular CAN data frame */
else
{
eated = pcan_decode_data_frame( ptr, size, sl );
}
/* ? error indicator */
if( !eated )
return;
eated = size - eated;
if( size < eated )
return;
ptr += eated;
size -= eated;
}
}
void pcan_protocol_init( void )
{
pcan_device.sja1000_ic_mode = SJA1000_PELICAN;
pcan_record_buffer_reset( &pcan_records );
pcan_can_init();
pcan_can_install_rx_callback( pcan_rx_message );
pcan_can_install_error_callback( pcan_can_error );
}
void pcan_protocol_poll( void )
{
uint16_t ts_ms = pcan_timestamp_ticks();
if( pcan_device.bus_active )
{
/* each ~1000 ms */
if( ( ((uint16_t)( ts_ms - pcan_device.last_timestamp_sync )) >= PCAN_TICKS_FROM_US( 1000000u ) ) )
{
pcan_device.last_timestamp_sync = ts_ms;
pcan_timesync_event( &pcan_records );
}
}
pcan_record_buffer_flush( &pcan_records );
pcan_can_poll();
}