The Real Andy said:
I don't know, I have never looked at the SNAP protocol. I assume that
it has a len field, and based on that you can find the crc and
subsequently validate.
It does have a length field, but if I missed that, and "drop in" in the
middle of a packet, how do I know it's finished?
I agree. The bonus about escaping and data that shares the same start
sequence is that synchronization becomes much faster.
Indeed. The code for this is very straightforward. Heck, I'll include it
here and now. This is C code written on a Philips XA, so a few bits a
registers must be adapted to the OP's processor. This is the low level link
protocol which provides framing of a binary packet. Add your flow control
and CRC to suit your needs.
Below is the complete description and source code.
Meindert
-------------------------------------
Link protocol
The reader uses a link protocol to communicate with the host. This link
protocol provides a transparant 8 bit serial link for binary data.
The link protocol defines frames of a fixed or variable length which are
framed by START and END markers.
The link protocol is derived from the SLIP protocol according to RFC-1055
and slightly modified. Three special link control bytes are defined to
uniquely frame a packet of data:
ESC (0xFF): This byte is inserted in the serial stream immediately
before a data byte identical to one of the three link control bytes.
START (0xFE): This byte marks the start of a frame.
END (0xFD): This byte marks the end of a frame.
The link protocol only provides framing of binary data. There is no error
handling or checksum. Both parties shall ignore a frame in case of a timeout
between bytes or START and END.
Implications for the transmitter
The choice of the values of the control bytes is such that no testing is
required on the presence of all three if them in the datastream. A simple
test whether a byte is greater then or equal to 0xFD is sufficient. When the
byte to be transmitted is lower, it can be transmitted immediately. When
equal or greater, an ESC is inserted in the datastream.
Implications for the receiver
When and ESC is received, the next byte will always be regarded as data and
not as link control byte. When a START or END is received without an ESC,
they will be regarded as start or end markers. The advantage of having
separate START and END markers is that on reception of a START, all
previously received bytes can be regarded as noise and therefore discarded.
Unlike SLIP, which uses only one marker for both the start and end of a
frame, in which case the receiver always has to check the validity of the
previously received bytes.
Code example (this is production code, which has proved to work)
Reception is done in rx_int() and send() and recv() are called to send and
receive frames.
/***************************************************************************
**/
/*
*/
/* Filename : serial.h
*/
/* Revision : 1.0
*/
/* Project : xxxxxxxxxxxxxxxxxxx
*/
/* Description: Header file for serial communication driver
*/
/*
*/
/* Author : M.H. Sprang, CustomWare
*/
/*
*/
/* History:
*/
/* Rev. Date Description
*/
/* -------------------------------------------------------------------------
*/
/* 0.0 23-05-00 Initial version
*/
/* 1.0 11-07-00 First release
*/
/*
*/
/***************************************************************************
**/
#ifndef __SERIAL_H
#define __SERIAL_H
#include "types.h"
#define PACKET_SIZE 387
// special protocol characters
#define C_ESC 0xFF
#define C_START 0xFE
#define C_END 0xFD
typedef enum dummy {S_IDLE,S_RCV,S_ESC,S_RDY} RX_STATE;
typedef struct
{
uint length;
char data[PACKET_SIZE];
} PACKET;
void send(PACKET *packet);
PACKET *recv(void);
#endif
/***************************************************************************
**/
/*
*/
/* Filename : serial.c
*/
/* Revision : 1.0
*/
/* Project : xxxxxxxxxxxxxxxxxxxx
*/
/* Description: Serial communication driver
*/
/*
*/
/* Author : M.H. Sprang, CustomWare
*/
/*
*/
/* History:
*/
/* Rev. Date Description
*/
/* -------------------------------------------------------------------------
*/
/* 0.0 23-05-00 Initial version
*/
/* 1.0 11-07-00 First release
*/
/*
*/
/***************************************************************************
**/
#include "serial.h"
PACKET packet;
char * _near rx_ptr;
uint rx_cntr;
char rx_sum;
RX_STATE rx_state;
/***************************************************************************
**/
/*
*/
/* init_comm(): Initialise serial driver to desired speed.
*/
/*
*/
/* The baudrate generator is T2, which is initialised in the
*/
/* timer module.
*/
/*
*/
/***************************************************************************
**/
void init_comm(void)
{
// serial port configuration
S0STAT = 0; // clear serial status, no interrupts on events
S0CON = 0;
SM1_0 = 1; // set 8 bit UART with variable speed
REN_0 = 1; // enable reception
TI_0 = 1; // 'enable' transmission
rx_state = S_IDLE;
ERI0 = 1; // enable receive interrupts
}
/***************************************************************************
**/
/*
*/
/* rx_int(): Receive interrupt handler.
*/
/*
*/
/***************************************************************************
**/
_interrupt (VEC_RX0) _using(0x8000 | (P_RI0 << 8))
void rx_int(void)
{
char c;
RI_0 = 0;
c = S0BUF;
switch (rx_state)
{
case S_IDLE:
if (c == C_START)
{
rx_ptr = packet.data;
rx_cntr = 0;
rx_sum = 0;
rx_state = S_RCV;
}
break;
case S_RCV:
case S_ESC:
if ((c < C_END) || (rx_state == S_ESC))
{
if (rx_cntr < PACKET_SIZE)
{
*rx_ptr++ = c;
rx_sum += c;
rx_cntr++;
}
rx_state = S_RCV;
}
else
{
switch (c)
{
case C_ESC:
rx_state = S_ESC;
break;
case C_START:
rx_ptr = packet.data;
rx_cntr = 0;
rx_sum = 0;
rx_state = S_RCV;
break;
case C_END:
if ((rx_cntr > 0) && (rx_sum == 0))
{
packet.length = rx_cntr - 1;
rx_state = S_RDY;
}
else
rx_state = S_IDLE;
break;
}
}
break;
}
}
/***************************************************************************
**/
/*
*/
/* send(): Send a packet.
*/
/* In: pointer to a packet structure
*/
/*
*/
/***************************************************************************
**/
void send(PACKET *packet)
{
char *c;
uint i;
putchar(C_START);
c = packet->data;
for (i=0;i<packet->length;i++)
{
if (*c >= C_END) putchar(C_ESC);
putchar(*c++);
}
putchar(C_END);
}
/***************************************************************************
**/
/*
*/
/* recv(): Check if a packet is received. Returns a pointer to a packet when
*/
/* data is received, nil if not.
*/
/*
*/
/***************************************************************************
**/
PACKET *recv(void)
{
if (rx_state == S_RDY)
{
rx_state = S_IDLE;
return &packet;
}
else
return 0;
}